diff --git a/public/index.html b/public/index.html index cf90eb7..9678917 100644 --- a/public/index.html +++ b/public/index.html @@ -4,7 +4,11 @@ - + + + + + AF Bullet Shaping Tool + + + + + + diff --git a/src/components/Bullets/Bullet.js b/src/components/Bullets/Bullet.js index 7b5e0e4..086af13 100644 --- a/src/components/Bullets/Bullet.js +++ b/src/components/Bullets/Bullet.js @@ -12,7 +12,6 @@ function Bullet({ const canvasRef = useRef(null); const [outputTextLines, setOutputTextLines] = useState(() => [" "]); - const [color, setColor] = useState("inherit"); const [loading, setLoading] = useState(false); const [optimStatus, setOptimStatus] = useState(STATUS.NOT_OPT); const [rendering, setBulletRendering] = useState({ textLines: [""] }); @@ -67,15 +66,12 @@ function Bullet({ }, [rendering, enableOptim, text, widthPxAdjusted]); //color effect - useEffect(() => { - if (loading) { - setColor("silver"); - } else if (optimStatus === STATUS.FAILED_OPT) { - setColor("red"); - } else { - setColor("black"); - } - }, [loading, outputTextLines, optimStatus]); + // choose a theme-aware class for text color + const statusClass = loading + ? "is-loading" + : optimStatus === STATUS.FAILED_OPT + ? "has-text-danger" + : ""; // the style properties help lock the canvas in the same spot and make it essentially invisible. //whitespace: pre-wrap is essential as it allows javascript string line breaks to appear properly. @@ -91,9 +87,9 @@ function Bullet({ }} />
-
+
Thesaurus{this.props.word ? ":" : ""} {this.props.word ? header : ""} @@ -125,7 +125,6 @@ class SynonymViewer extends PureComponent { } class SynonymList extends PureComponent { - render() { return (
@@ -141,7 +140,11 @@ class SynonymList extends PureComponent { otherAbbrs={otherAbbrs} /> - + ); diff --git a/src/components/Toolbars/Toolbars.js b/src/components/Toolbars/Toolbars.js index c25fb77..fd0d1b4 100644 --- a/src/components/Toolbars/Toolbars.js +++ b/src/components/Toolbars/Toolbars.js @@ -1,9 +1,18 @@ +import React from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAngleDown } from "@fortawesome/free-solid-svg-icons"; import ImportTools from "./ImportTools"; import SaveTools from "./SaveTools"; // form width, space optimization, select text -function OutputTools({ onOptimChange, enableOptim, onWidthUpdate, width, onHighlightChange, handleEnableHighlight, enableHighlight }) { +function OutputTools({ + onOptimChange, + enableOptim, + onWidthUpdate, + width, + onHighlightChange, + handleEnableHighlight, + enableHighlight, +}) { const widthAWD = 202.321; const widthEPR = 202.321; const widthOPR = 201.041; @@ -50,28 +59,30 @@ function OutputTools({ onOptimChange, enableOptim, onWidthUpdate, width, onHighl OPR
-
- - Auto-Space - - { onHighlightChange(); handleEnableHighlight() }} - id="enableHighlight" - > - Show Duplicates - -
+ + Auto-Space + + { + onHighlightChange(); + handleEnableHighlight(); + }} + id="enableHighlight" + > + Show Duplicates + + ); } // normalize spaces @@ -83,17 +94,47 @@ function InputTools({ onTextNorm }) { ); } -function Logo() { +function ThemeSelector() { + const [theme, setTheme] = React.useState( + () => localStorage.getItem("theme") || "system" + ); + + React.useEffect(() => { + const root = document.documentElement; + if (theme === "system") { + root.removeAttribute("data-theme"); // defer to prefers-color-scheme + localStorage.removeItem("theme"); + } else { + root.setAttribute("data-theme", theme); + localStorage.setItem("theme", theme); + } + }, [theme]); + return ( -

- AF - Bullet - Shaping & - iteration - tool -

