You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
v0.35.0
New: @kanzo-tech/testing
A harness per component, to drive a page built with Kanzo UI from a test.GraphCanvasHarness, ChartHarness, TimelineHarness, DashboardHarness, FilterBarHarness, AnswerHarness, FindingsHarness and DockHarness find their component by its ARIA and operate it — brush a chart in data, pick a bar by its value, lasso a graph over the points you name — and wait on its state, never on a time.
Make the environment before the page loads: const env = await playwright(page) in Playwright, or dom() from @kanzo-tech/testing/dom before render in a unit test. Install it as a dev dependency; it imports none of the components.
DockHarness takes the first radiogroup on the page; pick yours with DockHarness.with({ name: "Panels" }). AnswerHarness.addToDashboard() waits out the pending write, then for ✓ On the dashboard.
@kanzo-tech/ui
A timeline: ChartTimeline in @kanzo-tech/ui/analytics. A brushable histogram of one temporal column with a play button, on the page's crossfilter. The window is one clause, a chip in the FilterBar; playing moves it a bar every 50 ms. A dashboard's temporal filter is this part now. ChartRoot's context gains plot().
Breaking: ShellDockSwitcher is typed by your panel id and must be named. The "Panels" default is gone: pass exactly one of aria-label or aria-labelledby. value and onValueChange take the type of value.
useDebouncedCommit's commit answers what onCommit answered, so a caller can wait on the save ((next) => save.mutateAsync(next)).
What components now expose for @kanzo-tech/testing, which you may see in markup and snapshots: ChartRoot is a role="figure" named by its aria-label and aria-busy while it draws or queries; FilterBar's readout is a role="status"; Dashboard is aria-busy while it reads its fields; PromptInputTextarea is named by its placeholder.
Size budgets raised as a decision: analytics 32.9 → 33.4 kB, one dashboard 31.5 → 31.8 kB.
@kanzo-tech/graph
GraphTimeline, the timeline under the canvas, over every vertex table that has the column. The column is the setting Timeline (time-by), beside X axis and Group by; none, the default, draws no timeline. useGraphPrefs() answers it as timeline.
Requires a corpus attached by @fossil-lang/corpus 0.3.0-alpha.28 or later: the graph reads fossil_columns.datatype, and fails to read the structure without it.
Breaking: corpusReferences and CorpusReference are gone. Read the joins from readJoinGraph(coordinator, from) instead.
GraphCanvas is aria-busy until its first frame, carries data-frame, and always has an id.
Size budget raised as a decision: 23 → 23.5 kB.
@kanzo-tech/ai
Breaking: AnswerCard's Add to the dashboard takes your dashboards and hands back the next document.onAdd(key, add) is gone: pass dashboards with onAdd(next, { relation, key }), called with the whole next document. ✓ On the dashboard is read from dashboards, so it survives a remount. Return your save's promise and the button is pending until it settles; a rejection is drawn as a Problem in the words of the new copy prop. The tile's id is the answer's. AnswerAdded is exported.
answerRelationsOf(graph, { from, hops }) says which relations may be asked about — each type alone and through its hops; hops: 0 offers none. readAnswerRelations takes a signal and cancels its queued queries when it aborts.
The card stays aria-busy until the answer's tile, or its failure, is drawn.
@kanzo-tech/llm
A gateway error says what it was, as ai/unavailable. Any error answer but 429 — an alias no model answers, a provider's refusal, a 504 from a hung upstream — fails as an AiError coded ai/unavailable, with data.status and data.reason, instead of the SDK's uncoded APICallError, and is not retried. A host that matched on APICallError for these matches on the code instead.
@kanzo-tech/mosaic
engine() no longer loads DuckDB's json extension, and the package no longer ships it: about 820 kB (190 kB gzipped) less per page, 1.5 MB less installed. Your own SQL that calls a json function now fails naming json — tell us and it goes back in.
The services: Compose is the whole of it
Files.services/auth and services/ai are compose.yaml (the service as it runs anywhere) plus compose.dev.yaml (published ports, the seed, local models). services/compose.yaml is both as deployed, services/compose.dev.yaml both for development — an application includes one URL per environment: https://github.com/Kanzo-Tech/ui.git#v0.35.0:services/compose.yaml. Nothing needs vendoring.
Registering an application. The realm is applied from declarations mounted under /declarations in auth-realm: an x-application (client, own API, apis, origin and callback, roles, client_secret_file) and an x-organization per organization. An application runs no Terraform of its own; examples/app, realm/dev.tfvars and the realm's organizations/conformance variables are gone. modules/app sends a confidential client's secret write-only (Terraform ≥ 1.11, keycloak provider ≥ 5.10): it never reaches state, and the client_secret output is gone. In development the seed merges an application's roles on its groups from /seed.d.
The gateway.services/ai/modules/gateway is gone, and so are CHAT_URL/CHAT_MODEL. Which model answers an alias is AI_CHAT / AI_COMPLETE, provider/model (anthropic/…, openai/…, gemini/…, mistral/…, openrouter/…, local/…), with the provider's key under its usual name. Development defaults both to local models; the local-models profile pulls them with an 8192-token context that fits a 16 GB laptop. The budget is AI_TOKENS_PER_HOUR; the issuer follows KC_HOSTNAME.
A development realm volume from an earlier release holds state in the old shape: docker compose down -v once.