Skip to content

v0.20.0

Choose a tag to compare

@angel-ip angel-ip released this 02 Oct 13:06
936086a

@kanzo-tech/theme

Themes are four families now, and the theme choice is ThemePicker.

  • The catalogue is kanzo / kanzo-dark (rebuilt from the kanzo.tech brand), nord / nord-dark,
    catppuccin-latte / catppuccin-mocha and lofi / lofi-dark. Every other theme is deleted —
    a stored or configured dracula, bank, monochrome, catppuccin-latte-dark… is retired (the
    provider clears it and reports it through onThemeRetired).
  • themeIndex entries are ThemeOption { value, label, dark, family } (was { name, dark });
    ThemeIndexEntry is gone. ThemeOption gains the required dark and an optional family. New:
    themeFamilies, defaultThemePair, CONTRAST_PAIRS, auditContrast, ThemeFamily,
    ContrastPair, ContrastFinding.
  • KanzoThemeProvider: themes defaults to themeIndex, so themes={themeIndex.map(…)} can be
    deleted. defaultTheme is a { light, dark } pair only (a string no longer type-checks) and
    defaults to the first family. The resolved theme is always written to data-theme, the default
    included. themeScript({ defaultTheme }) takes the same pair — pass it whenever you pass it to
    the provider.
  • PreferencesColor and PreferencesColorProps are removed. Use <ThemePicker /> (GitHub's
    Appearance settings: a Light · Dark switch and a card per side); the Preferences panel draws it first.
    ThemePreview is the miniature it draws, exported for catalogues and branding pages.
  • Syntax tokens: --syntax-variable and --syntax-annotation are new and --syntax-identifier is
    removed; a tenant theme must author all eight --syntax-*, plus --input, --field and
    --faint, which components now read with no fallback.
  • Fonts: Geist and Geist Mono are the default font / monoFont (DEFAULT_PREFS was "system"),
    each falling back to the system face; --font-mono no longer refers to itself, so a host's
    .cm-scroller { font-family } override can be deleted.

Appearance is Light or Dark, picked by the user — the OS is no longer asked. appearance is
"light" | "dark" and defaults to "light"; a host moves the starting side with
policy.theme.appearance.default. Removed: the "" "follow the OS" value and the AppearancePref
type, prefers-color-scheme in KanzoThemeProvider and themeScript, resolvedAppearance (read
appearance, which is now always the side worn), and the appearance controller prop with its
AppearanceController type. A stored value that is not a side resolves to the default.

ThemePicker is laid out as GitHub's Appearance settings. A Light · Dark segment picks the
side, and two cards always follow — Light theme and Dark theme, side by side or stacked in a
narrow container — each with one large ThemePreview of its choice, the theme's name, and a row of
round swatches painted by each theme's real --background and --primary. Choosing a swatch files
that side's theme and never flips the appearance; the card of the side worn carries Active.
ThemePickerCopy is now appearance, light, dark, day, dayDescription, night,
nightDescription, active, retired.

A scoped theme no longer inherits the page's optional tokens. A light preview on a dark page drew
the dark theme's sidebar and popover, because a theme that leaves --popover, --sidebar or another
optional token unsaid inherited the page's value. Every [data-theme] element now resets them, so
the bridge's fallback resolves against the theme on that element.

@kanzo-tech/ui

@kanzo-tech/ui/analytics has a mini BI kit: Dashboard, and the parts it is made of.

  • <Dashboard table value onChange /> draws a filter row, stat tiles, chart cards and a paged rows
    table over one relation, all on the provider's crossfilter. With no value it draws an automatic
    dashboard from the relation's field stats; every edit calls onChange with the whole
    DashboardSpec, plain JSON for the host to save. exclude hides bookkeeping columns, config
    gives a field drawn as series its own labels, colours and icons.
  • The parts are exported too: DashboardFilters, DashboardStat, ChartCard, DetailTable, and
    FilterChips / useClauses for any selection. useFieldStats / queryFieldStats read a
    relation's fields (one SUMMARIZE); autoDashboard(fields) is the automatic spec.
  • useMosaic() gains retract(clauses): it clears those clauses on the selection each was
    published into, so removing a chip also clears the pick or brush that made it.
  • If you copied the docs' ChartCard, DashboardGrid or FilterChips frames: the first two are
    Card parts and a grid class now, and FilterChips is the library's — import it from
    @kanzo-tech/ui/analytics (it has no empty prop; render your own hint while useClauses is
    empty).

