@zephytiju/prism-quick-files 0.1.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 +192 -0
- package/dist/DropInModelCard.d.ts +9 -0
- package/dist/DropInModelCard.d.ts.map +1 -0
- package/dist/DropInModelCard.js +7 -0
- package/dist/FileList.d.ts +25 -0
- package/dist/FileList.d.ts.map +1 -0
- package/dist/FileList.js +23 -0
- package/dist/FileRow.d.ts +31 -0
- package/dist/FileRow.d.ts.map +1 -0
- package/dist/FileRow.js +54 -0
- package/dist/PanelHeader.d.ts +17 -0
- package/dist/PanelHeader.d.ts.map +1 -0
- package/dist/PanelHeader.js +11 -0
- package/dist/QuickFiles.d.ts +57 -0
- package/dist/QuickFiles.d.ts.map +1 -0
- package/dist/QuickFiles.js +75 -0
- package/dist/SearchBox.d.ts +13 -0
- package/dist/SearchBox.d.ts.map +1 -0
- package/dist/SearchBox.js +23 -0
- package/dist/SectionLabels.d.ts +9 -0
- package/dist/SectionLabels.d.ts.map +1 -0
- package/dist/SectionLabels.js +7 -0
- package/dist/clients.d.ts +22 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +13 -0
- package/dist/dragState.d.ts +20 -0
- package/dist/dragState.d.ts.map +1 -0
- package/dist/dragState.js +56 -0
- package/dist/icons.d.ts +35 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +58 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/locales/en.json +20 -0
- package/dist/locales/index.d.ts +72 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/index.js +27 -0
- package/dist/locales/zh-CN.json +20 -0
- package/dist/tokens.d.ts +19 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +18 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/useQuickFilesEntries.d.ts +16 -0
- package/dist/useQuickFilesEntries.d.ts.map +1 -0
- package/dist/useQuickFilesEntries.js +40 -0
- package/dist/visibleRows.d.ts +12 -0
- package/dist/visibleRows.d.ts.map +1 -0
- package/dist/visibleRows.js +57 -0
- package/locales/en.json +20 -0
- package/locales/zh-CN.json +20 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# PrismQuickFilesMicroUI
|
|
2
|
+
|
|
3
|
+
Platform Prism quick-files micro-UI. Component id: `quick-files`.
|
|
4
|
+
Published to npm as [`@zephytiju/prism-quick-files`](https://www.npmjs.com/package/@zephytiju/prism-quick-files).
|
|
5
|
+
|
|
6
|
+
The Dossier **Quick File Insert** side panel — an axiom component, sibling of the editor viewport
|
|
7
|
+
(not a sub-component of it): the `QUICK FILE INSERT` title header (title, subtitle, trailing meta
|
|
8
|
+
are component configuration keys — Prism has no composed title node), the bordered search box, the
|
|
9
|
+
`FILES ARE THE SHORTCUT` / `RECENT / COMMON FILES` section labels, per-prototype file rows (world
|
|
10
|
+
view / dossier doc / board / folder icons on semantic kind tokens, name + mono meta line, `+`
|
|
11
|
+
insert action) and the `DROP-IN FILE MODEL` info card. Rows are indexed through the `IFileEntry`
|
|
12
|
+
list query via the generated interface client over the host Lattice transport. Composed
|
|
13
|
+
applications (for example Guanlan) consume it as-is; the component is platform-owned.
|
|
14
|
+
|
|
15
|
+
## D6 — the drag/insert state contract (the heart of this component)
|
|
16
|
+
|
|
17
|
+
The panel **never references any receiving component**. On insert (`+`) or drag-start of a file
|
|
18
|
+
row it publishes ONLY a bounded drag/insert state object carrying file metadata — never a
|
|
19
|
+
component reference — on the `quick-files.drag-state` Prism channel; **ESC and drag-end clear it**
|
|
20
|
+
(drop or cancel alike). Receivers interpret the payload themselves:
|
|
21
|
+
|
|
22
|
+
| Receiver | Interpretation |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| world view | Geovision Embed block restoring the `IGeoView` snapshot addressed by `snapshotRef` (the drop-in file model) |
|
|
25
|
+
| evidence pack | Evidence Table block |
|
|
26
|
+
| dossier | new paragraph carrying an inline reference link (per D10, links are inline in paragraph text — never standalone blocks) |
|
|
27
|
+
| unknown kind | paragraph with a generic inline file link |
|
|
28
|
+
|
|
29
|
+
Because only state flows between them, the panel and the editor can be composed into different
|
|
30
|
+
side-panel/viewport layouts without either knowing about the other (design decision D6 — this
|
|
31
|
+
component is the reference case).
|
|
32
|
+
|
|
33
|
+
## Configuration keys
|
|
34
|
+
|
|
35
|
+
| Prop | Meaning |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `locale` | UI locale for the component-fixed strings: `"en" \| "zh-CN"` (default `"en"`) — see [i18n](#internationalization-i18n) |
|
|
38
|
+
| `title` | Panel title header (defaults to the locale's `panelTitle`, `"QUICK FILE INSERT"`) |
|
|
39
|
+
| `subtitle` | Mono subtitle under the title (defaults to the locale's `panelSubtitle`) |
|
|
40
|
+
| `trailingMeta` | Trailing header meta (defaults to the locale's `{count} FILES` pattern with the live row count) |
|
|
41
|
+
| `variant` | `"rail"` (default full side panel) or `"popover"` (compact, anchored to a block's `+` control; no info card) |
|
|
42
|
+
| `categoryFilters` | Restrict rendered rows to these `IFileEntry` kinds (world views, dossiers, boards, evidence, folders) |
|
|
43
|
+
| `recencyWindowMs` | Keep rows whose `lastOpenedAt` lies within this window; entries without a timestamp are kept |
|
|
44
|
+
| `kindTokens` | File kind → semantic color token overrides (defaults: world view → `signal`, doc/evidence → `ok`, board → `warn`, folder → `accent`) |
|
|
45
|
+
|
|
46
|
+
## Channel contract
|
|
47
|
+
|
|
48
|
+
| Direction | Kind | Id | Payload |
|
|
49
|
+
| --- | --- | --- | --- |
|
|
50
|
+
| 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 |
|
|
51
|
+
|
|
52
|
+
Channel ids are string literals at every call-site so the build-time channel-graph scanner can
|
|
53
|
+
derive the graph. The publication is setter-only (`usePrismStateSetter`): the panel never
|
|
54
|
+
re-renders from its own or anyone's publications. The panel also mirrors the payload into the
|
|
55
|
+
HTML5 data transfer (`application/x-prism-quick-file`) on real drags so non-Prism drop targets
|
|
56
|
+
work too. The panel consumes no channels.
|
|
57
|
+
|
|
58
|
+
## Lattice binding
|
|
59
|
+
|
|
60
|
+
Rows come from the interface-declared `interfaces/IFileEntry/list` query, invoked through the
|
|
61
|
+
generated `createFileEntryClient` from `@zephytiju/lattice-common-interfaces` over
|
|
62
|
+
`useLatticeTransport` — no URLs, clients, or credentials in component code. The generated bundle's
|
|
63
|
+
bounded summary (`id`/`name`/`kind`/`sizeBytes`) is a structural subset of the panel's row
|
|
64
|
+
projection (`mime`, `snapshotRef`, mono meta line, pin, recency); richer hosts return the superset
|
|
65
|
+
on the same route. Missing `mime`/`snapshotRef` fields fall back to bounded defaults (kind mime
|
|
66
|
+
map; the entry id as the snapshot address). The fetch lifecycle (busy skeletons, error alert with
|
|
67
|
+
Retry, guided empty state) is local state only — the panel publishes nothing about its own
|
|
68
|
+
loading.
|
|
69
|
+
|
|
70
|
+
## Audit rule
|
|
71
|
+
|
|
72
|
+
File browsing and insertion are routine actions and are NOT audit-worthy. This component emits NO
|
|
73
|
+
audit event and NO Prism event of any kind — the single outbound signal is the bounded drag/insert
|
|
74
|
+
state above.
|
|
75
|
+
|
|
76
|
+
## Internationalization (i18n)
|
|
77
|
+
|
|
78
|
+
The component ships `en` and `zh-CN` locale bundles — `src/locales/en.json` / `src/locales/zh-CN.json` —
|
|
79
|
+
and every component-fixed UI string is resolved from them (the panel title default, subtitle
|
|
80
|
+
default, `{count} FILES` pattern, search placeholder and label, section labels, the DROP-IN FILE
|
|
81
|
+
MODEL card, empty/no-match copy, error title, Retry). The component renders no hardcoded copy.
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"quick-files": {
|
|
86
|
+
"panelTitle": "QUICK FILE INSERT",
|
|
87
|
+
"panelSubtitle": "WORLD VIEWS, DOSSIERS, BOARDS, EVIDENCE",
|
|
88
|
+
"trailingMetaCount": "{count} FILES",
|
|
89
|
+
"searchPlaceholder": "Search recent files and world views",
|
|
90
|
+
"sectionHint": "FILES ARE THE SHORTCUT",
|
|
91
|
+
"sectionRecent": "RECENT / COMMON FILES",
|
|
92
|
+
"infoCardTitle": "DROP-IN FILE MODEL",
|
|
93
|
+
"infoCardBody": "World views are versioned spatial metadata files. …",
|
|
94
|
+
"emptyTitle": "No files yet",
|
|
95
|
+
"retry": "Retry"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `locale?: "en" | "zh-CN"` prop (default `"en"`) selects the string table per instance.
|
|
101
|
+
- `trailingMetaCount` uses a simple `{count}` placeholder interpolated with the visible row count
|
|
102
|
+
(plain substitution, no regexes — `formatMessage` is exported from the package entry).
|
|
103
|
+
- **Composition-authored strings are localized by the composer; component-fixed strings live in the
|
|
104
|
+
locale JSONs.** An explicit `title` / `subtitle` / `trailingMeta` override is
|
|
105
|
+
configuration-authored: a host with a localized title passes its own string per locale.
|
|
106
|
+
- Locale bundles are namespaced under the component id (`"quick-files"`) so a composer can
|
|
107
|
+
deep-merge every component's bundle into ONE UI language bundle without collisions:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { locales as quickFilesLocales } from "@zephytiju/prism-quick-files";
|
|
111
|
+
// quickFilesLocales["zh-CN"] -> { "quick-files": { … } }
|
|
112
|
+
const uiBundle = deepMerge(hostStrings, quickFilesLocales["zh-CN"]);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The parsed bundles are exported from the package entry (`locales`, `en`, `zhCN`,
|
|
116
|
+
`stringsForLocale`), and the raw JSONs are also served by the `./locales/*` exports subpath
|
|
117
|
+
(e.g. `@zephytiju/prism-quick-files/locales/zh-CN.json`); `files` ships both `dist` and `locales`.
|
|
118
|
+
|
|
119
|
+
## Theme
|
|
120
|
+
|
|
121
|
+
No palette is hardcoded. Every color resolves to SEMANTIC theme tokens (`ok`, `accent`,
|
|
122
|
+
`threat`, `warn`, `signal`, `card`, `card-dark`, `border`, `line`, `text`, `muted`, `input`,
|
|
123
|
+
`deep`, `panel`) consumed as CSS variables, plus `--mantine-font-family-monospace` for the mono
|
|
124
|
+
typography — the palette is supplied entirely by the host's `MantineProvider`. The prototype's
|
|
125
|
+
kind hues map onto tokens: purple world views → `signal`, mint docs → `ok`, amber boards → `warn`,
|
|
126
|
+
blue folders (and the `+` insert action, search border, subtitle) → `accent`. The local demo ships
|
|
127
|
+
TWO themes, both defined in `src/demo.tsx`: `geovisionTheme` (dark), mapping each semantic token
|
|
128
|
+
onto the exact `:root` variables of the authoritative v9 prototype, and the contrasting
|
|
129
|
+
`latticeLightTheme` (light) mapping the SAME semantic token keys onto a different palette — the
|
|
130
|
+
component is skinned purely through the surrounding `MantineProvider`.
|
|
131
|
+
|
|
132
|
+
## Source layout
|
|
133
|
+
|
|
134
|
+
`src/` is strictly two parts:
|
|
135
|
+
|
|
136
|
+
- Component source (what the package compiles): `QuickFiles.tsx` (the panel),
|
|
137
|
+
`FileRow.tsx` (item-level row owning the `quick-files.drag-state` publication),
|
|
138
|
+
`icons.tsx` (the prototype's `.fic` glyph set + kind token defaults), `types.ts` (bounded
|
|
139
|
+
channel/route types), `index.ts` (public entry), and `src/locales/` (`en.json`, `zh-CN.json`,
|
|
140
|
+
`index.ts` — the i18n string bundles, their resolver, and the `{count}` interpolation helper).
|
|
141
|
+
- Demo: exactly ONE file, `src/demo.tsx` — the two host themes (GeoVision v9 + Lattice Light),
|
|
142
|
+
the mock host executor answering `interfaces/IFileEntry/list` with the nine prototype rows,
|
|
143
|
+
the demo page rendering THREE `QuickFiles` hosts (dark rail + light rail + light popover with
|
|
144
|
+
category filters) behind a global EN | 中文 switcher (plus per-instance switches), and the D6
|
|
145
|
+
demonstration: a channel monitor reading the published drag state plus a mock receiver (drop
|
|
146
|
+
target) that consumes the SAME channel and interprets each kind its own way.
|
|
147
|
+
|
|
148
|
+
The npm package ships `dist` (compiled component + type declarations + locale JSONs) and the
|
|
149
|
+
top-level `locales/` directory (the raw JSON bundles, served by the `./locales/*` exports
|
|
150
|
+
subpath); no demo code is published. `scripts/copy-locales.mjs` copies the JSON bundles into
|
|
151
|
+
both locations during `npm run build`.
|
|
152
|
+
|
|
153
|
+
## Local development
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
npm install
|
|
157
|
+
npm run typecheck
|
|
158
|
+
npm test
|
|
159
|
+
npm run dev
|
|
160
|
+
npm run shot-demo
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`npm install` pulls the platform peers (`@zephytiju/prism-react`,
|
|
164
|
+
`@zephytiju/lattice-common-interfaces`) from the npm registry, along with the host-side peer
|
|
165
|
+
dependencies (`react`, `react-dom`, `@mantine/core`). When consuming the published package,
|
|
166
|
+
install it directly (`npm install @zephytiju/prism-quick-files`) and provide those peer
|
|
167
|
+
dependencies in the host application.
|
|
168
|
+
|
|
169
|
+
The demo (`npm run dev`, entry `src/demo.tsx`) plays the host: it installs a mock action executor
|
|
170
|
+
answering the interface-declared `IFileEntry` list route with the nine sample rows of the v9
|
|
171
|
+
prototype, then renders three themed `QuickFiles` hosts — GeoVision v9 dark (rail), Lattice
|
|
172
|
+
Light (rail), and a Lattice Light popover host restricted to world views + dossiers. A global
|
|
173
|
+
EN | 中文 segmented control switches the `locale` prop of every instance at once, and each
|
|
174
|
+
instance carries its own per-instance control so the hosts can render DIFFERING locales
|
|
175
|
+
simultaneously. Below the hosts, the D6 demonstration: the `quick-files.drag-state` monitor
|
|
176
|
+
shows the last published `{entryId, kind, name, mime, snapshotRef, sourcePanel}` payload, and the
|
|
177
|
+
mock receiver materializes typed blocks from each payload (world view → Geovision Embed restoring
|
|
178
|
+
the IGeoView snapshot; evidence pack → Evidence Table; dossier → paragraph with an inline link;
|
|
179
|
+
unknown → generic inline file link per D10). Host affordances demonstrate clearing the state
|
|
180
|
+
(same as ESC) and the error + Retry branch. `npm run shot-demo` boots the vite dev server, drives
|
|
181
|
+
the demo in headless Chrome (dark host `en`, light host `zh-CN`), inserts a world view and a
|
|
182
|
+
dossier row, and captures both states to `/tmp/guanlan-review/demo-quick-files-themes.png` and
|
|
183
|
+
`/tmp/guanlan-review/demo-quick-files-insert.png`.
|
|
184
|
+
|
|
185
|
+
## Design record
|
|
186
|
+
|
|
187
|
+
https://qcnwge0wy4s0.feishu.cn/wiki/B87YwS2PriaGvlkAcOtctKm8nsd — decisions D4 (interface
|
|
188
|
+
binding), D5 (component tiers — this is an axiom component), D6 (cross-component state passing —
|
|
189
|
+
this component is the reference case) and D10 (inline links, embedded block chrome).
|
|
190
|
+
Component doc: https://qcnwge0wy4s0.feishu.cn/wiki/KhFPwrEOIiALwpkvofrcc9KYnEe. Visual reference:
|
|
191
|
+
the authoritative v9 HTML prototype (`dossier-editor-standalone-v9.html`, `.qins` / `.qrow` /
|
|
192
|
+
`.fic`), attached to the design record.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface QuickFilesDropInModelCardProps {
|
|
2
|
+
/** Locale-resolved card title (DROP-IN FILE MODEL). */
|
|
3
|
+
readonly title: string;
|
|
4
|
+
/** Locale-resolved card body copy. */
|
|
5
|
+
readonly body: string;
|
|
6
|
+
}
|
|
7
|
+
/** The DROP-IN FILE MODEL info card of the rail variant (v9 bottom card). */
|
|
8
|
+
export declare function QuickFilesDropInModelCard({ title, body }: QuickFilesDropInModelCardProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=DropInModelCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropInModelCard.d.ts","sourceRoot":"","sources":["../src/DropInModelCard.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,8BAA8B;IAC7C,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,8BAA8B,+BAyBxF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "@mantine/core";
|
|
3
|
+
import { ACCENT, BORDER, CARD_DARK_BG, MONO, MUTED } from "./tokens.js";
|
|
4
|
+
/** The DROP-IN FILE MODEL info card of the rail variant (v9 bottom card). */
|
|
5
|
+
export function QuickFilesDropInModelCard({ title, body }) {
|
|
6
|
+
return (_jsxs(Box, { p: 10, style: { background: CARD_DARK_BG, border: `1px solid ${BORDER}`, borderRadius: 5 }, "data-testid": "quick-files-info-card", children: [_jsx(Text, { ff: MONO, fz: 9, fw: 500, style: { color: ACCENT, letterSpacing: "0.04em", marginBottom: 6 }, children: title }), _jsx(Text, { ff: MONO, fz: 9, style: { color: MUTED, lineHeight: 1.6 }, "data-testid": "quick-files-info-body", children: body })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { QuickFilesStrings } from "./locales/index.js";
|
|
2
|
+
import type { QuickFileEntry, QuickFilesStatus } from "./types.js";
|
|
3
|
+
export interface QuickFilesFileListProps {
|
|
4
|
+
/** Lifecycle of the most recent list fetch (busy / error / ready). */
|
|
5
|
+
readonly status: QuickFilesStatus;
|
|
6
|
+
/** The raw fetched entries (null while the first query is in flight). */
|
|
7
|
+
readonly entries: readonly QuickFileEntry[] | null;
|
|
8
|
+
/** Filtered + ordered rows to render (see selectVisibleRows). */
|
|
9
|
+
readonly rows: readonly QuickFileEntry[];
|
|
10
|
+
/** Locale string table for the region's fixed copy. */
|
|
11
|
+
readonly strings: QuickFilesStrings;
|
|
12
|
+
/** File kind → semantic color token overrides (defaults are curated). */
|
|
13
|
+
readonly kindTokens: Readonly<Record<string, string>>;
|
|
14
|
+
/** Re-runs the list query (the error state's Retry action). */
|
|
15
|
+
readonly onRetry: () => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The panel's rows region: busy skeletons while the first list query is in
|
|
19
|
+
* flight, an error alert with Retry, a guided empty state, the no-matches
|
|
20
|
+
* notice, and the file rows themselves (kind glyph + semantic token, name,
|
|
21
|
+
* mono meta line, + insert action — each row publishing the bounded drag
|
|
22
|
+
* state itself, per FileRow).
|
|
23
|
+
*/
|
|
24
|
+
export declare function QuickFilesFileList({ status, entries, rows, strings, kindTokens, onRetry }: QuickFilesFileListProps): import("react").JSX.Element;
|
|
25
|
+
//# sourceMappingURL=FileList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileList.d.ts","sourceRoot":"","sources":["../src/FileList.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAkB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInF,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAAC;IACnD,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IACzC,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,uBAAuB,+BA+DlH"}
|
package/dist/FileList.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Button, Card, Group, Skeleton, Stack, Text } from "@mantine/core";
|
|
3
|
+
import { FileRow } from "./FileRow.js";
|
|
4
|
+
import { DEFAULT_KIND_GLYPHS, DEFAULT_KIND_TOKENS } from "./icons.js";
|
|
5
|
+
import { buildQuickFileDragState } from "./dragState.js";
|
|
6
|
+
import { formatMessage } from "./locales/index.js";
|
|
7
|
+
import { formatBytes } from "./visibleRows.js";
|
|
8
|
+
import { MUTED } from "./tokens.js";
|
|
9
|
+
/**
|
|
10
|
+
* The panel's rows region: busy skeletons while the first list query is in
|
|
11
|
+
* flight, an error alert with Retry, a guided empty state, the no-matches
|
|
12
|
+
* notice, and the file rows themselves (kind glyph + semantic token, name,
|
|
13
|
+
* mono meta line, + insert action — each row publishing the bounded drag
|
|
14
|
+
* state itself, per FileRow).
|
|
15
|
+
*/
|
|
16
|
+
export function QuickFilesFileList({ status, entries, rows, strings, kindTokens, onRetry }) {
|
|
17
|
+
return (_jsx(_Fragment, { children: status.phase === "error" ? (_jsx(Alert, { variant: "light", color: "threat", title: strings.errorTitle, "data-testid": "quick-files-error", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", "data-testid": "quick-files-error-message", children: status.message }), _jsx(Group, { children: _jsx(Button, { variant: "default", "data-testid": "quick-files-retry", onClick: onRetry, children: strings.retry }) })] }) })) : entries === null ? (_jsx(Stack, { gap: 8, "data-testid": "quick-files-loading", "aria-busy": "true", children: [0, 1, 2, 3].map((row) => (_jsx(Skeleton, { height: 58, radius: 5 }, row))) })) : entries.length === 0 ? (_jsx(Card, { withBorder: true, "data-testid": "quick-files-empty", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { fw: 500, children: strings.emptyTitle }), _jsx(Text, { size: "sm", children: strings.emptyHint })] }) })) : rows.length === 0 ? (_jsx(Text, { size: "sm", c: MUTED, "data-testid": "quick-files-no-matches", children: strings.noMatches })) : (_jsx(Stack, { gap: 8, "data-testid": "quick-files-rows", children: rows.map((entry) => {
|
|
18
|
+
const glyph = entry.glyph ?? DEFAULT_KIND_GLYPHS[entry.kind] ?? "doc";
|
|
19
|
+
const token = kindTokens[entry.kind] ?? DEFAULT_KIND_TOKENS[entry.kind] ?? "accent";
|
|
20
|
+
const meta = entry.meta ?? `${entry.kind.toUpperCase()} • ${formatBytes(entry.sizeBytes)}`;
|
|
21
|
+
return (_jsx(FileRow, { entry: entry, dragState: buildQuickFileDragState(entry), glyph: glyph, kindToken: token, insertLabel: formatMessage(strings.insertLabel, { name: entry.name }), dragLabel: formatMessage(strings.dragLabel, { name: entry.name }), meta: meta }, entry.id));
|
|
22
|
+
}) })) }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { QuickFileDragState, QuickFileEntry, QuickFileGlyph } from "./types.js";
|
|
2
|
+
export interface FileRowProps {
|
|
3
|
+
/** The IFileEntry projection the row renders. */
|
|
4
|
+
readonly entry: QuickFileEntry;
|
|
5
|
+
/** Bounded drag/insert payload builder output for this row. */
|
|
6
|
+
readonly dragState: QuickFileDragState;
|
|
7
|
+
/** Icon glyph for the row's kind (boards pick workflow vs audit). */
|
|
8
|
+
readonly glyph: QuickFileGlyph;
|
|
9
|
+
/** Semantic color token driving the icon hue (host theme resolves it). */
|
|
10
|
+
readonly kindToken: string;
|
|
11
|
+
/** Accessible label for the + insert action (locale-resolved). */
|
|
12
|
+
readonly insertLabel: string;
|
|
13
|
+
/** Accessible label for the drag handle (locale-resolved). */
|
|
14
|
+
readonly dragLabel: string;
|
|
15
|
+
/** Mono meta line rendered under the name (defaults to kind + size). */
|
|
16
|
+
readonly meta: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Per-prototype Quick File Insert row (v9): kind icon, name, mono meta line
|
|
20
|
+
* and the + insert action.
|
|
21
|
+
*
|
|
22
|
+
* The row OWNS the "quick-files.drag-state" channel binding (D6 reference
|
|
23
|
+
* case): the + insert action and a drag-start each publish ONLY the bounded
|
|
24
|
+
* file-metadata state {entryId, kind, name, mime, snapshotRef, sourcePanel}
|
|
25
|
+
* — never a component reference — and drag-end (drop OR cancel) clears it.
|
|
26
|
+
* Any receiver (editor viewport body, block insertion point, another panel)
|
|
27
|
+
* interprets the payload in its own way; the row knows none of them. The
|
|
28
|
+
* setter-only binding means publications never re-render the panel.
|
|
29
|
+
*/
|
|
30
|
+
export declare function FileRow({ entry, dragState, glyph, kindToken, insertLabel, dragLabel, meta }: FileRowProps): import("react").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=FileRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileRow.d.ts","sourceRoot":"","sources":["../src/FileRow.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAErF,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,+BAkFzG"}
|
package/dist/FileRow.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { Box, Group, Text, UnstyledButton } from "@mantine/core";
|
|
4
|
+
import { usePrismStateSetter } from "@zephytiju/prism-react";
|
|
5
|
+
import { FileKindIcon } from "./icons.js";
|
|
6
|
+
import { DRAG_STATE_CHANNEL } from "./dragState.js";
|
|
7
|
+
import { ACCENT, BORDER, CARD_BG, MONO, MUTED, TEXT } from "./tokens.js";
|
|
8
|
+
/** MIME type carried in the HTML5 drag data so non-Prism drop targets work too. */
|
|
9
|
+
const DRAG_MIME = "application/x-prism-quick-file";
|
|
10
|
+
/**
|
|
11
|
+
* Per-prototype Quick File Insert row (v9): kind icon, name, mono meta line
|
|
12
|
+
* and the + insert action.
|
|
13
|
+
*
|
|
14
|
+
* The row OWNS the "quick-files.drag-state" channel binding (D6 reference
|
|
15
|
+
* case): the + insert action and a drag-start each publish ONLY the bounded
|
|
16
|
+
* file-metadata state {entryId, kind, name, mime, snapshotRef, sourcePanel}
|
|
17
|
+
* — never a component reference — and drag-end (drop OR cancel) clears it.
|
|
18
|
+
* Any receiver (editor viewport body, block insertion point, another panel)
|
|
19
|
+
* interprets the payload in its own way; the row knows none of them. The
|
|
20
|
+
* setter-only binding means publications never re-render the panel.
|
|
21
|
+
*/
|
|
22
|
+
export function FileRow({ entry, dragState, glyph, kindToken, insertLabel, dragLabel, meta }) {
|
|
23
|
+
const publishDragState = usePrismStateSetter(DRAG_STATE_CHANNEL);
|
|
24
|
+
const insert = useCallback(() => {
|
|
25
|
+
publishDragState(dragState);
|
|
26
|
+
}, [dragState, publishDragState]);
|
|
27
|
+
const iconColor = `var(--mantine-color-${kindToken}-filled)`;
|
|
28
|
+
return (_jsxs(Group, { gap: 10, wrap: "nowrap", align: "center", px: 12, style: {
|
|
29
|
+
minHeight: 58,
|
|
30
|
+
background: CARD_BG,
|
|
31
|
+
border: `1px solid ${BORDER}`,
|
|
32
|
+
borderRadius: 5,
|
|
33
|
+
cursor: "grab",
|
|
34
|
+
}, "data-testid": `quick-files-row-${entry.id}`, draggable: true, "aria-label": dragLabel, onDragStart: (event) => {
|
|
35
|
+
publishDragState(dragState);
|
|
36
|
+
// jsdom dispatches synthetic drags without a dataTransfer; real
|
|
37
|
+
// browsers always provide one.
|
|
38
|
+
if (event.dataTransfer) {
|
|
39
|
+
event.dataTransfer.effectAllowed = "copy";
|
|
40
|
+
event.dataTransfer.setData(DRAG_MIME, JSON.stringify(dragState));
|
|
41
|
+
}
|
|
42
|
+
}, onDragEnd: () => {
|
|
43
|
+
// Drop or cancel — the drag session is over either way: clear.
|
|
44
|
+
publishDragState(null);
|
|
45
|
+
}, children: [_jsx(Box, { style: { color: iconColor, flexShrink: 0 }, "data-testid": `quick-files-icon-${entry.id}`, children: _jsx(FileKindIcon, { glyph: glyph }) }), _jsxs(Box, { style: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { fz: 12, fw: 500, style: {
|
|
46
|
+
color: TEXT,
|
|
47
|
+
whiteSpace: "nowrap",
|
|
48
|
+
overflow: "hidden",
|
|
49
|
+
textOverflow: "ellipsis",
|
|
50
|
+
}, "data-testid": `quick-files-name-${entry.id}`, children: entry.name }), _jsx(Text, { ff: MONO, fz: 9, style: { color: MUTED, letterSpacing: "0.03em" }, "data-testid": `quick-files-meta-${entry.id}`, children: meta })] }), _jsx(UnstyledButton, { type: "button", fz: 18, fw: 500, lh: 1, style: { color: ACCENT, flexShrink: 0 }, "aria-label": insertLabel, "data-testid": `quick-files-insert-${entry.id}`, onClick: (event) => {
|
|
51
|
+
event.stopPropagation();
|
|
52
|
+
insert();
|
|
53
|
+
}, children: "+" })] }));
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface QuickFilesPanelHeaderProps {
|
|
2
|
+
/** Rail variant pads the header taller than the popover variant. */
|
|
3
|
+
readonly rail: boolean;
|
|
4
|
+
/** Resolved title — component configuration key with locale default applied. */
|
|
5
|
+
readonly title: string;
|
|
6
|
+
/** Resolved mono subtitle under the title. */
|
|
7
|
+
readonly subtitle: string;
|
|
8
|
+
/** Resolved trailing header meta (e.g. "{count} FILES"). */
|
|
9
|
+
readonly trailingMeta: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The panel's title header (v9 QUICK FILE INSERT block): title, mono subtitle
|
|
13
|
+
* and trailing meta — all component configuration keys (Prism has no composed
|
|
14
|
+
* title node), rendered with locale defaults by the composition root.
|
|
15
|
+
*/
|
|
16
|
+
export declare function QuickFilesPanelHeader({ rail, title, subtitle, trailingMeta }: QuickFilesPanelHeaderProps): import("react").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=PanelHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PanelHeader.d.ts","sourceRoot":"","sources":["../src/PanelHeader.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,0BAA0B,+BAyCxG"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Group, Stack, Text } from "@mantine/core";
|
|
3
|
+
import { ACCENT, CARD_DARK_BG, MONO, MUTED, TEXT } from "./tokens.js";
|
|
4
|
+
/**
|
|
5
|
+
* The panel's title header (v9 QUICK FILE INSERT block): title, mono subtitle
|
|
6
|
+
* and trailing meta — all component configuration keys (Prism has no composed
|
|
7
|
+
* title node), rendered with locale defaults by the composition root.
|
|
8
|
+
*/
|
|
9
|
+
export function QuickFilesPanelHeader({ rail, title, subtitle, trailingMeta }) {
|
|
10
|
+
return (_jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "nowrap", gap: 8, px: 16, pt: rail ? 14 : 10, pb: 10, style: { background: CARD_DARK_BG }, "data-testid": "quick-files-header", children: [_jsxs(Stack, { gap: 4, miw: 0, children: [_jsx(Text, { fz: 13, fw: 600, style: { color: TEXT, letterSpacing: "0.01em" }, "data-testid": "quick-files-title", children: title }), _jsx(Text, { ff: MONO, fz: 9, style: { color: ACCENT, letterSpacing: "0.04em" }, "data-testid": "quick-files-subtitle", children: subtitle })] }), _jsx(Text, { ff: MONO, fz: 9, style: { color: MUTED, letterSpacing: "0.03em", flexShrink: 0, paddingTop: 2 }, "data-testid": "quick-files-trailing-meta", children: trailingMeta })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { QuickFilesLocale } from "./locales/index.js";
|
|
2
|
+
export interface QuickFilesProps {
|
|
3
|
+
/** Panel title header (component configuration key; defaults to the locale's `panelTitle`). */
|
|
4
|
+
readonly title?: string;
|
|
5
|
+
/** Mono subtitle under the title (defaults to the locale's `panelSubtitle`). */
|
|
6
|
+
readonly subtitle?: string;
|
|
7
|
+
/** Trailing header meta (defaults to the locale's `{count} FILES` pattern). */
|
|
8
|
+
readonly trailingMeta?: string;
|
|
9
|
+
/** Layout variant: "rail" (default full side panel) or "popover" (compact, no info card). */
|
|
10
|
+
readonly variant?: "rail" | "popover";
|
|
11
|
+
/** Category filters — restrict rendered rows to these IFileEntry kinds (config key). */
|
|
12
|
+
readonly categoryFilters?: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Recency window in milliseconds — keep rows whose `lastOpenedAt` lies within
|
|
15
|
+
* the window (entries without a timestamp are kept; config key).
|
|
16
|
+
*/
|
|
17
|
+
readonly recencyWindowMs?: number;
|
|
18
|
+
/** File kind → semantic color token overrides (defaults are curated). */
|
|
19
|
+
readonly kindTokens?: Readonly<Record<string, string>>;
|
|
20
|
+
/** UI locale for the component-fixed strings (default "en"). */
|
|
21
|
+
readonly locale?: QuickFilesLocale;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Platform Prism quick-files micro-UI (component id "quick-files"): the
|
|
25
|
+
* Dossier Quick File Insert side panel — an axiom component, sibling of the
|
|
26
|
+
* editor viewport (NOT a sub-component of it).
|
|
27
|
+
*
|
|
28
|
+
* Renders the per-prototype panel of the authoritative v9 HTML prototype: the
|
|
29
|
+
* QUICK FILE INSERT title header (title, subtitle and trailing meta are
|
|
30
|
+
* component configuration keys — Prism has no composed title node), the
|
|
31
|
+
* bordered search box, the FILES ARE THE SHORTCUT / RECENT-COMMON FILES
|
|
32
|
+
* section labels, file rows (world view / dossier doc / board / folder icons
|
|
33
|
+
* with semantic kind tokens, name + mono meta line, + insert action) and the
|
|
34
|
+
* DROP-IN FILE MODEL info card. Rows are indexed through the IFileEntry list
|
|
35
|
+
* query.
|
|
36
|
+
*
|
|
37
|
+
* D6 REFERENCE CASE — the panel NEVER references any receiving component. On
|
|
38
|
+
* insert (+) or drag-start of a file row it publishes ONLY a bounded drag /
|
|
39
|
+
* insert state {entryId, kind, name, mime, snapshotRef, sourcePanel} on the
|
|
40
|
+
* "quick-files.drag-state" Prism channel; receivers interpret it themselves
|
|
41
|
+
* (world view → geovision embed restoring an IGeoView snapshot; evidence pack
|
|
42
|
+
* → evidence table; dossier → paragraph with inline link; unknown → generic
|
|
43
|
+
* inline file link, per D10). ESC and drag-end clear the state. No audit
|
|
44
|
+
* events: file browsing/insertion is routine, not audit-worthy.
|
|
45
|
+
*
|
|
46
|
+
* No palette is hardcoded: every color resolves to semantic theme tokens
|
|
47
|
+
* (ok / accent / warn / signal / card / card-dark / border / line / muted /
|
|
48
|
+
* text …) supplied by the host's MantineProvider.
|
|
49
|
+
*
|
|
50
|
+
* COMPOSITION ROOT of the focused sub-component files: PanelHeader (title
|
|
51
|
+
* header), SearchBox, SectionLabels, FileList (+ FileRow, icons),
|
|
52
|
+
* DropInModelCard, and the hooks/modules clients.ts (Lattice clients),
|
|
53
|
+
* dragState.ts (bounded drag-state publication), useQuickFilesEntries.ts
|
|
54
|
+
* (list lifecycle) and visibleRows.ts (row visibility/ordering rules).
|
|
55
|
+
*/
|
|
56
|
+
export declare function QuickFiles({ title, subtitle, trailingMeta, variant, categoryFilters, recencyWindowMs, kindTokens, locale, }: QuickFilesProps): import("react").JSX.Element;
|
|
57
|
+
//# sourceMappingURL=QuickFiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuickFiles.d.ts","sourceRoot":"","sources":["../src/QuickFiles.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,MAAM,WAAW,eAAe;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,wFAAwF;IACxF,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,gEAAgE;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,OAAgB,EAChB,eAAe,EACf,eAAe,EACf,UAAe,EACf,MAAa,GACd,EAAE,eAAe,+BA0EjB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { Stack } from "@mantine/core";
|
|
4
|
+
import { useLatticeTransport } from "@zephytiju/prism-react";
|
|
5
|
+
import { createQuickFilesClients } from "./clients.js";
|
|
6
|
+
import { useQuickFileDragStatePublish } from "./dragState.js";
|
|
7
|
+
import { QuickFilesDropInModelCard } from "./DropInModelCard.js";
|
|
8
|
+
import { QuickFilesFileList } from "./FileList.js";
|
|
9
|
+
import { QuickFilesPanelHeader } from "./PanelHeader.js";
|
|
10
|
+
import { QuickFilesSearchBox } from "./SearchBox.js";
|
|
11
|
+
import { QuickFilesSectionLabels } from "./SectionLabels.js";
|
|
12
|
+
import { CARD_BG, LINE } from "./tokens.js";
|
|
13
|
+
import { useQuickFilesEntries } from "./useQuickFilesEntries.js";
|
|
14
|
+
import { formatMessage, stringsForLocale } from "./locales/index.js";
|
|
15
|
+
import { selectVisibleRows } from "./visibleRows.js";
|
|
16
|
+
/**
|
|
17
|
+
* Platform Prism quick-files micro-UI (component id "quick-files"): the
|
|
18
|
+
* Dossier Quick File Insert side panel — an axiom component, sibling of the
|
|
19
|
+
* editor viewport (NOT a sub-component of it).
|
|
20
|
+
*
|
|
21
|
+
* Renders the per-prototype panel of the authoritative v9 HTML prototype: the
|
|
22
|
+
* QUICK FILE INSERT title header (title, subtitle and trailing meta are
|
|
23
|
+
* component configuration keys — Prism has no composed title node), the
|
|
24
|
+
* bordered search box, the FILES ARE THE SHORTCUT / RECENT-COMMON FILES
|
|
25
|
+
* section labels, file rows (world view / dossier doc / board / folder icons
|
|
26
|
+
* with semantic kind tokens, name + mono meta line, + insert action) and the
|
|
27
|
+
* DROP-IN FILE MODEL info card. Rows are indexed through the IFileEntry list
|
|
28
|
+
* query.
|
|
29
|
+
*
|
|
30
|
+
* D6 REFERENCE CASE — the panel NEVER references any receiving component. On
|
|
31
|
+
* insert (+) or drag-start of a file row it publishes ONLY a bounded drag /
|
|
32
|
+
* insert state {entryId, kind, name, mime, snapshotRef, sourcePanel} on the
|
|
33
|
+
* "quick-files.drag-state" Prism channel; receivers interpret it themselves
|
|
34
|
+
* (world view → geovision embed restoring an IGeoView snapshot; evidence pack
|
|
35
|
+
* → evidence table; dossier → paragraph with inline link; unknown → generic
|
|
36
|
+
* inline file link, per D10). ESC and drag-end clear the state. No audit
|
|
37
|
+
* events: file browsing/insertion is routine, not audit-worthy.
|
|
38
|
+
*
|
|
39
|
+
* No palette is hardcoded: every color resolves to semantic theme tokens
|
|
40
|
+
* (ok / accent / warn / signal / card / card-dark / border / line / muted /
|
|
41
|
+
* text …) supplied by the host's MantineProvider.
|
|
42
|
+
*
|
|
43
|
+
* COMPOSITION ROOT of the focused sub-component files: PanelHeader (title
|
|
44
|
+
* header), SearchBox, SectionLabels, FileList (+ FileRow, icons),
|
|
45
|
+
* DropInModelCard, and the hooks/modules clients.ts (Lattice clients),
|
|
46
|
+
* dragState.ts (bounded drag-state publication), useQuickFilesEntries.ts
|
|
47
|
+
* (list lifecycle) and visibleRows.ts (row visibility/ordering rules).
|
|
48
|
+
*/
|
|
49
|
+
export function QuickFiles({ title, subtitle, trailingMeta, variant = "rail", categoryFilters, recencyWindowMs, kindTokens = {}, locale = "en", }) {
|
|
50
|
+
// Every component-fixed UI string comes from the bundled locale JSONs;
|
|
51
|
+
// the title header renders from component configuration keys with locale
|
|
52
|
+
// defaults (composition-authored overrides stay localized by the composer).
|
|
53
|
+
const strings = stringsForLocale(locale);
|
|
54
|
+
const [search, setSearch] = useState("");
|
|
55
|
+
// Setter-only drag-state publication + the ESC cancel listener (D6).
|
|
56
|
+
useQuickFileDragStatePublish();
|
|
57
|
+
const transport = useLatticeTransport();
|
|
58
|
+
const clients = useMemo(() => createQuickFilesClients(transport), [transport]);
|
|
59
|
+
const { entries, status, reload } = useQuickFilesEntries(clients);
|
|
60
|
+
const rows = useMemo(() => (entries === null ? [] : selectVisibleRows(entries, search, categoryFilters, recencyWindowMs)), [entries, search, categoryFilters, recencyWindowMs]);
|
|
61
|
+
const rail = variant === "rail";
|
|
62
|
+
const resolvedTitle = title ?? strings.panelTitle;
|
|
63
|
+
const resolvedSubtitle = subtitle ?? strings.panelSubtitle;
|
|
64
|
+
const resolvedTrailingMeta = trailingMeta ?? formatMessage(strings.trailingMetaCount, { count: rows.length });
|
|
65
|
+
return (_jsxs(Stack, { gap: 8, miw: 0, style: {
|
|
66
|
+
width: rail ? 340 : 300,
|
|
67
|
+
background: CARD_BG,
|
|
68
|
+
borderLeft: `1px solid ${LINE}`,
|
|
69
|
+
borderTopRightRadius: 6,
|
|
70
|
+
borderBottomRightRadius: 6,
|
|
71
|
+
overflow: "hidden",
|
|
72
|
+
}, "data-testid": "quick-files-panel", children: [_jsx(QuickFilesPanelHeader, { rail: rail, title: resolvedTitle, subtitle: resolvedSubtitle, trailingMeta: resolvedTrailingMeta }), _jsxs(Stack, { gap: 12, px: 16, pb: 14, miw: 0, children: [_jsx(QuickFilesSearchBox, { value: search, onChange: setSearch, placeholder: strings.searchPlaceholder, label: strings.searchLabel }), _jsx(QuickFilesSectionLabels, { hint: strings.sectionHint, recent: strings.sectionRecent }), _jsx(QuickFilesFileList, { status: status, entries: entries, rows: rows, strings: strings, kindTokens: kindTokens, onRetry: () => {
|
|
73
|
+
void reload();
|
|
74
|
+
} }), rail && entries !== null && entries.length > 0 ? (_jsx(QuickFilesDropInModelCard, { title: strings.infoCardTitle, body: strings.infoCardBody })) : null] })] }));
|
|
75
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface QuickFilesSearchBoxProps {
|
|
2
|
+
/** Current search text (controlled). */
|
|
3
|
+
readonly value: string;
|
|
4
|
+
/** Reports the raw input value on every keystroke. */
|
|
5
|
+
readonly onChange: (value: string) => void;
|
|
6
|
+
/** Locale-resolved placeholder. */
|
|
7
|
+
readonly placeholder: string;
|
|
8
|
+
/** Locale-resolved accessible label. */
|
|
9
|
+
readonly label: string;
|
|
10
|
+
}
|
|
11
|
+
/** The panel's bordered accent search box on the card-dark surface (v9 ⌕). */
|
|
12
|
+
export declare function QuickFilesSearchBox({ value, onChange, placeholder, label }: QuickFilesSearchBoxProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=SearchBox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchBox.d.ts","sourceRoot":"","sources":["../src/SearchBox.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,wBAAwB;IACvC,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,mCAAmC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,wBAAwB,+BAsCpG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Group, TextInput } from "@mantine/core";
|
|
3
|
+
import { SearchGlyph } from "./icons.js";
|
|
4
|
+
import { ACCENT, INPUT_BG, MONO, MUTED, TEXT } from "./tokens.js";
|
|
5
|
+
/** The panel's bordered accent search box on the card-dark surface (v9 ⌕). */
|
|
6
|
+
export function QuickFilesSearchBox({ value, onChange, placeholder, label }) {
|
|
7
|
+
return (_jsxs(Group, { gap: 10, wrap: "nowrap", align: "center", px: 10, style: {
|
|
8
|
+
height: 40,
|
|
9
|
+
background: INPUT_BG,
|
|
10
|
+
border: `1px solid ${ACCENT}`,
|
|
11
|
+
borderRadius: 5,
|
|
12
|
+
}, "data-testid": "quick-files-search", c: ACCENT, children: [_jsx(SearchGlyph, {}), _jsx(TextInput, { variant: "unstyled", value: value, onChange: (event) => {
|
|
13
|
+
onChange(event.currentTarget.value);
|
|
14
|
+
}, placeholder: placeholder, "aria-label": label, "data-testid": "quick-files-search-input", styles: {
|
|
15
|
+
input: {
|
|
16
|
+
fontFamily: MONO,
|
|
17
|
+
fontSize: 10,
|
|
18
|
+
color: TEXT,
|
|
19
|
+
minHeight: 36,
|
|
20
|
+
"::placeholder": { color: MUTED },
|
|
21
|
+
},
|
|
22
|
+
} })] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface QuickFilesSectionLabelsProps {
|
|
2
|
+
/** Locale-resolved upper section label (FILES ARE THE SHORTCUT). */
|
|
3
|
+
readonly hint: string;
|
|
4
|
+
/** Locale-resolved lower section label (RECENT / COMMON FILES). */
|
|
5
|
+
readonly recent: string;
|
|
6
|
+
}
|
|
7
|
+
/** The panel's two mono section labels (prototype: FILES ARE THE SHORTCUT / RECENT-COMMON). */
|
|
8
|
+
export declare function QuickFilesSectionLabels({ hint, recent }: QuickFilesSectionLabelsProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=SectionLabels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SectionLabels.d.ts","sourceRoot":"","sources":["../src/SectionLabels.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,4BAA4B;IAC3C,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,+FAA+F;AAC/F,wBAAgB,uBAAuB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,4BAA4B,+BAWrF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Stack, Text } from "@mantine/core";
|
|
3
|
+
import { MONO, MUTED } from "./tokens.js";
|
|
4
|
+
/** The panel's two mono section labels (prototype: FILES ARE THE SHORTCUT / RECENT-COMMON). */
|
|
5
|
+
export function QuickFilesSectionLabels({ hint, recent }) {
|
|
6
|
+
return (_jsxs(Stack, { gap: 5, "data-testid": "quick-files-sections", children: [_jsx(Text, { ff: MONO, fz: 9, fw: 500, style: { color: MUTED, letterSpacing: "0.04em" }, children: hint }), _jsx(Text, { ff: MONO, fz: 9, fw: 500, style: { color: MUTED, letterSpacing: "0.04em" }, children: recent })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LatticeTransport } from "@zephytiju/prism-react";
|
|
2
|
+
import type { QuickFileEntryListResult, QuickFileListRequest } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Lattice clients for the quick-files panel (design record D4).
|
|
5
|
+
*
|
|
6
|
+
* The list query goes through the GENERATED `createFileEntryClient` from
|
|
7
|
+
* @zephytiju/lattice-common-interfaces over the host Lattice transport — no
|
|
8
|
+
* URLs, clients, or credentials in component code. The generated bundle's
|
|
9
|
+
* bounded summary (id/name/kind/sizeBytes) is a structural subset of the
|
|
10
|
+
* panel's row projection; richer hosts return the superset on the same route,
|
|
11
|
+
* which survives the erased generic and is picked up by the widening cast
|
|
12
|
+
* below (bounded fields only — mime, snapshotRef, meta line, pin, recency).
|
|
13
|
+
*/
|
|
14
|
+
export interface QuickFilesClients {
|
|
15
|
+
/** `interfaces/IFileEntry/list` — the panel's file index. */
|
|
16
|
+
readonly files: {
|
|
17
|
+
readonly list: (request?: QuickFileListRequest) => Promise<QuickFileEntryListResult>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Builds the interface clients bound to the host Lattice transport. */
|
|
21
|
+
export declare function createQuickFilesClients(transport: LatticeTransport): QuickFilesClients;
|
|
22
|
+
//# sourceMappingURL=clients.d.ts.map
|