Repository navigation
v0.20.0
@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-mochaandlofi/lofi-dark. Every other theme is deleted —
a stored or configureddracula,bank,monochrome,catppuccin-latte-dark… is retired (the
provider clears it and reports it throughonThemeRetired). themeIndexentries areThemeOption { value, label, dark, family }(was{ name, dark });
ThemeIndexEntryis gone.ThemeOptiongains the requireddarkand an optionalfamily. New:
themeFamilies,defaultThemePair,CONTRAST_PAIRS,auditContrast,ThemeFamily,
ContrastPair,ContrastFinding.KanzoThemeProvider:themesdefaults tothemeIndex, sothemes={themeIndex.map(…)}can be
deleted.defaultThemeis a{ light, dark }pair only (a string no longer type-checks) and
defaults to the first family. The resolved theme is always written todata-theme, the default
included.themeScript({ defaultTheme })takes the same pair — pass it whenever you pass it to
the provider.PreferencesColorandPreferencesColorPropsare removed. Use<ThemePicker />(GitHub's
Appearance settings: a Light · Dark switch and a card per side); thePreferencespanel draws it first.
ThemePreviewis the miniature it draws, exported for catalogues and branding pages.- Syntax tokens:
--syntax-variableand--syntax-annotationare new and--syntax-identifieris
removed; a tenant theme must author all eight--syntax-*, plus--input,--fieldand
--faint, which components now read with no fallback. - Fonts: Geist and Geist Mono are the default
font/monoFont(DEFAULT_PREFSwas"system"),
each falling back to the system face;--font-monono 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 novalueit draws an automatic
dashboard from the relation's field stats; every edit callsonChangewith the whole
DashboardSpec, plain JSON for the host to save.excludehides 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/useClausesfor any selection.useFieldStats/queryFieldStatsread a
relation's fields (oneSUMMARIZE);autoDashboard(fields)is the automatic spec. useMosaic()gainsretract(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,DashboardGridorFilterChipsframes: the first two are
Cardparts and a grid class now, andFilterChipsis the library's — import it from
@kanzo-tech/ui/analytics(it has noemptyprop; render your own hint whileuseClausesis
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-mochaandlofi/lofi-dark. Every other theme is deleted —
a stored or configureddracula,bank,monochrome,catppuccin-latte-dark… is retired (the
provider clears it and reports it throughonThemeRetired). themeIndexentries areThemeOption { value, label, dark, family }(was{ name, dark });
ThemeIndexEntryis gone.ThemeOptiongains the requireddarkand an optionalfamily. New:
themeFamilies,defaultThemePair,CONTRAST_PAIRS,auditContrast,ThemeFamily,
ContrastPair,ContrastFinding.KanzoThemeProvider:themesdefaults tothemeIndex, sothemes={themeIndex.map(…)}can be
deleted.defaultThemeis a{ light, dark }pair only (a string no longer type-checks) and
defaults to the first family. The resolved theme is always written todata-theme, the default
included.themeScript({ defaultTheme })takes the same pair — pass it whenever you pass it to
the provider.PreferencesColorandPreferencesColorPropsare removed. Use<ThemePicker />(GitHub's
Appearance settings: a Light · Dark switch and a card per side); thePreferencespanel draws it first.
ThemePreviewis the miniature it draws, exported for catalogues and branding pages.- Syntax tokens:
--syntax-variableand--syntax-annotationare new and--syntax-identifieris
removed; a tenant theme must author all eight--syntax-*, plus--input,--fieldand
--faint, which components now read with no fallback. - Fonts: Geist and Geist Mono are the default
font/monoFont(DEFAULT_PREFSwas"system"),
each falling back to the system face;--font-monono 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.
DashboardFiltersandDashboard'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
spannow applies inside aDashboard's grid, measured on the dashboard. DetailTableformats a time by its column's type: aDATEas a day (no time, and in UTC so
it is not the day before west of Greenwich), aTIMESTAMPas its wall clock, aTIMESTAMPTZin
the viewer's zone, aTIMEas it came.- A stacked area completes its grid.
ChartAreaY/ChartAreaXstacked 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 namedx/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. queryFieldStatsresolves to{ fields, columns }(was the field array) anduseFieldStats
returnscolumnsbesidefields. If you calledqueryFieldStats, 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.