EditablePreview truncates: over an Input it is one ellipsised line carrying the full value as
its title once cut off; over a Textarea it wraps and grows. Consumers drop hand-written
block truncate whitespace-nowrap overrides. size is now "sm" | "md" | "lg" | "xl" (the icon
sizes were never meaningful for a preview), and EditablePreviewProps is exported.

Themes are four families now, and the theme choice is ThemePicker.

  • The catalogue is kanzo / kanzo-dark (rebuilt from the kanzo.tech brand), nord / nord-dark,
    catppuccin-latte / catppuccin-mocha and lofi / lofi-dark. Every other theme is deleted —
    a stored or configured dracula, bank, monochrome, catppuccin-latte-dark… is retired (the
    provider clears it and reports it through onThemeRetired).
  • themeIndex entries are ThemeOption { value, label, dark, family } (was { name, dark });
    ThemeIndexEntry is gone. ThemeOption gains the required dark and an optional family. New:
    themeFamilies, defaultThemePair, CONTRAST_PAIRS, auditContrast, ThemeFamily,
    ContrastPair, ContrastFinding.
  • KanzoThemeProvider: themes defaults to themeIndex, so themes={themeIndex.map(…)} can be
    deleted. defaultTheme is a { light, dark } pair only (a string no longer type-checks) and
    defaults to the first family. The resolved theme is always written to data-theme, the default
    included. themeScript({ defaultTheme }) takes the same pair — pass it whenever you pass it to
    the provider.
  • PreferencesColor and PreferencesColorProps are removed. Use <ThemePicker /> (GitHub's
    Appearance settings: a Light · Dark switch and a card per side); the Preferences panel draws it first.
    ThemePreview is the miniature it draws, exported for catalogues and branding pages.
  • Syntax tokens: --syntax-variable and --syntax-annotation are new and --syntax-identifier is
    removed; a tenant theme must author all eight --syntax-*, plus --input, --field and
    --faint, which components now read with no fallback.
  • Fonts: Geist and Geist Mono are the default font / monoFont (DEFAULT_PREFS was "system"),
    each falling back to the system face; --font-mono no longer refers to itself, so a host's
    .cm-scroller { font-family } override can be deleted.

Four fixes to the analytics BI kit.

  • The grids follow the dashboard's width, not the screen's. DashboardFilters and Dashboard's
    card grid lay out with container queries, so a dashboard beside a dock or in a pane gets the
    columns its own width allows, and a search filter no longer overflows a narrow cell. ChartCard's
    span now applies inside a Dashboard's grid, measured on the dashboard.
  • DetailTable formats a time by its column's type: a DATE as a day (no time, and in UTC so
    it is not the day before west of Greenwich), a TIMESTAMP as its wall clock, a TIMESTAMPTZ in
    the viewer's zone, a TIME as it came.
  • A stacked area completes its grid. ChartAreaY / ChartAreaX stacked by a series column draw
    a series that has no row at some step as zero there, instead of a jagged stack. This adds
    @uwdata/mosaic-plot (^0.29.2, already a dependency of @uwdata/vgplot) as an optional peer:
    install it beside vgplot (pnpm add @uwdata/mosaic-plot).
  • plotRelation(table, { fields, columns }) is exported — the relation and fields as the plots
    can read them, for a host composing the parts over a relation with columns named x/y.

Dashboard draws a relation that has columns named x or y.

  • A line or area over a time, the timeline filter and the tiles' trends failed on such a relation (a
    fossil corpus's layout coordinates, say) with "column … must appear in the GROUP BY clause". The
    plots now read the relation without the columns named like a plot channel (x, y, x1, x2,
    y1, y2, fx, fy, z, fill, stroke), and those columns are not offered as fields.
  • queryFieldStats resolves to { fields, columns } (was the field array) and useFieldStats
    returns columns beside fields. If you called queryFieldStats, read .fields.

GraphSearch and useFieldStats keep a failure whole, like every other part since v0.19.0.
GraphSearch hands a failed read of the names to the root's onFailure as the thrown value and its
input says "The names could not be read." instead of staying disabled. useFieldStats (and so
Dashboard) reports a failed SUMMARIZE to the MosaicProvider's onFailure, and its error is
now the thrown value (unknown) rather than a re-wrapped Error — if you read error.message, narrow
it first.