+
+ + Theme + + +
+ +
+
+
); } + +function Logo() { + return

AF Bullet Shaping & Iteration Tool

; +} function ThesaurusTools({ onHide }) { return ( + ); diff --git a/src/styles/index.scss b/src/styles/index.scss index 4c56296..392c770 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,94 +1,259 @@ @charset "utf-8"; - - // If need, change your variables before importing Bulma // $title-color: #ff0000; @import "bulma/bulma.scss"; -textarea.bullets{ +/* --------------------------- + THEME TOKENS & OVERRIDES +----------------------------*/ +html { color-scheme: dark light; } + +:root { + --bg: #14161A; + --text: #f2f3f5; + --muted: #a3a3a3; + --border: #ffffff26; + --bg-elevated: #14161A; + --logo: #e6e9ff; + --plus-icon: #ff2828; - height: 200px; - white-space: pre-line; - text-indent: 0px; - overflow: hidden; - border: none; - resize: none; - /*word-break: break-all;*/ + /* Highlights & validation (dark) */ + --hl-bg: #e7c64b; + --hl-alt-bg: #58d66e; + --hl-fg: #0b0b0c; + --valid: var(--text); + --invalid: #ff2828; } -.border{ - width:fit-content; - border: solid black 1.1px; + +/* SYSTEM LIGHT OVERRIDE */ +@media (prefers-color-scheme: light) { + :root { + --bg: #ffffff; + --text: #111111; + --muted: #6b7280; + --border: #1111111c; + --bg-elevated: #ffffff; + --logo: #0F0F0F; + + --hl-bg: #fff3a3; + --hl-alt-bg: #c9f7a8; + --hl-fg: #111111; + --valid: var(--text); + --invalid: #ff2828; + } } -.bulletContainer{ - margin:10px 10px 0px 0px; + +/* MANUAL OVERRIDES (leave as-is, order after the above so they win) */ +:root[data-theme="light"] { + color-scheme: light; + --bg: #ffffff; + --text: #111111; + --muted: #6b7280; + --border: #1111111c; + --bg-elevated: #ffffff; + --logo: #0F0F0F; + + --hl-bg: #fff3a3; + --hl-alt-bg: #c9f7a8; + --hl-fg: #111111; + --valid: var(--text); + --invalid: #ff2828; } -.bullets{ - //font-family: "AdobeTimes"; - font-family: 'Times New Roman'; - font-size: 12pt; - display:inline; - font-kerning:none; - text-rendering: geometricPrecision; + +:root[data-theme="dark"] { + color-scheme: dark; + --bg: #14161A; + --text: #f2f3f5; + --muted: #a3a3a3; + --border: #ffffff26; + --bg-elevated: #14161A; + --logo: #e6e9ff; + + --hl-bg: #e7c64b; + --hl-alt-bg: #58d66e; + --hl-fg: #0b0b0c; + --valid: var(--text); + --invalid: #ff2828; } -input.htCheckboxRendererInput{ - margin: 2px auto!important; - display:block!important; + +/* --------------------------- + GLOBAL & LAYOUT +----------------------------*/ +html, body, #root { + min-height: 100vh; + background: var(--bg); } -#thesaurus{ - padding: 10px; - margin:10px 10px 0px 0px; - width: unset; +body { color: var(--text); margin: 0; } +::selection { background: var(--hl-bg); color: var(--hl-fg); } + +/* Typography */ +.title { color: var(--text); } +.logo { color: var(--logo); } +h1, h2 { color: var(--text); font-family: monospace; } + +/* Navbar & tabs */ +.navbar { background-color: var(--bg-elevated); } +.navbar-item, .navbar-link { color: var(--text); } +.navbar.is-wrap { + background: var(--bg-elevated); + padding: .5rem; } -#thesaurus > ul { - columns:10; - list-style-type:circle; - margin: 5px auto; +.navbar.is-wrap .navbar-start { + display: flex; + flex-wrap: wrap; /* key line */ + align-items: center; + gap: .5rem; /* consistent spacing between items */ +} + +/* Tighter items so they pack nicely when wrapped */ +.navbar.is-wrap .navbar-item { + padding: 0; /* remove bulky navbar padding */ +} + +/* Let grouped controls wrap too (Bulma buttons/fields) */ +.navbar.is-wrap .field.has-addons, +.navbar.is-wrap .buttons.has-addons { + flex-wrap: wrap; + gap: .25rem; } -#thesaurus > ul > li { - margin:auto 5px; +/* Keep control heights consistent (buttons, selects, inputs) */ +.navbar.is-wrap .button, +.navbar.is-wrap .input { + height: 2.25rem; /* matches Bulma default button height */ + line-height: 2.25rem; } +/* Optional: keep tabs readable across themes */ +.tabs a { color: var(--text); } +.tabs li.is-active a { + color: var(--text); + border-bottom-color: var(--border); +} +.tabs ul { border-bottom-color: var(--border);} + +/* Generic containers */ +.section, .container, .columns, .column { background: transparent; } -#abbrTable{ - margin:10px 10px 0px 0px; +/* Borders & footer */ +.border { + border: 1.1px solid var(--border); + width: fit-content; } -#bulletIOWrapper{ - min-height:10px; +#footer { + color: var(--muted); + text-align: center; + font-size: 12px; } -.logo{ - color: #0F0F0F; +#footer div { display: block; } + +/* --------------------------- + FORMS & CONTROLS +----------------------------*/ +.input, +.textarea, +.select select { + background: var(--bg-elevated); + color: var(--text); + border-color: var(--border); } -h1{ - font-family:monospace; +.input::placeholder, +.textarea::placeholder { color: var(--muted); opacity: 1; } + +/* Output area theming */ +.output-box, +textarea.output-box { + background: var(--bg-elevated); + color: var(--text); /* default/valid */ + border: 1px solid var(--border); } -h2{ - font-family: monospace; +.has-text-success { color: var(--valid); } /* valid/OK text */ +.output-box.is-valid { color: var(--valid); } +.output-box.is-invalid, +.has-text-danger, +.is-danger { color: var(--invalid) !important; } +.is-loading { color: var(--muted); } /* loading/working state */ + +.public-DraftEditor-content { + color: var(--text); + caret-color: var(--text); } -#topStuff{ - /*position: fixed; - top: 0; - width: 100%; - margin-right:10px;*/ - background-color: white; + +/* --------------------------- + HIGHLIGHTING (duplicates) +----------------------------*/ +/* Preferred highlight classes used by the decorator */ +.dup-highlight { + background: var(--hl-bg); + color: var(--hl-fg); + border-radius: 2px; + padding: 0 .08em; + cursor: pointer; +} +.dup-highlight--alt { + background: var(--hl-alt-bg); } -.toolbox{ - display:inherit; +/* If earlier code injects inline "yellow" styles, force ours */ +.yellow-highlight { + background: var(--hl-bg) !important; + color: var(--hl-fg) !important; + border-radius: 2px; + padding: 0 .08em; + cursor: pointer; } -.toolbox button{ - margin:5px; +/* --------------------------- + PROJECT-SPECIFIC STYLES +----------------------------*/ +textarea.bullets { + height: 200px; + white-space: pre-line; + text-indent: 0px; + overflow: hidden; + border: none; + resize: none; + /* word-break: break-all; */ } -#footer{ - text-align:center; - font-size:12px; - color:grey; +.bulletContainer { margin: 10px 10px 0 0; } + +.bullets { + // font-family: "AdobeTimes"; + font-family: 'Times New Roman'; + font-size: 12pt; + display: inline; + font-kerning: none; + text-rendering: geometricPrecision; } -#footer div{ - display:block -} \ No newline at end of file + +input.htCheckboxRendererInput { + margin: 2px auto !important; + display: block !important; +} + +#thesaurus { + padding: 10px; + margin: 10px 10px 0 0; + width: unset; +} +#thesaurus > ul { + columns: 10; + list-style-type: circle; + margin: 5px auto; +} +#thesaurus > ul > li { margin: auto 5px; } +.thesaurus-plus { color: var(--plus-icon); } +.thesaurus-plus:hover, +.thesaurus-plus:focus { + opacity: 0.9; +} + +#abbrTable { margin: 10px 10px 0 0; } +#bulletIOWrapper { min-height: 10px; } + +.toolbox { display: inherit; } +.toolbox button { margin: 5px; } \ No newline at end of file