diff --git a/docs/redesign/mockups/Settings.dc.html b/docs/redesign/mockups/Settings.dc.html index f2f668b..bd398ce 100644 --- a/docs/redesign/mockups/Settings.dc.html +++ b/docs/redesign/mockups/Settings.dc.html @@ -1,239 +1,1923 @@ - - -QueryCraft · settings - - - - - - - - -
- -
-

{{c.caption}}

-
- - -
-
-

Settings

- -
-
-
-
-

Accent color

-

{{c.accentName}}

-
-
- - - - -
-

Tints the primary button, switches and URL keys. Text on the accent is picked to keep 4.5:1.

-
-
-

Preview

- -
- -
-

Theme

-
- - - - -
-

{{c.themeDesc}}

-
-
-
-
-
-
-
-
- + + + + + + + +
+ +
+

+ {{c.caption}} +

+
+ + +
+
+

+ Settings +

+ +
+
+
+
+

+ Accent color +

+

+ {{c.accentName}} +

+
+
+ + + + +
+

+ Tints the primary button, switches and URL keys. +

+
+
+

+ Preview +

+ +
+ +
+

+ Theme +

+
+ + + + +
+

+ {{c.themeDesc}} +

+
+
+
+
+
+
+
+
+ - + const SW = [['Mono', 'var(--foreground)', 'var(--background)'], ['Blue', '#6690e0', '#0a0a0a'], ['Cyan', '#4fa8c9', '#0a0a0a'], + ['Teal', '#4fa98e', '#0a0a0a'], ['Green', '#4f9d6e', '#0a0a0a'], ['Lime', '#8db84f', '#0a0a0a'], ['Amber', '#c99a3d', '#0a0a0a'], + ['Orange', '#d9824f', '#0a0a0a'], ['Red', '#d9636b', '#0a0a0a'], ['Pink', '#d9709c', '#0a0a0a'], ['Violet', '#9575e0', '#0a0a0a']]; + const PREVIEW = [{ t: '?', isB: true }, { t: 'view', isK: true }, { t: '=', isB: true }, { t: 'table', isV: true }, + { t: '&', isB: true }, { t: 'live', isK: true }, { t: '=', isB: true }, { t: 'true', isV: true }]; + function themes(selected) { + return ['System', 'Light', 'Dark'].map((label) => ({ + label: label, sel: label === selected, unsel: label !== selected, + isSys: label === 'System', isLight: label === 'Light', isDark: label === 'Dark' + })); + } + class Component extends DCLogic { + renderVals() { + const accent = this.props.accent ?? 'violet'; + const selName = accent === 'violet' ? 'Violet' : 'Mono'; + const swatches = SW.map((s) => ({ + name: s[0], paint: 'background: ' + s[1] + '; color: ' + s[2], + sel: s[0] === selName, unsel: s[0] !== selName + })); + const common = Object.assign(editorFields(), { swatches: swatches, accentName: selName, previewParts: PREVIEW }); + return { + accentCls: accent === 'violet' ? 'qc-violet' : '', + cells: [ + Object.assign({}, common, { cls: 'qc-light', caption: 'Light · theme “System”, OS is light', themes: themes('System'), + themeDesc: 'Follows your OS appearance — light right now.' }), + Object.assign({}, common, { cls: 'qc-dark', caption: 'Dark · theme set to “Dark”, overriding a light OS', themes: themes('Dark'), + themeDesc: 'Overrides your OS appearance for QueryCraft only.' }) + ] + }; + } + } + + diff --git a/manifest.json b/manifest.json index c233f09..1785070 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "QueryCraft", - "version": "1.0.10", + "version": "1.0.11", "description": "Inspect, edit, save, and replay URL query parameters on the active tab.", "action": { "default_popup": "src/popup/index.html", diff --git a/package-lock.json b/package-lock.json index a0c8de4..d4db4b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "querycraft", - "version": "1.0.10", + "version": "1.0.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "querycraft", - "version": "1.0.10", + "version": "1.0.11", "dependencies": { "@base-ui/react": "^1.8.0", "@fontsource-variable/inter": "^5.3.0", diff --git a/package.json b/package.json index 1bec4c5..4e240ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "querycraft", - "version": "1.0.10", + "version": "1.0.11", "description": "Inspect, edit, save, and replay URL query parameters on the active tab.", "private": true, "type": "module", diff --git a/public/theme-boot.js b/public/theme-boot.js index b536273..cc0ee66 100644 --- a/public/theme-boot.js +++ b/public/theme-boot.js @@ -1,7 +1,7 @@ /* - * Applies the saved theme and accent before first paint (docs/redesign §4.5). + * Applies the saved theme, accent and layout direction before first paint (docs/redesign §4.5). * chrome.storage is async, so the popup would flash the wrong scheme; this - * reads the synchronous localStorage mirror that useTheme / useAccent keep. + * reads the synchronous localStorage mirror that useTheme / useAccent / useDirection keep. * Loaded as a file, not inline: the MV3 content security policy blocks inline scripts. */ try { @@ -15,6 +15,8 @@ try { root.setAttribute('data-theme', scheme); root.style.colorScheme = scheme; + if (localStorage.getItem('qc-dir') === 'rtl') root.dir = 'rtl'; + var accent = JSON.parse(localStorage.getItem('qc-accent') || 'null'); if (accent) { root.style.setProperty('--primary', accent.primary); diff --git a/src/__tests__/useDirection.test.ts b/src/__tests__/useDirection.test.ts new file mode 100644 index 0000000..d16403a --- /dev/null +++ b/src/__tests__/useDirection.test.ts @@ -0,0 +1,36 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { act, renderHook, waitFor } from '@testing-library/react'; +import { useDirection, DIRECTION_MIRROR_KEY } from '@/hooks/useDirection'; + +afterEach(() => { + document.documentElement.removeAttribute('dir'); + localStorage.clear(); +}); + +describe('useDirection', () => { + it('is left-to-right by default', () => { + const { result } = renderHook(() => useDirection()); + + expect(result.current.direction).toBe('ltr'); + expect(document.documentElement.dir).toBe('ltr'); + }); + + it('applies the setting to , persists it and mirrors it for the boot script', async () => { + const { result } = renderHook(() => useDirection()); + + act(() => result.current.setDirection('rtl')); + + expect(document.documentElement.dir).toBe('rtl'); + expect(localStorage.getItem(DIRECTION_MIRROR_KEY)).toBe('rtl'); + await waitFor(() => + expect(chrome.storage.local.set).toHaveBeenCalledWith({ 'qc.direction': 'rtl' }), + ); + }); + + it('starts from the mirror, so the first render already has the right direction', () => { + localStorage.setItem(DIRECTION_MIRROR_KEY, 'rtl'); + const { result } = renderHook(() => useDirection()); + + expect(result.current.direction).toBe('rtl'); + }); +}); diff --git a/src/components/SavedLinksDrawer/SaveForm.tsx b/src/components/SavedLinksDrawer/SaveForm.tsx index 90b4b99..dd72224 100644 --- a/src/components/SavedLinksDrawer/SaveForm.tsx +++ b/src/components/SavedLinksDrawer/SaveForm.tsx @@ -123,7 +123,7 @@ function ReadOnlyUrl({ data-overflowing={overflowing || undefined} className="max-h-20 overflow-y-auto data-overflowing:mask-b-from-[calc(100%-1.5rem)]" > -
+