Appearance is Light or Dark, picked by the user — the OS is no longer asked. appearance is
"light" | "dark" and defaults to "light"; a host moves the starting side with
policy.theme.appearance.default. Removed: the "" "follow the OS" value and the AppearancePref
type, prefers-color-scheme in KanzoThemeProvider and themeScript, resolvedAppearance (read
appearance, which is now always the side worn), and the appearance controller prop with its
AppearanceController type. A stored value that is not a side resolves to the default.

ThemePicker is laid out as GitHub's Appearance settings. A Light · Dark segment picks the
side, and two cards always follow — Light theme and Dark theme, side by side or stacked in a
narrow container — each with one large ThemePreview of its choice, the theme's name, and a row of
round swatches painted by each theme's real --background and --primary. Choosing a swatch files
that side's theme and never flips the appearance; the card of the side worn carries Active.
ThemePickerCopy is now appearance, light, dark, day, dayDescription, night,
nightDescription, active, retired.

A scoped theme no longer inherits the page's optional tokens. A light preview on a dark page drew
the dark theme's sidebar and popover, because a theme that leaves --popover, --sidebar or another
optional token unsaid inherited the page's value. Every [data-theme] element now resets them, so
the bridge's fallback resolves against the theme on that element.

@kanzo-tech/graph

GraphLegend is the category rows only; what is drawn of the whole is the new GraphCounts.
The legend's last line, "N of M drawn", is gone, and a legend with no category to key (colour a
constant, nothing on symbol) now renders nothing instead of an empty box. GraphCounts says it
in one sentence — "1.2K of 5K nodes drawn · 8K edges" — compact in the nearest LocaleProvider's
locale, with "—" for a figure not yet known and an optional spinner while the graph loads.
Drawn gains edges, the links whose two ends are drawn. If you read the count off the legend, put
<GraphCounts /> where you want it.

Dragging a node pins it only while a layout runs, and releasing the pins visibly re-flows the
graph.
Before, a drag pinned the node even with no layout running, where a pin holds against
nothing, and "Release N pinned nodes" cleared the pins without moving anything. Now a drag with the
layout settled just moves the node; a drag while the layout runs or is paused pins it; and
unpin() — the toolbar's release button — reheats the layout: a running one keeps running, a
settled one cools back to settled on its own, and a paused one runs briefly and pauses again.
Nothing to change in your code.

GraphLooks: the graph's picture as three looks and the axes under them. A new part for a
preferences panel or a dock: Nebula, Atlas and Ink as cards with live miniatures, and the axes
folded under "Customize" — Marks as a segmented control, the rest as GRAPH_SECTION declares them.
It needs GRAPH_SECTION among the provider's sections (without it, it draws nothing) and no
GraphRoot. useGraphPrefs() now also answers preset — "nebula" | "atlas" | "ink" | null, typed
LookPreset — so a host can pair channels with a look, e.g. Ink with fill="var(--foreground)" and
symbol on the category. If you hand-built look cards over the section, <GraphLooks /> replaces
them.

GraphSearch: find a vertex by its text and go to it. A new part under GraphRoot. It reads
every drawn vertex's title (or its table's identity) once, filters in the browser as the reader
types — no query per keystroke — orders matches by the r ramp so the hubs come first, names each
by the root's categories, and picking one calls reveal. Props: limit (default 50),
placeholder, size, className. If you wrote your own search over the corpus, <GraphSearch />
replaces it.

GraphSearch and useFieldStats keep a failure whole, like every other part since v0.19.0.
GraphSearch hands a failed read of the names to the root's onFailure as the thrown value and its
input says "The names could not be read." instead of staying disabled. useFieldStats (and so
Dashboard) reports a failed SUMMARIZE to the MosaicProvider's onFailure, and its error is
now the thrown value (unknown) rather than a re-wrapped Error — if you read error.message, narrow
it first.

A graph opens framed even when its canvas settles its size after mount. In a split panel or
beside a dock the canvas width changes after the first frame, and the view opened zoomed into one
corner until you pressed Fit. The camera now frames the corpus again when the GPU device comes up
and on every resize of the canvas, until you zoom or pan yourself; Fit hands it back. Nothing to
change in your code.

A running layout no longer collapses the graph into a corner and leaves it there. The corpus's
positions are now centred in the square cosmos.gl simulates in, so the layout contracts around the
graph's own centre; and while it runs the camera re-frames the moving points, and frames them once
more when they settle, until you zoom or pan yourself. Fit, after a layout has run, frames where the
points are. GraphCounts spinner also spins while a layout runs ("Laying out"). status stays the
data's life — a layout over a drawn graph is idle — so a footer that wants to say "laying out"
reads motion === "running" (and progress). Nothing to change otherwise.