diff --git a/.changeset/array-editor-effect-loop.md b/.changeset/array-editor-effect-loop.md new file mode 100644 index 00000000..82612290 --- /dev/null +++ b/.changeset/array-editor-effect-loop.md @@ -0,0 +1,17 @@ +### New Features +- Alt+J previews the whole row as JSON in the bottom dock, and follows the cursor from row to row. Also on the cell right-click menu +- JSON colours are selectable in Settings → Appearance: Auto, Vivid, Ocean, Solarized, GitHub and Monochrome + +### Bug Fixes +- Opening an array cell no longer takes the view down with "This view hit an error" +- Scrolling no longer tears, leaving a band of repeated rows above a seam +- A cell holding multi-line text reads on one line instead of showing gaps where the line breaks were +- Control characters show what they are instead of an empty box +- The switch keeps its thumb inside its track at every zoom level + +### Changes +- Array columns read as JSON, the same form the editor and the jsonb column beside them already used +- The array editor reads as one list instead of a stack of separate fields, and its row controls stay put instead of appearing under the pointer +- The expanded row and its JSON tree sit on one type scale, and a key's colon sits against the key +- Settings descriptions are one line each instead of a paragraph +- Opening a schema with hundreds of tables costs one icon-settings subscription instead of one per icon diff --git a/src/app.css b/src/app.css index f32b7ac9..0aca2303 100644 --- a/src/app.css +++ b/src/app.css @@ -154,6 +154,72 @@ html[data-theme="light"] { --json-number: oklch(0.55 0.13 60); --json-boolean: oklch(0.5 0.18 310); } + +/* Selectable JSON palettes (Settings → Appearance → JSON colours). + `auto` is the pair above: it follows light and dark and is the default. + Each of the rest is written twice, once for dark surfaces and once for + light, because a palette tuned for one is unreadable on the other - the + lightness moves, the hue does not. */ +[data-json-theme="vivid"] { + --json-key: oklch(0.78 0.16 250); + --json-string: oklch(0.82 0.19 145); + --json-number: oklch(0.85 0.17 80); + --json-boolean: oklch(0.78 0.21 320); +} +html[data-theme="light"][data-json-theme="vivid"], +html[data-theme="light"] [data-json-theme="vivid"] { + --json-key: oklch(0.48 0.21 255); + --json-string: oklch(0.45 0.18 150); + --json-number: oklch(0.5 0.17 60); + --json-boolean: oklch(0.46 0.24 320); +} + +[data-json-theme="ocean"] { + --json-key: oklch(0.76 0.11 230); + --json-string: oklch(0.79 0.11 195); + --json-number: oklch(0.82 0.09 160); + --json-boolean: oklch(0.72 0.13 265); +} +html[data-theme="light"][data-json-theme="ocean"], +html[data-theme="light"] [data-json-theme="ocean"] { + --json-key: oklch(0.46 0.14 235); + --json-string: oklch(0.45 0.11 200); + --json-number: oklch(0.48 0.1 165); + --json-boolean: oklch(0.44 0.16 270); +} + +/* Solarized's own accent values, which are defined once and used on both of + its backgrounds - that is the point of the palette. */ +[data-json-theme="solarized"] { + --json-key: oklch(0.63 0.11 240); /* blue #268bd2 */ + --json-string: oklch(0.68 0.1 190); /* cyan #2aa198 */ + --json-number: oklch(0.68 0.13 130);/* green #859900 */ + --json-boolean: oklch(0.6 0.15 350);/* magenta #d33682 */ +} + +[data-json-theme="github"] { + --json-key: oklch(0.72 0.14 280); /* purple */ + --json-string: oklch(0.75 0.11 235);/* blue */ + --json-number: oklch(0.76 0.13 45); /* orange */ + --json-boolean: oklch(0.7 0.16 20); /* red */ +} +html[data-theme="light"][data-json-theme="github"], +html[data-theme="light"] [data-json-theme="github"] { + --json-key: oklch(0.45 0.19 285); + --json-string: oklch(0.42 0.15 240); + --json-number: oklch(0.5 0.15 45); + --json-boolean: oklch(0.48 0.19 20); +} + +/* No hue at all: the structure carries on the shades and the app theme's own + accent is the only colour left on screen. */ +[data-json-theme="monochrome"] { + --json-key: var(--foreground); + --json-string: color-mix(in oklch, var(--foreground) 72%, transparent); + --json-number: color-mix(in oklch, var(--foreground) 88%, transparent); + --json-boolean: color-mix(in oklch, var(--foreground) 60%, transparent); + --json-null: color-mix(in oklch, var(--foreground) 42%, transparent); +} .json-tok-key { color: var(--json-key); } .json-tok-str { color: var(--json-string); } .json-tok-num { color: var(--json-number); } diff --git a/src/lib/components/ArrayCellEditor.svelte b/src/lib/components/ArrayCellEditor.svelte index c030d55c..ca0957c6 100644 --- a/src/lib/components/ArrayCellEditor.svelte +++ b/src/lib/components/ArrayCellEditor.svelte @@ -32,10 +32,16 @@ // Seed the working copy whenever the editor opens for a new cell. $effect(() => { if (open) { - items = (Array.isArray(value) ? value : []).map((el) => ({ + // Built into a local and read from it. Assigning `items` and then reading + // `items.length` on the next line made this effect depend on the state it + // had just written - and since each run assigns a brand new array, the + // dependency fired again every time. Opening an array cell took the whole + // view down with effect_update_depth_exceeded. + const next = (Array.isArray(value) ? value : []).map((el) => ({ v: el === null || el === undefined ? null : String(el), })) - inputEls.length = items.length + items = next + inputEls.length = next.length } }) @@ -100,7 +106,7 @@ onclick={(e) => { if (e.target === e.currentTarget) cancel() }} onkeydown={(e) => { if (e.key === 'Escape') { e.preventDefault(); cancel() } }} > -
Empty array {'{}'}
@@ -138,21 +146,22 @@${escapeHtml(source)}`
+ ? `${escapeHtml(source)}`
: highlightJson(source)
})
@@ -351,10 +351,10 @@
oncontextmenu={handleContextMenu}
>
{#if !html}
- Loading…
+Loading…
{:else}Null sort order
-Applied to quick-query ordering on databases that support explicit null placement.
+Applies where the database supports explicit null placement.
Formatting options
- Casing, indentation and wrapping for Format (⇧⌥F) in every editor and for the DDL viewer. + Casing, indentation and wrapping for Format (⇧⌥F).
- A {$lockStatus.pinLength}-digit PIN that locks Stroke on launch. Stored as a salted - hash in your OS keychain - there is no reset link, so pick one you will remember. + A {$lockStatus.pinLength}-digit PIN that locks Stroke on launch. There is no reset, + so pick one you will remember.
Auto-lock
- Lock again after this much inactivity. Open tabs and queries are kept - the - screen goes over the session, not through it. + Lock again after this much inactivity. Open tabs and queries are kept.
JSON colours
++ Colours for JSON keys, strings, numbers and booleans. Auto follows the app theme. +
+Row spacing
- Vertical space each row of the data grid takes. Compact fits about a third more rows on screen; relaxed is easier to track across a wide table. + Row height in the data grid. Compact fits about a third more rows on screen.
Grid text size
- Size of the values in the data grid at 100% zoom. Separate from the app zoom, so the grid can run denser or larger than the rest of the interface without changing it. + Text size in the data grid at 100% zoom, independent of the app zoom.
Rows per page
- How many rows a newly opened table fetches. The page-size control in the grid toolbar changes the same value, and applies to the table already open. + How many rows a newly opened table fetches. Same value as the toolbar control.
Sidebar position
- Which side of the window the tables sidebar sits on. Also on its own right-click menu. + Which side of the window the sidebar sits on.
Motion
- System follows your OS reduced-motion setting. Override it when you want animation in your window manager but not in a tool you stare at all day, or when the machine's setting isn't yours to change. A loading spinner keeps turning either way. + System follows your OS reduced-motion setting. Spinners keep turning either way.
Cell alignment
- Which side grid cell text sits on. "Numbers right" lines digits up by place value so you can compare magnitudes down a column, and leaves prose on the left. + Which side cell text sits on. Numbers right lines digits up by place value.