@aiquants/enrichment-react 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -40
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +92 -21
- package/dist/index.d.ts +92 -21
- package/dist/index.js +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @aiquants/enrichment-react
|
|
2
2
|
|
|
3
|
-
React UI components for storage metadata AI enrichment: the ingest console, the pipeline flow viewer and
|
|
3
|
+
React UI components for storage metadata AI enrichment: the ingest console, the pipeline flow viewer, the document change history inspector and the recent changes card, the search and model-comparison workbenches, the strategy board, and the status, health, storage, and ontology cards.
|
|
4
4
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ pnpm add @aiquants/enrichment-react @aiquants/enrichment-core @aiquants/console-
|
|
|
19
19
|
|
|
20
20
|
- **Host owns routes and lists.** No component names an application route or ships a domain list; the host passes `onSearch`, `onCompare`, `domainOptions`, and the like.
|
|
21
21
|
- **DOM hooks are `data-*` / `role` / element only.** Class names are styling and change freely.
|
|
22
|
-
- **Rendered inside the console root.** `InfrastructureHealthMonitor`, `PipelineFlowViewer`, `
|
|
22
|
+
- **Rendered inside the console root.** `InfrastructureHealthMonitor`, `PipelineFlowViewer`, `DocumentHistoryInspector`, `DocumentChangeFeed`, `QdrantStorageCard`, `SyncStatusMonitor`, `DocumentIngestConsole`, and the instant slots `RecordedInstant` and `FinishedInstant` read `@aiquants/console-ui`'s `ConsoleUiRoot` and throw outside it. The other components do not read the root and render anywhere.
|
|
23
23
|
Every component that reads the root checks it before drawing anything (the root requirement below), so none of them reads the root, draws the root's label or writes in the root's zone under a root it was not written for.
|
|
24
24
|
This package ships no stylesheet (the host's Tailwind build compiles its classes), and the host loads `@aiquants/console-ui/styles/console-ui.css` once, with its `cui` cascade layer ranked above the host's reset, as `@aiquants/console-ui`'s README describes under "Loading the stylesheet" (for a Tailwind v4 host, `@import` it in the CSS entry after the layer statement `@layer theme, base, components, utilities;`).
|
|
25
25
|
Without it the root's `--cui-*` token variables are unset, a `Measured` count loses its tabular figures, `QdrantStorageCard`'s name buttons (`@aiquants/console-ui`'s `RowButton`) are drawn as the browser's plain buttons without the selected look, `InfrastructureHealthMonitor`'s probe-state marks and `QdrantStorageCard`'s health badge lose their role colours (`success-fg` / `danger-fg` and their tints), and the root's live region (`role="status"`) is drawn in the flow of the page instead of being visually hidden.
|
|
@@ -34,15 +34,15 @@ pnpm add @aiquants/enrichment-react @aiquants/enrichment-core @aiquants/console-
|
|
|
34
34
|
</ConsoleUiRoot>
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
- **Unmeasured is not zero.** A figure the source could not measure renders as the root's unmeasured label (`未測定` under `locale="ja"`), never as `0`. In `InfrastructureHealthMonitor`, `PipelineFlowViewer`, `
|
|
38
|
-
|
|
37
|
+
- **Unmeasured is not zero.** A figure the source could not measure renders as the root's unmeasured label (`未測定` under `locale="ja"`), never as `0`. In `InfrastructureHealthMonitor`, `PipelineFlowViewer`, `DocumentHistoryInspector`, and `QdrantStorageCard`, every value slot that holds one is `@aiquants/console-ui`'s `Measured` (directly or through `MeasuredText` / `MeasuredFigure`), so `data-measured="false"` marks every unmeasured value slot there.
|
|
38
|
+
A run's step report in the history inspector is the one place an unmeasured figure has no slot of its own: a report field that was not measured is named in the step's `未測定:` line instead (see `DocumentHistoryInspector`), and only a page pair with one half measured keeps both `Measured` slots.
|
|
39
39
|
The other components keep their own words for a missing value: `DocumentIngestConsole` writes a missing revision as `N/A` (it reads the root for its instants and its byte format only), `MetadataSearchWorkbench` a missing lexical or graph rank (`字句順位` / `グラフ順位`) as `N/A`, and `OntologyStatusCard` a missing count as `未設定`; `SyncStatusMonitor` writes its counts as bare numbers.
|
|
40
40
|
A count drawn as a value — the Qdrant totals, segment count, collection count, each collection's points and each named vector's dimensions (`次元`), the health probe's counted details, every probe latency in ms (応答時間 in both `InfrastructureHealthMonitor` and `QdrantStorageCard`), the flow metric's sample, succeeded, failed and skipped counts,
|
|
41
41
|
and every count the core record types as a measured count (the step duration and the report's `duration_ms` in ms, characters, pages, chunks, points, graph nodes, and the flow's p50 / p95 latencies in ms) — is `Measured`, written in the root's `formatLocale` with tabular figures and `data-measured="true"`.
|
|
42
42
|
The components that do not read the root write their durations as bare numbers, not grouped: `ModelComparisonWorkbench`'s execution time (`compare-execution-ms-${strategyId}`) and `MetadataSearchWorkbench`'s search latency.
|
|
43
43
|
A measured value that is not a count is written by its own format inside one of the package's two private value slots, in tabular figures with `data-measured="true"`, and when it was not measured the slot writes the same `Measured` with the label.
|
|
44
44
|
A value that reads as one unit — a byte size (`1.0 MiB`), an instant, a probe's port or boolean — is `MeasuredFigure`, which never breaks inside (`white-space: nowrap`), so a number never parts from its unit or a date from its time; a text — a probe's text, a service's version, the report's `prompt_version` and its other texts — is `MeasuredText`, which wraps with its container.
|
|
45
|
-
Every recorded instant, in each of the
|
|
45
|
+
Every recorded instant, in each of the components that write one (`InfrastructureHealthMonitor`, `PipelineFlowViewer`, `DocumentHistoryInspector`, `DocumentChangeFeed`, `SyncStatusMonitor`, `DocumentIngestConsole`), is written by the exported `RecordedInstant` (or `FinishedInstant`, for a finish time that may be open) into a `MeasuredFigure` slot under a `-value` hook:
|
|
46
46
|
`health-checked-at-value`, `flow-metric-last-started-at-value`, `run-history-started-at-value-${id}`, `run-started-at-value`, `run-finished-at-value`, `failed-item-time-value-${id}`, `ingest-last-run-finished-at-value-${id}` and `ingest-created-at-value-${id}`, so a column of instants lines up digit by digit.
|
|
47
47
|
A state shown in place of an instant is a word, not a slot: `未完了` for a run that has not finished (`FinishedInstant` writes it in `run-finished-at` and `ingest-last-run-finished-at-${id}`) and `記録なし` for a failure the record holds no time for (`failed-item-time-${id}`).
|
|
48
48
|
A text with nothing to read is refused like a broken count, with a `RangeError` that names the slot's hook: drawn, it would be an empty slot that claims a measurement. Pass `null` for a value that was not measured.
|
|
@@ -58,14 +58,14 @@ pnpm add @aiquants/enrichment-react @aiquants/enrichment-core @aiquants/console-
|
|
|
58
58
|
the host resolves the zone once on the server and passes it to the root, so the server-rendered page and the hydrated one print the same string, and a host that switches the root's zone re-writes every mounted instant in the new clock.
|
|
59
59
|
A timestamp is ISO 8601 with an explicit offset (`Z`, `±hh:mm` or `±hhmm`) naming an existing date and time: every text the core record schemas refuse (`z.string().datetime({ offset: true })`) throws, and so does an accepted text that names no instant the console can show (an offset beyond ±23:59, or a wall clock outside years 1–9999 in the zone). The refusal is a `ZonedTimeError` (a `RangeError` with `reason: "malformed"`).
|
|
60
60
|
- **Layout.** The components use Tailwind's stock spacing, sizes, and palette, not `@aiquants/console-ui`'s tokens (its 8-px grid and 11 / 13 / 16 / 20 / 25 px type scale).
|
|
61
|
-
Most cards are `rounded-xl border border-slate-200 border-solid bg-white p-6 shadow-sm`; `InfrastructureHealthMonitor` is `rounded-lg border border-solid bg-card p-5 shadow-sm` (the host's `card` theme colours), and its probe-state marks alone take `@aiquants/console-ui`'s colour roles (`success-fg` on `success-tint`, `danger-fg` on `danger-tint`, see its section). `PipelineFlowViewer`,
|
|
61
|
+
Most cards are `rounded-xl border border-slate-200 border-solid bg-white p-6 shadow-sm`; `InfrastructureHealthMonitor` is `rounded-lg border border-solid bg-card p-5 shadow-sm` (the host's `card` theme colours), and its probe-state marks alone take `@aiquants/console-ui`'s colour roles (`success-fg` on `success-tint`, `danger-fg` on `danger-tint`, see its section). `PipelineFlowViewer`, the history inspector's step timeline, and `ModelComparisonWorkbench` set type at 11 / 13 / 16 px; the history screens (`DocumentHistoryInspector`, `DocumentChangeFeed`) take their type, spacing and colours from `@aiquants/console-ui`'s tokens and roles only; the other Tailwind-styled components also use 10 px, `text-xs` (12 px), and `text-sm` (14 px).
|
|
62
62
|
`DegradedSearchBanner`, `QdrantStorageCard`, and `SyncStatusMonitor` are styled inline, with no Tailwind classes. The two cards have an 8-px radius and 16-px padding, set type from 11 px up to 18 px (`QdrantStorageCard`) or 20 px (`SyncStatusMonitor`), 12 and 14 px included, and use spacings such as 2, 6, 10, and 14 px that are off the 4-px half-step grid; the banner has a 6-px radius and a padding of 12 px by 16 px, and takes its type size from the host.
|
|
63
63
|
- **One card-title scale.** Every card title (`h2`) is 16 px bold (`text-base font-bold`, or `fontSize: "16px", fontWeight: 700` where the component styles inline) — the same scale `@aiquants/enrichment-react-router` and `@aiquants/governance-react` use, so sibling cards on one tab read as one heading level. `tests/HeadingOutline.spec.tsx` checks every panel's `h2`.
|
|
64
64
|
- **Borders name their style.** Every Tailwind border-width utility (`border`, `border-t`, `border-2`, …) sits in the same class list as `border-solid` (or another explicit style), and every `divide-x` / `divide-y` next to `divide-solid`. A host whose base layer sets `border-style: none` on every element (for example `* { @apply border-none }`) would otherwise leave those utilities with a width and no line.
|
|
65
65
|
`tests/borderStyleContract.spec.ts` scans every string literal in `src/` and fails on a width without its style. Per-side widths (`border-t border-solid`) rely on the host's Tailwind preflight setting the other sides to width 0.
|
|
66
66
|
- **Nothing is clipped without a way to read it, and nothing that reads as one unit breaks inside where the layout holds it.** Labels that must stay on one line (`whitespace-nowrap`) move to the next row instead of shrinking (the search-mode buttons, the health badges, the Qdrant card's badge, latency and refresh button), and unbroken identifiers (URIs, versions, endpoints, the run inspector's report texts) wrap anywhere (`wrap-anywhere`) inside their card instead of overflowing it or ending in an ellipsis.
|
|
67
67
|
A value that reads as one unit — a count with its unit, a latency, a byte size, an instant, the unmeasured label — never breaks inside in these places, so `1,234 件` never stands as `1,23` over `4 件` and `1,234 ms` never as `1,234` over `ms` there:
|
|
68
|
-
`InfrastructureHealthMonitor`'s rows (in a narrow row the label wraps beside the value instead), the Qdrant card's header (the value moves to the next line whole) and its named-vector tags, the measured figure in every `MeasuredFigure` slot (byte sizes, instants, a probe's port or boolean), `PipelineFlowViewer`'s p50 / p95 latencies, and
|
|
68
|
+
`InfrastructureHealthMonitor`'s rows (in a narrow row the label wraps beside the value instead), the Qdrant card's header (the value moves to the next line whole) and its named-vector tags, the measured figure in every `MeasuredFigure` slot (byte sizes, instants, a probe's port or boolean), `PipelineFlowViewer`'s p50 / p95 latencies, and the history inspector's step report (each report value sits below its label with the column's whole width).
|
|
69
69
|
Any other `Measured` slot (a step's duration in its summary line, the flow metric's counts) does not hold the rule itself, and neither does an unmeasured `MeasuredFigure` slot elsewhere (the Qdrant card's RAM and disk tiles, the flow metric's last start), which is `@aiquants/console-ui`'s `Measured` with its label: `Measured` joins a count and its unit with a plain space and leaves its text free to wrap, so in a column narrower than the value a number can part from its unit and the label can break between its characters.
|
|
70
70
|
|
|
71
71
|
## Exported Components
|
|
@@ -84,7 +84,7 @@ Manual ingestion form (URI, confidentiality level, target domain, labels), file
|
|
|
84
84
|
The table shows sync status, the last pipeline run (`ingest-last-run-${id}`: the shared run badge `ingest-last-run-status-${id}` with `data-presentation`, the failed step, and the finish time `ingest-last-run-finished-at-${id}`, its instant under `ingest-last-run-finished-at-value-${id}` or `未完了` while the run is open), the registration time (`ingest-created-at-value-${id}` inside `ingest-created-at-${id}`), both in the root's zone, and — when `onShowHistory` is given —
|
|
85
85
|
a `履歴` button per row (`ingest-history-button-${id}`) that asks the host to open the document's whole processing history (called for rows without a recorded run too; the inspector then says there is none).
|
|
86
86
|
|
|
87
|
-
Required props: `tenantId`, `domainOptions: readonly { key; label }[]` (empty → the select is disabled and `ingest-domain-empty` explains why). The console reads the root's zone and format, so it renders inside `ConsoleUiRoot`. Each `RecentIngestItem` carries `lastRun: { runId; status; retryScheduled; failedStepKey; finishedAt } | null`; a `FAILED` run with `retryScheduled: true` reads `失敗 (再試行予定)
|
|
87
|
+
Required props: `tenantId`, `domainOptions: readonly { key; label }[]` (empty → the select is disabled and `ingest-domain-empty` explains why). The console reads the root's zone and format, so it renders inside `ConsoleUiRoot`. `recentItems` is `readonly RecentIngestEntry[]`: a readable row `{ kind: "readable", item: RecentIngestItem }` or a restricted mark (`RestrictedIngestMark`, V11) that the table draws as one row with the fixed text `隔離 (RESTRICTED) の文書`, its registration time and its sync status only (`recent-item-restricted-${position}`, `data-restricted-mark="ingest"`), with no history entry and no row action; a mark whose `syncStatus` is `null` (the document has no enrichment record) reads the neutral `取り込みの記録なし` (`NO_ENRICHMENT_RECORD_TEXT`, no status badge) in its status cell (`recent-item-restricted-status-${position}`). Each `RecentIngestItem` carries `lastRun: ReadableEntry<{ runId; status; retryScheduled; failedStepKey; finishedAt }> | RestrictedRunMark | null`; a `FAILED` run with `retryScheduled: true` reads `失敗 (再試行予定)`, and a last run that is a mark reads `隔離されていた期間の実行` with its status (`ingest-last-run-restricted-${id}`). `listRefusal?: string` is the full sentence that refuses the operator the list (no `file:read`, or a column-scoped one; V6 / V9): when set, the list is replaced by that sentence in a warning `Notice` (`recent-items-refused`), with no row, no empty sentence (`取り込み履歴はありません。` would claim that nothing was ingested) and no row action.
|
|
88
88
|
The inspection result writes the file's size under `inspection-file-size-value` (`MeasuredFigure`: tabular figures, `data-measured="true"`, never broken between the number and its unit, the same slot as `QdrantStorageCard`'s byte sizes) with the package's one byte format:
|
|
89
89
|
binary units (B below 1 KiB, then KiB / MiB / GiB / TiB) chosen after rounding (1,048,575 bytes is `1.0 MiB`), the mantissa grouped in the root's format locale (`3,072.00 TiB`). `fileSize` must be a non-negative safe integer; otherwise the console throws `RangeError` naming `fileSize`, with `@aiquants/console-ui`'s `formatCount` refusal as its `cause`, instead of writing a negative size as `0 B` or a fraction without a unit.
|
|
90
90
|
That refusal is a backstop: thrown while the console renders, it would take the operator's unsent form input with the console. The host refuses a broken size where it reads the inspect response (its schema fails the inspect request, shown as a failed inspection), so a value that reaches the console is valid.
|
|
@@ -173,37 +173,30 @@ The pure layout lives in `layoutFlow(flow, geometry = FLOW_GEOMETRY)` (`pipeline
|
|
|
173
173
|
Text is sized by `@aiquants/console-ui`'s fixed sans model (`textWidthEm`: full-width glyph 1 em, half-width 0.55 em), the one text-width model of the console, of which this package keeps no copy;
|
|
174
174
|
`FLOW_GEOMETRY.node.width` is the widest step label at 13 px plus the text inset on both sides (`FLOW_NODE_TEXT`), rounded up to the 8-px grid with `ceil8` (184 for the shipped steps); the padding keeps a focus ring (`FLOW_FOCUS_RING`) inside the figure and the gaps keep it off the neighbouring nodes.
|
|
175
175
|
|
|
176
|
-
### 9. `
|
|
177
|
-
|
|
178
|
-
A non-modal dialog panel (`
|
|
179
|
-
|
|
180
|
-
Props: `
|
|
181
|
-
|
|
182
|
-
- **
|
|
183
|
-
- **
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
- **
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
A text field (`provider`, `model`, `engine`, `prompt_key`, `collection`, `embedding_model`) that is empty or only white space throws `RangeError` naming its hook (`run-step-provenance-value-${sequence}-provider: a measured text must not be empty or only white space …`), and the whole panel is not drawn. The core record schemas still accept such a string, so a host that reads persisted records refuses it where it reads them.
|
|
195
|
-
A skipped step shows its reason through `skipReasonLabel`; a `SKIPPED` step with no reason, which `PipelineStepRecordSchema` refuses, throws `RangeError` instead of drawing a stand-in. The child records of a fan-out step (`fanOut: "children"`) are grouped under the step (`run-step-group-${stepKey}`) with a count and a per-status breakdown; the group is open when a child failed or the run is short.
|
|
196
|
-
A page pair with one half measured is two `Measured` values (`…-pages_read`, `…-pages_total`) and reads `3 / 未測定` or `未測定 / 5`.
|
|
197
|
-
The run list's count (`実行の履歴 (<n> 件、新しい順)`) and a fan-out group's count (`対象 <n> 件`) and per-status breakdown are written as bare numbers: they are not `Measured`, so they are not grouped and do not follow the root's `formatLocale`.
|
|
198
|
-
- **Focus:** focus moves to the heading when the panel mounts and whenever `openRequest` changes; the element outside the panel that held focus then becomes the opener (none when nothing held focus; a request that arrives while focus is inside the panel keeps the previous opener).
|
|
199
|
-
A run picked in the panel's own list moves focus to that run's heading (`run-detail-heading`) once the host selects it; any other change of `metadataId` or `selectedRunId` — such as a load settling on the newest run — leaves focus where it is.
|
|
200
|
-
Escape (while focus is inside) and the close button call `onClose`; on unmount focus returns to the most recent opener while it is still in the document, and otherwise to the element `returnFocusFallback()` returns (nothing moves when it returns `null`).
|
|
176
|
+
### 9. `DocumentHistoryInspector`
|
|
177
|
+
|
|
178
|
+
A non-modal dialog panel (`history-inspector`, `role="dialog"` without `aria-modal`) showing one document's change history across its revisions (S1 §3.3.5-13, §8.19). It reads through the host and draws only what the host returns.
|
|
179
|
+
|
|
180
|
+
Props: `host: { loadHistory, compare, readLines }` (`DocumentHistoryHost` without `loadChanges`; every read resolves to a `ResourceOutcome`), `metadataId` (any revision row of the document), `target: DocumentHistoryTarget` (`{ kind: "latest_state" }` for an ingest row's history, `{ kind: "run", runId }` for a recent run's detail, `{ kind: "state", stateId }` for a change's arriving state), `openRequest: number` (changed by the host on every open or re-target from outside, including a press naming what is already shown; each change reads the history again, keeping the shown one until the answer settles and withdrawing it on a 403, and applies `target` to the fresh history; while the re-read is in flight, a target the shown history does not hold reads `文書の履歴を読み込んでいます…` (`history-detail-loading`, `aria-busy`) instead of being reported missing), `onClose()`, `returnFocusFallback: () => HTMLElement | null`, and the optional `captureState(metadataId)` (the host's manual capture, a `ResourceOutcome<{ outcome }>`).
|
|
181
|
+
|
|
182
|
+
- **Header:** `h2` (`history-heading`) `文書の履歴 — <登録名>` and a `dl` with the URI, the current revision (`r3`), the confidentiality level, the number of revisions and the last change's time.
|
|
183
|
+
- **Timeline (master, `MasterDetail` with a 288 px master and a 448 px detail minimum):** one group per revision, newest first; the current revision opens, older ones start closed; inside, the readable runs (with the badges of the state each produced, `本文 +12 −3`, `チャンク +2 −1 ~4`, `分類 変更`) and the states no readable run produced (`上書きの前の記録`, `手動の記録`, `記録の起点`), newest first, as `SelectableList` rows (`history-item`, `data-item-key="run:<id>"` / `"state:<id>"`).
|
|
184
|
+
Marks are written as sentences: `失敗した実行の後の状態`, `記録されていません` (a finished run with no terminal step or a failed one), `この変化には記録されなかった実行 #n の変化も含まれます`, and `ソースは同じで、モデルの出力だけが変わりました`.
|
|
185
|
+
Restricted marks (V11) sit in their own group ahead of the revisions, `隔離されていた期間の記録 (n 件)`, closed at first, one row each (`history-restricted-mark`, `data-restricted-mark="state" | "run"`) with `隔離されていた期間の状態` / `隔離されていた期間の実行`, the time and the result only; a mark cannot be selected, pinned or returned to, and its React key is its position in the answer.
|
|
186
|
+
- **Detail (`history-detail`):** one `h3` (`history-detail-heading`, the same element whatever it shows) and the selected state's change from its predecessor (`DocumentChangeView`: a `nav` of changed facets, one `section` with an `h4` per changed facet, `変化なし` for the rest; the text and summary as `TextDiffView`, the chunks as `ChunkChangeTable`). A first state and a state whose predecessor the operator cannot read both say `直前の記録との比較はありません。` and read no comparison.
|
|
187
|
+
A run shows its summary, its error, its state's change and its step timeline (`PipelineRunSteps`, the terminal step named `文書の状態の記録`) in a `<details>`.
|
|
188
|
+
`比較の起点にする` pins a state (`history-pin-button`); the next state selected is compared with it, older side first; `外す` (`history-unpin`) releases it.
|
|
189
|
+
- **Text diff:** statistics (lines and graphemes, the similarity or the root's unmeasured label), attribute changes (`改行 CRLF → LF`), the hunk index (`45 行目 (p.3) +3 −1`), and hunk bodies read only when opened, `DIFF_LINE_PAGE_MAX` lines a page (`LoadMore`). Side by side applies from `splitViewMinPx` (2 × (line-number gutter + sign column + 40 characters) + 16 px in the 13 px mono face, 784 px for 5 digits); narrower, the view keeps one column and says `幅が足りないため 1 列で表示しています` in a visible sentence, and the change view announces it once through the root's announcer, however many diffs it holds. It measures its width with `ResizeObserver`. Each line table is named after its facet and hunk (`本文 45 行目 (p.3) +3 −1`); side-by-side rows are paired page by page (never across a page boundary) and keyed by their line numbers, so a page read later only appends rows and `LoadMore` focuses its first row.
|
|
190
|
+
- **Failures** are worded by `labels.failureSentence`: a 403 is `文書の履歴を閲覧する権限がありません。<原因>。` and withdraws a history already shown; any other failure is `文書の履歴を読めませんでした (<理由>)。` (`比較`, `差分の行` for the other reads) with a reload. A document with no record says so in a `role="status"` sentence.
|
|
191
|
+
- **Manual capture:** with `captureState`, `今の状態を記録` appears while the newest readable run has no state; the outcome is announced (`記録しました` / `前の記録から変化はありません`) and the history is read again.
|
|
192
|
+
- **Nothing to select:** a history with restricted marks only says `選べる記録はありません。` (`role="status"`) in the detail instead of prompting a pick. The revision groups' open state belongs to the document: re-targeting another document starts again from the defaults.
|
|
193
|
+
- **Focus:** only the `h2` and the detail `h3` take focus (both `tabIndex={-1}`); pressing the row already selected moves focus to the `h3` too. When `今の状態を記録` disappears while holding focus (the recorded state was read back), focus moves to the detail `h3`, or the `h2` when nothing is selected; when a re-read's 403 withdraws content that held focus, focus moves to the `h2`. A refresh failure that repeats with the same words is announced again (its `AlertMessage` occurrence counts the failures). The `h2` takes it on mount and whenever `openRequest` changes, recording the element outside the panel that held focus as the opener; an item picked in the panel's own timeline moves focus to the `h3`; a load settling moves nothing. A selected item inside a closed revision group opens the group. Pinning and unpinning move focus to the control that replaced the one holding it. Escape (with focus inside) and the close button call `onClose`; on unmount focus returns to the opener while it is still in the document, otherwise to `returnFocusFallback()`.
|
|
201
194
|
|
|
202
195
|
```tsx
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
<DocumentHistoryInspector
|
|
197
|
+
host={documentHistoryHost} metadataId={entry.metadataId} target={entry.target}
|
|
198
|
+
openRequest={entry.openRequest} onClose={closeHistory}
|
|
199
|
+
returnFocusFallback={() => tabRefs.current[activeTab]} captureState={captureDocumentState}
|
|
207
200
|
/>
|
|
208
201
|
```
|
|
209
202
|
|
|
@@ -233,7 +226,15 @@ Without `selection` the names are text and every row is `data-selected="false"`.
|
|
|
233
226
|
|
|
234
227
|
### 11. `SyncStatusMonitor`
|
|
235
228
|
|
|
236
|
-
`sync_status` breakdown with individual and bulk retry controls. Required props: `counts`, `totalFiles`; each failed item's `failed_at` (an ISO 8601 instant with an explicit offset) is written in the root's zone under `failed-item-time-value-${id}` inside `failed-item-time-${id}`, the same clock as every other panel, and `null` reads `記録なし`. The monitor reads the root's zone, so it renders inside `ConsoleUiRoot`.
|
|
229
|
+
`sync_status` breakdown with individual and bulk retry controls. Required props: `counts`, `totalFiles`; `failedItems` is `readonly FailedSyncEntry[]`, readable rows `{ kind: "readable", item: FailedSyncItem }` and restricted marks (`RestrictedFailureMark`, V11) drawn as one row with `隔離 (RESTRICTED) の文書`, the sync status and the failure time only, without a retry (`failed-item-restricted-${position}`); `listRefusal?: string` replaces the list with the refusal sentence in a warning `Notice` (`failed-items-refused`), with no row, no empty sentence, no row retry and no count in the list's heading, while the status tiles stay; each failed item's `failed_at` (an ISO 8601 instant with an explicit offset) is written in the root's zone under `failed-item-time-value-${id}` inside `failed-item-time-${id}`, the same clock as every other panel, and `null` reads `記録なし`. The monitor reads the root's zone, so it renders inside `ConsoleUiRoot`.
|
|
230
|
+
|
|
231
|
+
### 12. `DocumentChangeFeed`
|
|
232
|
+
|
|
233
|
+
The recent document changes (S1 §3.3.5-4), a full-width `Card` `直近の文書の変化` holding a `DataTable` read a keyset page at a time (`DOCUMENT_CHANGE_FEED_PAGE_SIZE` rows, `LoadMore`). Props: `host: { loadChanges(cursor, signal) }` and the optional `onOpen(metadataId, stateId)` (without it there is no `開く` column).
|
|
234
|
+
|
|
235
|
+
- A row (`change-feed-row`, `data-row-kind`) shows the arriving state's time, the document (name and URI), the revisions (`r4 → r5` or `r4 (同じ版)`), the cause, the operator, the changed facets as badges and `開く`, which selects the arriving state and names it to assistive technology (`開く doc-9.pdf r4 → r5 の変化`, the file name and the revisions in `VisuallyHidden`). No storage figure is shown (V5).
|
|
236
|
+
- A restricted mark (V11) is one row of the same table with its time and `隔離 (RESTRICTED) の文書` only; its React key is its position (`changeFeedRowKey`).
|
|
237
|
+
- A failed re-read or later page keeps the rows shown and says why (a failed re-read in `change-feed-refresh-failed`, announced again when the same failure repeats); a later page's 403 re-reads the first page, and a 403 there withdraws the list (`文書の履歴を閲覧する権限がありません。<原因>。`).
|
|
237
238
|
|
|
238
239
|
## Colour Contrast
|
|
239
240
|
|
|
@@ -242,7 +243,7 @@ The ratio is `@aiquants/console-ui`'s `contrastRatio`, the repository's only con
|
|
|
242
243
|
The package keeps no luminance computation or AA bar of its own, the colour spec included.
|
|
243
244
|
|
|
244
245
|
- **Flow viewer:** every node's label (`flow-node-label-${key}`), phase (`flow-node-phase-${key}`), and gate line (`flow-gate-badge-${key}`) on the node's fill, unselected (`white`) and selected (`indigo-50`); the dependency edges and their arrowhead on the figure's background (`slate-500` on `rgb(248 250 252)`, 4.56:1).
|
|
245
|
-
- **
|
|
246
|
+
- **History screens:** `DocumentHistoryInspector` and `DocumentChangeFeed` paint only through `@aiquants/console-ui`'s roles; their specs run `scanColourRoles` over the rendered panel, so every text sits on a pair `CONTRAST_PAIRS` lists (the diff pieces are `fg` on `danger-tint` / `success-tint`).
|
|
246
247
|
- **Status badges and counts:** the ontology card's HITL count (`ontology-unresolved-count`, `amber-700` on its tile) and the strategy board's stage switches, open (`emerald-700` on `emerald-50`) and closed (`slate-600` on `slate-100`).
|
|
247
248
|
- **Probe-state marks:** the health monitor's overall badge, each probe's badge, and each probe's error text (`service-error-${service}`), with every probe connected and with some probes failed (a connected one among them carrying an error text), each at rest and while re-probing, with and without the refresh button; the Qdrant card's health badge (`qdrant-health-badge`), connected and failed, at rest and while refreshing.
|
|
248
249
|
Each mark's texts pass `@aiquants/console-ui/testing`'s role scan `scanColourRoles` as exactly its own pair, its state's `success-fg` on `success-tint` or `danger-fg` on `danger-tint`, and the error text's `danger-fg` on `danger-tint` in either state (pairs `@aiquants/console-ui`'s `CONTRAST_PAIRS` lists, so each meets the text bar).
|
|
@@ -258,10 +259,13 @@ Colour pairs outside this list are not measured.
|
|
|
258
259
|
|
|
259
260
|
Exported for hosts that draw their own run tables, so a run reads the same everywhere.
|
|
260
261
|
|
|
262
|
+
- `ledgerStepLabel(key)` — the name of any step a run records: a flow step's `PIPELINE_STEPS` label, the terminal step `record_document_state` as `RUN_TERMINAL_STEP_LABEL` (`文書の状態の記録`). The flow diagram and the step metrics never show the terminal step (`PIPELINE_FLOWS` does not name it); only `PipelineRunSteps` does. `RUN_TERMINAL_STEP_OUTCOME_LABELS` words a succeeded terminal step's outcome.
|
|
263
|
+
- `documentHistoryPresentation` — the history's words for hosts: `FACET_LABELS`, `DOCUMENT_STATE_CAUSE_LABELS`, `facetCountsText(counts)`, `revisionLabel`, and the restricted marks' fixed texts `RESTRICTED_DOCUMENT_TEXT` (`隔離 (RESTRICTED) の文書`), `RESTRICTED_STATE_TEXT` and `RESTRICTED_RUN_TEXT` (`隔離されていた期間の実行`), so a host table that draws a mark row writes the same text.
|
|
264
|
+
|
|
261
265
|
- `PIPELINE_RUN_STATUS_PRESENTATION` — `{ label, className }` for `RUNNING` / `SUCCEEDED` / `FAILED` and `FAILED_RETRY_SCHEDULED` (`失敗 (再試行予定)`); pick the key with `pipelineRunPresentationKey({ status, retryScheduled })` (a retry marker on a non-failed run is ignored).
|
|
262
266
|
- `PIPELINE_STEP_STATUS_PRESENTATION` — `{ label, className, bar }` per step status.
|
|
263
267
|
- `PIPELINE_TRIGGER_LABELS` — operator-facing names of the triggers `PipelineTriggerSchema` allows: `ingest` (取り込み), `retry` (再試行), `resync` (再同期).
|
|
264
|
-
- `skipReasonLabel(text)` — reads the text through `parseSkipReason`: `gate_closed:<flag>` → `<flag> が無効`, `no_children` → `子アセットなし`, `source_skipped:<step>` → `<step label> が実行されなかった`, `nothing_to_read:<token>` → `NOTHING_TO_READ_LABELS[token]` (`no_pages` → `読む対象なし (ページなし)`, `no_pages_rendered` → `読む対象なし (描画できたページなし)`)
|
|
268
|
+
- `skipReasonLabel(text)` — reads the text through `parseSkipReason`: `gate_closed:<flag>` → `<flag> が無効`, `no_children` → `子アセットなし`, `source_skipped:<step>` → `<step label> が実行されなかった`, `nothing_to_read:<token>` → `NOTHING_TO_READ_LABELS[token]` (`no_pages` → `読む対象なし (ページなし)`, `no_pages_rendered` → `読む対象なし (描画できたページなし)`), and the terminal step's `superseded` → `実行の版が現在の版でなくなった`, `recording_disabled` → `記録の切替が無効`, `recording_misconfigured` → `記録の切替の設定の誤り`; text outside the grammar is returned verbatim. A fan-out step's per-child records — including a child skipped as `gate_closed:ENABLE_OCR` or `nothing_to_read:no_pages` — are grouped under the step in the step timeline, each with its own reason.
|
|
265
269
|
- `RecordedInstant({ iso, testId })` — a recorded instant in the root's zone, to the second (`2026/09/24 01:02:03`), in a measured figure slot under `testId` (`data-measured="true"`, tabular figures, never broken between the date and the time); `iso: null` is the root's unmeasured label (`data-measured="false"`). A refused instant throws `ZonedTimeError` (see **Time zone is the root's**). Render it inside the Japanese console root, like every component that reads the root.
|
|
266
270
|
- `FinishedInstant({ iso, testId })` — a finish time that may still be open: `RecordedInstant` for a finished run or step, or the word `未完了` (`UNFINISHED_LABEL`) outside any slot while `iso` is `null`. It checks the root in both cases.
|
|
267
271
|
- The package publishes no function that turns an instant into text: a host table draws its instants with these two, so they share the root's clock, the slot and its hook with every panel of the package.
|