Skip to content

v0.35.0

Choose a tag to compare

@angel-ip angel-ip released this 09 Oct 13:06
c85f487

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.