@zephytiju/prism-quick-files
v0.1.0
Published
Platform Prism quick-files micro-UI (component id "quick-files"): the Dossier Quick File Insert side panel — QUICK FILE INSERT title header, search box, FILES ARE THE SHORTCUT / RECENT-COMMON FILES sections, per-prototype file rows (world view / dossier d
Readme
PrismQuickFilesMicroUI
Platform Prism quick-files micro-UI. Component id: quick-files.
Published to npm as @zephytiju/prism-quick-files.
The Dossier Quick File Insert side panel — an axiom component, sibling of the editor viewport
(not a sub-component of it): the QUICK FILE INSERT title header (title, subtitle, trailing meta
are component configuration keys — Prism has no composed title node), the bordered search box, the
FILES ARE THE SHORTCUT / RECENT / COMMON FILES section labels, per-prototype file rows (world
view / dossier doc / board / folder icons on semantic kind tokens, name + mono meta line, +
insert action) and the DROP-IN FILE MODEL info card. Rows are indexed through the IFileEntry
list query via the generated interface client over the host Lattice transport. Composed
applications (for example Guanlan) consume it as-is; the component is platform-owned.
D6 — the drag/insert state contract (the heart of this component)
The panel never references any receiving component. On insert (+) or drag-start of a file
row it publishes ONLY a bounded drag/insert state object carrying file metadata — never a
component reference — on the quick-files.drag-state Prism channel; ESC and drag-end clear it
(drop or cancel alike). Receivers interpret the payload themselves:
| Receiver | Interpretation |
| --- | --- |
| world view | Geovision Embed block restoring the IGeoView snapshot addressed by snapshotRef (the drop-in file model) |
| evidence pack | Evidence Table block |
| dossier | new paragraph carrying an inline reference link (per D10, links are inline in paragraph text — never standalone blocks) |
| unknown kind | paragraph with a generic inline file link |
Because only state flows between them, the panel and the editor can be composed into different side-panel/viewport layouts without either knowing about the other (design decision D6 — this component is the reference case).
Configuration keys
| Prop | Meaning |
| --- | --- |
| locale | UI locale for the component-fixed strings: "en" \| "zh-CN" (default "en") — see i18n |
| title | Panel title header (defaults to the locale's panelTitle, "QUICK FILE INSERT") |
| subtitle | Mono subtitle under the title (defaults to the locale's panelSubtitle) |
| trailingMeta | Trailing header meta (defaults to the locale's {count} FILES pattern with the live row count) |
| variant | "rail" (default full side panel) or "popover" (compact, anchored to a block's + control; no info card) |
| categoryFilters | Restrict rendered rows to these IFileEntry kinds (world views, dossiers, boards, evidence, folders) |
| recencyWindowMs | Keep rows whose lastOpenedAt lies within this window; entries without a timestamp are kept |
| kindTokens | File kind → semantic color token overrides (defaults: world view → signal, doc/evidence → ok, board → warn, folder → accent) |
Channel contract
| Direction | Kind | Id | Payload |
| --- | --- | --- | --- |
| publishes | state | quick-files.drag-state | { entryId, kind, name, mime, snapshotRef, sourcePanel: "quick-files" } \| null — published on + insert and on drag-start, cleared (null) on drag-end and ESC |
Channel ids are string literals at every call-site so the build-time channel-graph scanner can
derive the graph. The publication is setter-only (usePrismStateSetter): the panel never
re-renders from its own or anyone's publications. The panel also mirrors the payload into the
HTML5 data transfer (application/x-prism-quick-file) on real drags so non-Prism drop targets
work too. The panel consumes no channels.
Lattice binding
Rows come from the interface-declared interfaces/IFileEntry/list query, invoked through the
generated createFileEntryClient from @zephytiju/lattice-common-interfaces over
useLatticeTransport — no URLs, clients, or credentials in component code. The generated bundle's
bounded summary (id/name/kind/sizeBytes) is a structural subset of the panel's row
projection (mime, snapshotRef, mono meta line, pin, recency); richer hosts return the superset
on the same route. Missing mime/snapshotRef fields fall back to bounded defaults (kind mime
map; the entry id as the snapshot address). The fetch lifecycle (busy skeletons, error alert with
Retry, guided empty state) is local state only — the panel publishes nothing about its own
loading.
Audit rule
File browsing and insertion are routine actions and are NOT audit-worthy. This component emits NO audit event and NO Prism event of any kind — the single outbound signal is the bounded drag/insert state above.
Internationalization (i18n)
The component ships en and zh-CN locale bundles — src/locales/en.json / src/locales/zh-CN.json —
and every component-fixed UI string is resolved from them (the panel title default, subtitle
default, {count} FILES pattern, search placeholder and label, section labels, the DROP-IN FILE
MODEL card, empty/no-match copy, error title, Retry). The component renders no hardcoded copy.
{
"quick-files": {
"panelTitle": "QUICK FILE INSERT",
"panelSubtitle": "WORLD VIEWS, DOSSIERS, BOARDS, EVIDENCE",
"trailingMetaCount": "{count} FILES",
"searchPlaceholder": "Search recent files and world views",
"sectionHint": "FILES ARE THE SHORTCUT",
"sectionRecent": "RECENT / COMMON FILES",
"infoCardTitle": "DROP-IN FILE MODEL",
"infoCardBody": "World views are versioned spatial metadata files. …",
"emptyTitle": "No files yet",
"retry": "Retry"
}
}locale?: "en" | "zh-CN"prop (default"en") selects the string table per instance.trailingMetaCountuses a simple{count}placeholder interpolated with the visible row count (plain substitution, no regexes —formatMessageis exported from the package entry).- Composition-authored strings are localized by the composer; component-fixed strings live in the
locale JSONs. An explicit
title/subtitle/trailingMetaoverride is configuration-authored: a host with a localized title passes its own string per locale. - Locale bundles are namespaced under the component id (
"quick-files") so a composer can deep-merge every component's bundle into ONE UI language bundle without collisions:
import { locales as quickFilesLocales } from "@zephytiju/prism-quick-files";
// quickFilesLocales["zh-CN"] -> { "quick-files": { … } }
const uiBundle = deepMerge(hostStrings, quickFilesLocales["zh-CN"]);The parsed bundles are exported from the package entry (locales, en, zhCN,
stringsForLocale), and the raw JSONs are also served by the ./locales/* exports subpath
(e.g. @zephytiju/prism-quick-files/locales/zh-CN.json); files ships both dist and locales.
Theme
No palette is hardcoded. Every color resolves to SEMANTIC theme tokens (ok, accent,
threat, warn, signal, card, card-dark, border, line, text, muted, input,
deep, panel) consumed as CSS variables, plus --mantine-font-family-monospace for the mono
typography — the palette is supplied entirely by the host's MantineProvider. The prototype's
kind hues map onto tokens: purple world views → signal, mint docs → ok, amber boards → warn,
blue folders (and the + insert action, search border, subtitle) → accent. The local demo ships
TWO themes, both defined in src/demo.tsx: geovisionTheme (dark), mapping each semantic token
onto the exact :root variables of the authoritative v9 prototype, and the contrasting
latticeLightTheme (light) mapping the SAME semantic token keys onto a different palette — the
component is skinned purely through the surrounding MantineProvider.
Source layout
src/ is strictly two parts:
- Component source (what the package compiles):
QuickFiles.tsx(the panel),FileRow.tsx(item-level row owning thequick-files.drag-statepublication),icons.tsx(the prototype's.ficglyph set + kind token defaults),types.ts(bounded channel/route types),index.ts(public entry), andsrc/locales/(en.json,zh-CN.json,index.ts— the i18n string bundles, their resolver, and the{count}interpolation helper). - Demo: exactly ONE file,
src/demo.tsx— the two host themes (GeoVision v9 + Lattice Light), the mock host executor answeringinterfaces/IFileEntry/listwith the nine prototype rows, the demo page rendering THREEQuickFileshosts (dark rail + light rail + light popover with category filters) behind a global EN | 中文 switcher (plus per-instance switches), and the D6 demonstration: a channel monitor reading the published drag state plus a mock receiver (drop target) that consumes the SAME channel and interprets each kind its own way.
The npm package ships dist (compiled component + type declarations + locale JSONs) and the
top-level locales/ directory (the raw JSON bundles, served by the ./locales/* exports
subpath); no demo code is published. scripts/copy-locales.mjs copies the JSON bundles into
both locations during npm run build.
Local development
npm install
npm run typecheck
npm test
npm run dev
npm run shot-demonpm install pulls the platform peers (@zephytiju/prism-react,
@zephytiju/lattice-common-interfaces) from the npm registry, along with the host-side peer
dependencies (react, react-dom, @mantine/core). When consuming the published package,
install it directly (npm install @zephytiju/prism-quick-files) and provide those peer
dependencies in the host application.
The demo (npm run dev, entry src/demo.tsx) plays the host: it installs a mock action executor
answering the interface-declared IFileEntry list route with the nine sample rows of the v9
prototype, then renders three themed QuickFiles hosts — GeoVision v9 dark (rail), Lattice
Light (rail), and a Lattice Light popover host restricted to world views + dossiers. A global
EN | 中文 segmented control switches the locale prop of every instance at once, and each
instance carries its own per-instance control so the hosts can render DIFFERING locales
simultaneously. Below the hosts, the D6 demonstration: the quick-files.drag-state monitor
shows the last published {entryId, kind, name, mime, snapshotRef, sourcePanel} payload, and the
mock receiver materializes typed blocks from each payload (world view → Geovision Embed restoring
the IGeoView snapshot; evidence pack → Evidence Table; dossier → paragraph with an inline link;
unknown → generic inline file link per D10). Host affordances demonstrate clearing the state
(same as ESC) and the error + Retry branch. npm run shot-demo boots the vite dev server, drives
the demo in headless Chrome (dark host en, light host zh-CN), inserts a world view and a
dossier row, and captures both states to /tmp/guanlan-review/demo-quick-files-themes.png and
/tmp/guanlan-review/demo-quick-files-insert.png.
Design record
https://qcnwge0wy4s0.feishu.cn/wiki/B87YwS2PriaGvlkAcOtctKm8nsd — decisions D4 (interface
binding), D5 (component tiers — this is an axiom component), D6 (cross-component state passing —
this component is the reference case) and D10 (inline links, embedded block chrome).
Component doc: https://qcnwge0wy4s0.feishu.cn/wiki/KhFPwrEOIiALwpkvofrcc9KYnEe. Visual reference:
the authoritative v9 HTML prototype (dossier-editor-standalone-v9.html, .qins / .qrow /
.fic), attached to the design record.
