@zephytiju/prism-recent-work 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 +195 -0
- package/dist/EmptyState.d.ts +14 -0
- package/dist/EmptyState.d.ts.map +1 -0
- package/dist/EmptyState.js +11 -0
- package/dist/FileRow.d.ts +22 -0
- package/dist/FileRow.d.ts.map +1 -0
- package/dist/FileRow.js +96 -0
- package/dist/RecentWork.d.ts +43 -0
- package/dist/RecentWork.d.ts.map +1 -0
- package/dist/RecentWork.js +68 -0
- package/dist/SectionHeader.d.ts +15 -0
- package/dist/SectionHeader.d.ts.map +1 -0
- package/dist/SectionHeader.js +12 -0
- package/dist/fileRowFormat.d.ts +9 -0
- package/dist/fileRowFormat.d.ts.map +1 -0
- package/dist/fileRowFormat.js +58 -0
- package/dist/fileRowIcons.d.ts +10 -0
- package/dist/fileRowIcons.d.ts.map +1 -0
- package/dist/fileRowIcons.js +32 -0
- package/dist/fileRowTypes.d.ts +57 -0
- package/dist/fileRowTypes.d.ts.map +1 -0
- package/dist/fileRowTypes.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/locales/en.json +25 -0
- package/dist/locales/index.d.ts +81 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/index.js +27 -0
- package/dist/locales/zh-CN.json +25 -0
- package/locales/en.json +25 -0
- package/locales/zh-CN.json +25 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# PrismRecentWorkMicroUI
|
|
2
|
+
|
|
3
|
+
Platform Prism recent-work micro-UI. Component id: `recent-work`.
|
|
4
|
+
Published to npm as [`@zephytiju/prism-recent-work`](https://www.npmjs.com/package/@zephytiju/prism-recent-work).
|
|
5
|
+
|
|
6
|
+
The VAULT root-level continuation strip (Guanlan File Hub page): the
|
|
7
|
+
`RECENT WORK / CONTINUE WHERE YOU LEFT OFF` header (titles render from component
|
|
8
|
+
configuration keys — Prism has no composed title node) followed by per-prototype
|
|
9
|
+
file-row members — kind icon, file name, descriptor line, live/sync dot —
|
|
10
|
+
rendered 0..N at app runtime from recent `IFileEntry` records (decision D3),
|
|
11
|
+
ordered by `updatedAt` with an optional recency window. It consumes the
|
|
12
|
+
host-seeded `recent-work.file-entries` channel and never talks to Lattice
|
|
13
|
+
itself. Opening a row publishes the navigation/open intent consumed by the
|
|
14
|
+
shell and the File Viewer's path slot (D6/D8). Composed applications (for
|
|
15
|
+
example Guanlan) consume it as-is; the component is platform-owned.
|
|
16
|
+
|
|
17
|
+
## Configuration keys
|
|
18
|
+
|
|
19
|
+
| Prop | Meaning |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `locale` | UI locale for the component-fixed strings: `"en" \| "zh-CN"` (default `"en"`) — see [i18n](#internationalization-i18n) |
|
|
22
|
+
| `sectionTitle` | Section header title (defaults to the locale's `sectionTitle`) |
|
|
23
|
+
| `sectionMeta` | Continue-where-you-left-off meta on the header's right (defaults to the locale's `sectionMeta`) |
|
|
24
|
+
| `columns` | Row-grid column count (default `2` — the design's default 2-column row grid; the VAULT page composition passes `3`) |
|
|
25
|
+
| `recencyWindowHours` | Recency window in hours: entries older than now − N hours are excluded (default no window) |
|
|
26
|
+
| `descriptor` | `(entry) => string` full descriptor line (default `KIND • STATUS-or-EDITED-ago • DETAIL`) |
|
|
27
|
+
| `insertAffordance` | Renders the `+` insert affordance on rows (insert contexts only; default off) |
|
|
28
|
+
| `insertLabel` | Label for the insert affordance (defaults to the locale's `insert`) |
|
|
29
|
+
|
|
30
|
+
## Channel contract
|
|
31
|
+
|
|
32
|
+
| Direction | Kind | Id | Payload |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| consumes | state | `recent-work.file-entries` | `RecentFilesProjection \| null` — recent `RecentFileEntry` records (the bounded `FileEntrySummary` plus `updatedAt`, `sync`, optional `status`/`detail`) |
|
|
35
|
+
| publishes | state | `recent-work.opened-file` | `{ id, name, kind, sync, ontologyInterface: "IFileEntry" }` on row open — the navigation/open intent (D8): a bounded file reference the shell and the File Viewer's path slot interpret in their own way (D6); receivers fetch their own domain data (D4) |
|
|
36
|
+
| emits | event | `recent-work.insert-file` | same bounded `IFileEntry` reference — only when `insertAffordance` is enabled (insert contexts) |
|
|
37
|
+
|
|
38
|
+
Channel ids are string literals at every call-site so the build-time channel-graph scanner can
|
|
39
|
+
derive the graph. Channel reads use `usePrismStateValue`; both publications are
|
|
40
|
+
setter-only (`usePrismStateSetter`) / event emissions. `RecentFileEntry` carries
|
|
41
|
+
exactly the recency data the design specifies on top of `IFileEntry` — `updatedAt`
|
|
42
|
+
(ordering + "EDITED 4M AGO") and the workspace `sync` state driving the live dot.
|
|
43
|
+
|
|
44
|
+
## Audit rule
|
|
45
|
+
|
|
46
|
+
Rendering recent files is a routine read and is NOT audit-worthy. This component
|
|
47
|
+
emits NO audit event. The only event it can ever emit is the config-gated
|
|
48
|
+
`recent-work.insert-file` (an insert-context handshake, not an observable domain
|
|
49
|
+
action).
|
|
50
|
+
|
|
51
|
+
## Internationalization (i18n)
|
|
52
|
+
|
|
53
|
+
The component ships `en` and `zh-CN` locale bundles — `src/locales/en.json` / `src/locales/zh-CN.json` —
|
|
54
|
+
and every component-fixed UI string is resolved from them (the section title and
|
|
55
|
+
continue-where-you-left-off meta, the per-kind descriptor labels, the
|
|
56
|
+
`EDITED {ago} AGO` relativization with its `M/H/D` units, the empty-state title
|
|
57
|
+
and hint, the open tooltip, the sync labels, and the insert label). The component
|
|
58
|
+
renders no hardcoded copy.
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"recent-work": {
|
|
63
|
+
"sectionTitle": "RECENT WORK",
|
|
64
|
+
"sectionMeta": "CONTINUE WHERE YOU LEFT OFF",
|
|
65
|
+
"emptyTitle": "No recent work yet",
|
|
66
|
+
"emptyHint": "Files you open and edit will appear here so you can pick up where you left off.",
|
|
67
|
+
"openFile": "Open file",
|
|
68
|
+
"editedAgo": "EDITED {ago} AGO",
|
|
69
|
+
"minutesAgo": "{count}M",
|
|
70
|
+
"hoursAgo": "{count}H",
|
|
71
|
+
"daysAgo": "{count}D",
|
|
72
|
+
"syncLive": "Live",
|
|
73
|
+
"syncSyncing": "Syncing",
|
|
74
|
+
"syncSynced": "Synced",
|
|
75
|
+
"insert": "+",
|
|
76
|
+
"kindFolder": "FOLDER",
|
|
77
|
+
"kindDossier": "DOSSIER FILE",
|
|
78
|
+
"kindEvidence": "EVIDENCE FILE",
|
|
79
|
+
"kindReference": "REFERENCE",
|
|
80
|
+
"kindBoard": "BOARD",
|
|
81
|
+
"kindWorkflowBoard": "WORKFLOW BOARD",
|
|
82
|
+
"kindAuditBoard": "AUDIT BOARD",
|
|
83
|
+
"kindWorld": "GEOVISION FILE"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- `locale?: "en" | "zh-CN"` prop (default `"en"`) selects the string table per instance.
|
|
89
|
+
- `editedAgo` uses a simple `{ago}` placeholder interpolated with the relative
|
|
90
|
+
age (plain substitution, no regexes — `formatMessage` is exported from the
|
|
91
|
+
package entry).
|
|
92
|
+
- Explicit `sectionTitle` / `sectionMeta` props override the locale strings.
|
|
93
|
+
- **Composition-authored strings are localized by the composer; component-fixed strings live in the
|
|
94
|
+
locale JSONs.** An explicit `sectionTitle` override (and the `descriptor`
|
|
95
|
+
callback) is configuration-authored: a host with a localized section passes its
|
|
96
|
+
own string per locale. Entry `status`/`detail` parts are host data and stay
|
|
97
|
+
host-localized.
|
|
98
|
+
- Locale bundles are namespaced under the component id (`"recent-work"`) so a composer can
|
|
99
|
+
deep-merge every component's bundle into ONE UI language bundle without collisions:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { locales as recentWorkLocales } from "@zephytiju/prism-recent-work";
|
|
103
|
+
// recentWorkLocales["zh-CN"] -> { "recent-work": { … } }
|
|
104
|
+
const uiBundle = deepMerge(hostStrings, recentWorkLocales["zh-CN"]);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The parsed bundles are exported from the package entry (`locales`, `en`, `zhCN`,
|
|
108
|
+
`stringsForLocale`), and the raw JSONs are also served by the `./locales/*` exports subpath
|
|
109
|
+
(e.g. `@zephytiju/prism-recent-work/locales/zh-CN.json`); `files` ships both `dist` and
|
|
110
|
+
`locales`.
|
|
111
|
+
|
|
112
|
+
## Theme
|
|
113
|
+
|
|
114
|
+
No palette is hardcoded. Every color resolves to SEMANTIC theme tokens (`ok`,
|
|
115
|
+
`accent`, `threat`, `warn`, `signal`, `card`, `card-dark`, `input`, `border`,
|
|
116
|
+
`line`, `text`, `muted`, `deep`, `panel`) consumed as CSS variables, plus
|
|
117
|
+
`--mantine-font-family-monospace` for the mono typography — the palette is
|
|
118
|
+
supplied entirely by the host's `MantineProvider`. The local demo ships TWO
|
|
119
|
+
themes, both defined in `src/demo.tsx`: `geovisionTheme` (dark), mapping each
|
|
120
|
+
semantic token onto the exact `:root` variables of the VAULT v9 prototype (deep
|
|
121
|
+
bg, panel black, card, mint, red, blue, amber, purple, muted, border), and the
|
|
122
|
+
contrasting `latticeLightTheme` (light), mapping the SAME semantic token keys
|
|
123
|
+
onto a different palette — the component is skinned purely through the
|
|
124
|
+
surrounding `MantineProvider`. Per-kind icon hues follow the prototype's palette
|
|
125
|
+
(doc mint → `ok`, board amber → `warn`, world purple → `signal`, folder blue →
|
|
126
|
+
`accent`); the sync dot uses `ok` (dimmed when synced) / `warn` (syncing).
|
|
127
|
+
|
|
128
|
+
## Source layout
|
|
129
|
+
|
|
130
|
+
`src/` is strictly two parts:
|
|
131
|
+
|
|
132
|
+
- Component source (what the package compiles): `RecentWork.tsx` (the section),
|
|
133
|
+
`FileRow.tsx` (the file-row member — the File Row sub-library axiom, owning
|
|
134
|
+
the open-intent publication), `index.ts` (public entry), and `src/locales/`
|
|
135
|
+
(`en.json`, `zh-CN.json`, `index.ts` — the i18n string bundles, their
|
|
136
|
+
resolver, and the `{ago}` interpolation helper).
|
|
137
|
+
- Demo: exactly ONE file, `src/demo.tsx` — the two host themes (GeoVision v9 +
|
|
138
|
+
Lattice Light), all demo test data (the six per-prototype recent files with
|
|
139
|
+
mixed kinds and relative recency), the channel seeding
|
|
140
|
+
(`seedDemoChannels`) and channel monitors, and the demo page rendering TWO
|
|
141
|
+
`RecentWork` instances side by side behind a global EN | 中文 language switcher
|
|
142
|
+
(plus per-instance switches): HOST A runs the default 2-column row grid, HOST
|
|
143
|
+
B the VAULT page's 3-column composition, with reseed/clear buttons.
|
|
144
|
+
|
|
145
|
+
The npm package ships `dist` (compiled component + type declarations + locale
|
|
146
|
+
JSONs) and the top-level `locales/` directory (the raw JSON bundles, served by
|
|
147
|
+
the `./locales/*` exports subpath); no demo code is published.
|
|
148
|
+
`scripts/copy-locales.mjs` copies the JSON bundles into both locations during
|
|
149
|
+
`npm run build`.
|
|
150
|
+
|
|
151
|
+
## Local development
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
npm install
|
|
155
|
+
npm run typecheck
|
|
156
|
+
npm test
|
|
157
|
+
npm run dev
|
|
158
|
+
npm run shot-demo
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`npm install` pulls the platform peers (`@zephytiju/prism-react`,
|
|
162
|
+
`@zephytiju/lattice-common-interfaces`) from the npm registry, along with the
|
|
163
|
+
host-side peer dependencies (`react`, `react-dom`, `@mantine/core`). When
|
|
164
|
+
consuming the published package, install it directly
|
|
165
|
+
(`npm install @zephytiju/prism-recent-work`) and provide those peer dependencies
|
|
166
|
+
in the host application.
|
|
167
|
+
|
|
168
|
+
The demo (`npm run dev`, entry `src/demo.tsx`) plays the host: on load it seeds
|
|
169
|
+
the six per-prototype recent files onto the GLOBAL recent-work channel (the
|
|
170
|
+
component itself makes no Lattice calls, so no mock executor is needed) and
|
|
171
|
+
renders TWO `RecentWork` instances side by side, each inside its own
|
|
172
|
+
`MantineProvider` with a different theme (GeoVision v9 dark left with the
|
|
173
|
+
default 2-column grid, Lattice Light right with the page's 3-column
|
|
174
|
+
composition) — since both instances consume the same global channels, they
|
|
175
|
+
always render the same rows in two skins. A global EN | 中文 segmented control
|
|
176
|
+
switches the `locale` prop of BOTH instances at once, and each instance carries
|
|
177
|
+
its own per-instance control so the two hosts can render DIFFERING locales
|
|
178
|
+
simultaneously. Buttons reseed the recent files or clear the channel to inspect
|
|
179
|
+
the guided empty state — in both themes and both languages — and the channel
|
|
180
|
+
monitors show the shared consumed state plus the `recent-work.opened-file`
|
|
181
|
+
publication. `npm run shot-demo` boots the vite dev server, drives the demo in
|
|
182
|
+
headless Chrome (LEFT instance `en`, RIGHT instance `zh-CN`), OPENS a recent
|
|
183
|
+
file row so the monitor shows the published open intent, and captures the
|
|
184
|
+
language switcher plus both themed instances in one shot to
|
|
185
|
+
`/tmp/guanlan-review/demo-recent-work-themes.png`.
|
|
186
|
+
|
|
187
|
+
## Design record
|
|
188
|
+
|
|
189
|
+
Page design: https://qcnwge0wy4s0.feishu.cn/wiki/TLFtwgBgpiW8iWkXT7rcOyKgnAd —
|
|
190
|
+
Component — Recent Work Section:
|
|
191
|
+
https://qcnwge0wy4s0.feishu.cn/wiki/Cfu2w4uLUiwwlIkzFCMcWIktnMF — File Row
|
|
192
|
+
sub-library: https://qcnwge0wy4s0.feishu.cn/wiki/BmoTw4WnriMV9MkKK8HchKO8nfg —
|
|
193
|
+
Grouping: https://qcnwge0wy4s0.feishu.cn/wiki/X7qdw8Akeiq1w0kJBdmcgTHBnmh.
|
|
194
|
+
Visual reference: the page's v9 interactive HTML prototype
|
|
195
|
+
(`vault-standalone.html`, `.sec-l` / `.sec-r` / `.frow` / `.fic`).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface EmptyStateProps {
|
|
2
|
+
/** Locale-resolved empty-state title (e.g. "No recent work yet"). */
|
|
3
|
+
readonly title: string;
|
|
4
|
+
/** Locale-resolved empty-state hint (e.g. "Files you open and edit will appear here"). */
|
|
5
|
+
readonly hint: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The guided empty state rendered when the recent-work channel carries no
|
|
9
|
+
* projection (or an empty one) — a bordered card with a title and hint. No
|
|
10
|
+
* palette is hardcoded; colors resolve to semantic theme tokens supplied by
|
|
11
|
+
* the host's MantineProvider.
|
|
12
|
+
*/
|
|
13
|
+
export declare function EmptyState({ title, hint }: EmptyStateProps): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=EmptyState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../src/EmptyState.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,eAAe,+BAa1D"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Card, Stack, Text } from "@mantine/core";
|
|
3
|
+
/**
|
|
4
|
+
* The guided empty state rendered when the recent-work channel carries no
|
|
5
|
+
* projection (or an empty one) — a bordered card with a title and hint. No
|
|
6
|
+
* palette is hardcoded; colors resolve to semantic theme tokens supplied by
|
|
7
|
+
* the host's MantineProvider.
|
|
8
|
+
*/
|
|
9
|
+
export function EmptyState({ title, hint }) {
|
|
10
|
+
return (_jsx(Card, { withBorder: true, "data-testid": "recent-work-empty", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { fw: 500, "data-testid": "recent-work-empty-title", children: title }), _jsx(Text, { size: "sm", "data-testid": "recent-work-empty-hint", children: hint })] }) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FileRowProps } from "./fileRowTypes.js";
|
|
2
|
+
export type { FileRowProps, FileSyncState, RecentFileEntry, RecentFilesProjection, OpenedFile, } from "./fileRowTypes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Per-prototype VAULT file row (the File Row sub-library axiom): kind icon,
|
|
5
|
+
* file name, descriptor line, live/sync dot — the row itself is the click
|
|
6
|
+
* target, and a + insert affordance appears in insert contexts only.
|
|
7
|
+
*
|
|
8
|
+
* The row OWNS the "recent-work.opened-file" channel binding: opening the row
|
|
9
|
+
* (click or keyboard activation — it is a real button) publishes the
|
|
10
|
+
* navigation/open intent, a bounded IFileEntry reference consumed by the shell
|
|
11
|
+
* and the File Viewer's path slot (D6/D8). The opened highlight derives from
|
|
12
|
+
* that same channel value, so every row bound to the channel (across hosts)
|
|
13
|
+
* reflects the currently opened file consistently. The optional + affordance
|
|
14
|
+
* emits the ephemeral "recent-work.insert-file" event instead. No Lattice
|
|
15
|
+
* calls; routine reads are NOT audit-worthy.
|
|
16
|
+
*
|
|
17
|
+
* No palette is hardcoded: every color resolves to semantic theme tokens
|
|
18
|
+
* (ok / warn / accent / card / border / muted / text / input …) supplied by
|
|
19
|
+
* the host's MantineProvider.
|
|
20
|
+
*/
|
|
21
|
+
export declare function FileRow({ entry, strings, descriptor, insertAffordance, insertLabel, }: FileRowProps): import("react").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=FileRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileRow.d.ts","sourceRoot":"","sources":["../src/FileRow.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAA8C,MAAM,mBAAmB,CAAC;AAGlG,YAAY,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,UAAU,GACX,MAAM,mBAAmB,CAAC;AAU3B;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,OAAO,EACP,UAAU,EACV,gBAAwB,EACxB,WAAW,GACZ,EAAE,YAAY,+BA0Hd"}
|
package/dist/FileRow.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { Stack, Text, UnstyledButton } from "@mantine/core";
|
|
4
|
+
import { emitPrismEvent, usePrismStateSetter, usePrismStateValue } from "@zephytiju/prism-react";
|
|
5
|
+
import { visualForKind } from "./fileRowIcons.js";
|
|
6
|
+
import { defaultDescriptor, syncLabelKeyFor, syncTokenFor } from "./fileRowFormat.js";
|
|
7
|
+
const MONO = "var(--mantine-font-family-monospace)";
|
|
8
|
+
const TEXT = "var(--mantine-color-text-filled)";
|
|
9
|
+
const MUTED = "var(--mantine-color-muted-filled)";
|
|
10
|
+
const CARD_BG = "var(--mantine-color-card-filled)";
|
|
11
|
+
const BORDER = "var(--mantine-color-border-filled)";
|
|
12
|
+
const INPUT_BG = "var(--mantine-color-input-filled)";
|
|
13
|
+
const ACCENT = "var(--mantine-color-accent-filled)";
|
|
14
|
+
/**
|
|
15
|
+
* Per-prototype VAULT file row (the File Row sub-library axiom): kind icon,
|
|
16
|
+
* file name, descriptor line, live/sync dot — the row itself is the click
|
|
17
|
+
* target, and a + insert affordance appears in insert contexts only.
|
|
18
|
+
*
|
|
19
|
+
* The row OWNS the "recent-work.opened-file" channel binding: opening the row
|
|
20
|
+
* (click or keyboard activation — it is a real button) publishes the
|
|
21
|
+
* navigation/open intent, a bounded IFileEntry reference consumed by the shell
|
|
22
|
+
* and the File Viewer's path slot (D6/D8). The opened highlight derives from
|
|
23
|
+
* that same channel value, so every row bound to the channel (across hosts)
|
|
24
|
+
* reflects the currently opened file consistently. The optional + affordance
|
|
25
|
+
* emits the ephemeral "recent-work.insert-file" event instead. No Lattice
|
|
26
|
+
* calls; routine reads are NOT audit-worthy.
|
|
27
|
+
*
|
|
28
|
+
* No palette is hardcoded: every color resolves to semantic theme tokens
|
|
29
|
+
* (ok / warn / accent / card / border / muted / text / input …) supplied by
|
|
30
|
+
* the host's MantineProvider.
|
|
31
|
+
*/
|
|
32
|
+
export function FileRow({ entry, strings, descriptor, insertAffordance = false, insertLabel, }) {
|
|
33
|
+
const publishOpenedFile = usePrismStateSetter("recent-work.opened-file");
|
|
34
|
+
const openedFile = usePrismStateValue("recent-work.opened-file");
|
|
35
|
+
const opened = openedFile?.id === entry.id;
|
|
36
|
+
const resolvedDescriptor = descriptor ?? ((item) => defaultDescriptor(item, strings));
|
|
37
|
+
const open = useCallback(() => {
|
|
38
|
+
publishOpenedFile({
|
|
39
|
+
id: entry.id,
|
|
40
|
+
name: entry.name,
|
|
41
|
+
kind: entry.kind,
|
|
42
|
+
sync: entry.sync ?? "live",
|
|
43
|
+
ontologyInterface: "IFileEntry",
|
|
44
|
+
});
|
|
45
|
+
}, [entry, publishOpenedFile]);
|
|
46
|
+
const visual = visualForKind(entry.kind);
|
|
47
|
+
const Icon = visual.Icon;
|
|
48
|
+
const sync = entry.sync ?? "live";
|
|
49
|
+
const syncToken = syncTokenFor(sync);
|
|
50
|
+
return (_jsxs(UnstyledButton, { onClick: () => {
|
|
51
|
+
open();
|
|
52
|
+
}, title: strings.openFile, style: {
|
|
53
|
+
display: "flex",
|
|
54
|
+
alignItems: "center",
|
|
55
|
+
gap: 12,
|
|
56
|
+
width: "100%",
|
|
57
|
+
height: 62,
|
|
58
|
+
padding: "0 14px 0 12px",
|
|
59
|
+
background: CARD_BG,
|
|
60
|
+
border: `1px solid ${opened ? ACCENT : BORDER}`,
|
|
61
|
+
borderRadius: 6,
|
|
62
|
+
cursor: "pointer",
|
|
63
|
+
textAlign: "left",
|
|
64
|
+
...(opened ? { boxShadow: "0 0 0 1px var(--mantine-color-accent-outline)" } : {}),
|
|
65
|
+
}, "data-testid": `recent-work-row-${entry.id}`, children: [_jsx("span", { style: {
|
|
66
|
+
width: 30,
|
|
67
|
+
display: "flex",
|
|
68
|
+
alignItems: "center",
|
|
69
|
+
justifyContent: "center",
|
|
70
|
+
flexShrink: 0,
|
|
71
|
+
color: `var(--mantine-color-${visual.token}-filled)`,
|
|
72
|
+
}, "data-testid": `recent-work-icon-${entry.id}`, children: _jsx(Icon, {}) }), _jsxs(Stack, { gap: 2, miw: 0, style: { flex: 1, alignItems: "flex-start" }, children: [_jsx(Text, { fz: 12, fw: 500, truncate: "end", style: { color: TEXT, letterSpacing: "0.01em", maxWidth: "100%" }, "data-testid": `recent-work-name-${entry.id}`, children: entry.name }), _jsx(Text, { ff: MONO, fz: 9, truncate: "end", style: { color: MUTED, letterSpacing: "0.02em", maxWidth: "100%" }, "data-testid": `recent-work-meta-${entry.id}`, children: resolvedDescriptor(entry) })] }), insertAffordance ? (_jsx(UnstyledButton, { type: "button", ff: MONO, fz: 11, fw: 500, c: "accent", style: {
|
|
73
|
+
flexShrink: 0,
|
|
74
|
+
height: 22,
|
|
75
|
+
padding: "0 10px",
|
|
76
|
+
borderRadius: 11,
|
|
77
|
+
background: INPUT_BG,
|
|
78
|
+
border: `1px solid ${BORDER}`,
|
|
79
|
+
}, "data-testid": `recent-work-insert-${entry.id}`, onClick: (event) => {
|
|
80
|
+
event.stopPropagation();
|
|
81
|
+
emitPrismEvent("recent-work.insert-file", {
|
|
82
|
+
id: entry.id,
|
|
83
|
+
name: entry.name,
|
|
84
|
+
kind: entry.kind,
|
|
85
|
+
sync,
|
|
86
|
+
ontologyInterface: "IFileEntry",
|
|
87
|
+
});
|
|
88
|
+
}, children: insertLabel ?? strings.insert })) : null, _jsx("span", { "aria-label": strings[syncLabelKeyFor(sync)], role: "img", style: {
|
|
89
|
+
width: 7,
|
|
90
|
+
height: 7,
|
|
91
|
+
borderRadius: "50%",
|
|
92
|
+
background: `var(--mantine-color-${syncToken}-filled)`,
|
|
93
|
+
opacity: sync === "synced" ? 0.45 : 1,
|
|
94
|
+
flexShrink: 0,
|
|
95
|
+
}, "data-testid": `recent-work-dot-${entry.id}` })] }));
|
|
96
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RecentFileEntry } from "./FileRow.js";
|
|
2
|
+
import type { RecentWorkLocale } from "./locales/index.js";
|
|
3
|
+
export interface RecentWorkProps {
|
|
4
|
+
/** Section header title (defaults to the locale's `sectionTitle`). */
|
|
5
|
+
readonly sectionTitle?: string;
|
|
6
|
+
/** Continue-where-you-left-off meta on the header's right (defaults to the locale's `sectionMeta`). */
|
|
7
|
+
readonly sectionMeta?: string;
|
|
8
|
+
/** Row-grid column count (default 2 — the design's default 2-column row grid). */
|
|
9
|
+
readonly columns?: number;
|
|
10
|
+
/** Recency window in hours: entries older than now − N hours are excluded (default no window). */
|
|
11
|
+
readonly recencyWindowHours?: number;
|
|
12
|
+
/** Supplies the full row descriptor line (defaults to kind • status-or-editedAgo • detail). */
|
|
13
|
+
readonly descriptor?: (entry: RecentFileEntry) => string;
|
|
14
|
+
/** Renders the + insert affordance on every row (insert contexts only; default off). */
|
|
15
|
+
readonly insertAffordance?: boolean;
|
|
16
|
+
/** Label for the insert affordance (defaults to the locale's `insert`). */
|
|
17
|
+
readonly insertLabel?: string;
|
|
18
|
+
/** UI locale for the component-fixed strings (default "en"). */
|
|
19
|
+
readonly locale?: RecentWorkLocale;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Platform Prism recent-work micro-UI (component id "recent-work") — the
|
|
23
|
+
* VAULT root-level continuation strip: the "RECENT WORK / CONTINUE WHERE YOU
|
|
24
|
+
* LEFT OFF" header followed by per-prototype file-row members (kind icon,
|
|
25
|
+
* name, descriptor, live sync dot) rendered 0..N at app runtime from recent
|
|
26
|
+
* IFileEntry records (decision D3). The section title is part of this
|
|
27
|
+
* component — Prism has no composed title node, so titles render from
|
|
28
|
+
* component configuration keys.
|
|
29
|
+
*
|
|
30
|
+
* Rows order by updatedAt (most recent first) and can be bounded by a
|
|
31
|
+
* recency-window configuration; the live dot on each row reflects that
|
|
32
|
+
* entry's workspace sync state. Opening a row publishes the navigation/open
|
|
33
|
+
* intent on "recent-work.opened-file" — consumed by the shell and the File
|
|
34
|
+
* Viewer's path slot (D6/D8). The component is a read-only consumer of the
|
|
35
|
+
* host-seeded "recent-work.file-entries" channel and makes NO Lattice calls;
|
|
36
|
+
* rendering recent files is a routine read and is NOT audit-worthy.
|
|
37
|
+
*
|
|
38
|
+
* No palette is hardcoded: every color resolves to semantic theme tokens
|
|
39
|
+
* (ok / accent / card / border / muted / text …) supplied by the host's
|
|
40
|
+
* MantineProvider.
|
|
41
|
+
*/
|
|
42
|
+
export declare function RecentWork({ sectionTitle, sectionMeta, columns, recencyWindowHours, descriptor, insertAffordance, insertLabel, locale, }: RecentWorkProps): import("react").JSX.Element;
|
|
43
|
+
//# sourceMappingURL=RecentWork.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecentWork.d.ts","sourceRoot":"","sources":["../src/RecentWork.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,cAAc,CAAC;AAI3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,uGAAuG;IACvG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,CAAC;IACzD,wFAAwF;IACxF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,WAAW,EACX,OAAW,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAwB,EACxB,WAAW,EACX,MAAa,GACd,EAAE,eAAe,+BAgEjB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { Box, Stack } from "@mantine/core";
|
|
4
|
+
import { usePrismStateValue } from "@zephytiju/prism-react";
|
|
5
|
+
import { FileRow } from "./FileRow.js";
|
|
6
|
+
import { EmptyState } from "./EmptyState.js";
|
|
7
|
+
import { SectionHeader } from "./SectionHeader.js";
|
|
8
|
+
import { stringsForLocale } from "./locales/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Platform Prism recent-work micro-UI (component id "recent-work") — the
|
|
11
|
+
* VAULT root-level continuation strip: the "RECENT WORK / CONTINUE WHERE YOU
|
|
12
|
+
* LEFT OFF" header followed by per-prototype file-row members (kind icon,
|
|
13
|
+
* name, descriptor, live sync dot) rendered 0..N at app runtime from recent
|
|
14
|
+
* IFileEntry records (decision D3). The section title is part of this
|
|
15
|
+
* component — Prism has no composed title node, so titles render from
|
|
16
|
+
* component configuration keys.
|
|
17
|
+
*
|
|
18
|
+
* Rows order by updatedAt (most recent first) and can be bounded by a
|
|
19
|
+
* recency-window configuration; the live dot on each row reflects that
|
|
20
|
+
* entry's workspace sync state. Opening a row publishes the navigation/open
|
|
21
|
+
* intent on "recent-work.opened-file" — consumed by the shell and the File
|
|
22
|
+
* Viewer's path slot (D6/D8). The component is a read-only consumer of the
|
|
23
|
+
* host-seeded "recent-work.file-entries" channel and makes NO Lattice calls;
|
|
24
|
+
* rendering recent files is a routine read and is NOT audit-worthy.
|
|
25
|
+
*
|
|
26
|
+
* No palette is hardcoded: every color resolves to semantic theme tokens
|
|
27
|
+
* (ok / accent / card / border / muted / text …) supplied by the host's
|
|
28
|
+
* MantineProvider.
|
|
29
|
+
*/
|
|
30
|
+
export function RecentWork({ sectionTitle, sectionMeta, columns = 2, recencyWindowHours, descriptor, insertAffordance = false, insertLabel, locale = "en", }) {
|
|
31
|
+
// Every component-fixed UI string comes from the bundled locale JSONs;
|
|
32
|
+
// configuration-provided strings (an explicit sectionTitle override, …)
|
|
33
|
+
// stay composition-authored.
|
|
34
|
+
const strings = stringsForLocale(locale);
|
|
35
|
+
const resolvedSectionTitle = sectionTitle ?? strings.sectionTitle;
|
|
36
|
+
const resolvedSectionMeta = sectionMeta ?? strings.sectionMeta;
|
|
37
|
+
const projection = usePrismStateValue("recent-work.file-entries");
|
|
38
|
+
// App-runtime member rendering (D3): sort by updatedAt descending (most
|
|
39
|
+
// recent first — the design's recency ordering), then apply the optional
|
|
40
|
+
// recency window. Malformed timestamps sink to the end and fall outside
|
|
41
|
+
// any window.
|
|
42
|
+
const entries = useMemo(() => {
|
|
43
|
+
const listed = [...(projection?.entries ?? [])];
|
|
44
|
+
listed.sort((a, b) => {
|
|
45
|
+
const at = Date.parse(a.updatedAt);
|
|
46
|
+
const bt = Date.parse(b.updatedAt);
|
|
47
|
+
const aTime = Number.isNaN(at) ? Number.NEGATIVE_INFINITY : at;
|
|
48
|
+
const bTime = Number.isNaN(bt) ? Number.NEGATIVE_INFINITY : bt;
|
|
49
|
+
if (bTime !== aTime) {
|
|
50
|
+
return bTime - aTime;
|
|
51
|
+
}
|
|
52
|
+
return a.name.localeCompare(b.name);
|
|
53
|
+
});
|
|
54
|
+
if (recencyWindowHours === undefined) {
|
|
55
|
+
return listed;
|
|
56
|
+
}
|
|
57
|
+
const cutoff = Date.now() - recencyWindowHours * 3_600_000;
|
|
58
|
+
return listed.filter((entry) => {
|
|
59
|
+
const time = Date.parse(entry.updatedAt);
|
|
60
|
+
return !Number.isNaN(time) && time >= cutoff;
|
|
61
|
+
});
|
|
62
|
+
}, [projection, recencyWindowHours]);
|
|
63
|
+
return (_jsxs(Stack, { gap: 10, miw: 0, "data-testid": "recent-work-section", children: [_jsx(SectionHeader, { title: resolvedSectionTitle, meta: resolvedSectionMeta }), entries.length === 0 ? (_jsx(EmptyState, { title: strings.emptyTitle, hint: strings.emptyHint })) : (_jsx(Box, { style: {
|
|
64
|
+
display: "grid",
|
|
65
|
+
gridTemplateColumns: `repeat(${Math.max(1, columns)}, minmax(0, 1fr))`,
|
|
66
|
+
gap: 12,
|
|
67
|
+
}, "data-testid": "recent-work-grid", children: entries.map((entry) => (_jsx(FileRow, { entry: entry, strings: strings, descriptor: descriptor, insertAffordance: insertAffordance, insertLabel: insertLabel }, entry.id))) }))] }));
|
|
68
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface SectionHeaderProps {
|
|
2
|
+
/** Resolved section title (locale string or explicit configuration override). */
|
|
3
|
+
readonly title: string;
|
|
4
|
+
/** Resolved continue-where-you-left-off meta (locale string or override). */
|
|
5
|
+
readonly meta: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The Recent Work section header: the section title on the left and the
|
|
9
|
+
* continue-where-you-left-off meta on the right. The header is part of the
|
|
10
|
+
* component (Prism has no composed title node), so titles render from
|
|
11
|
+
* component configuration keys. No palette is hardcoded — colors resolve to
|
|
12
|
+
* semantic theme tokens supplied by the host's MantineProvider.
|
|
13
|
+
*/
|
|
14
|
+
export declare function SectionHeader({ title, meta }: SectionHeaderProps): import("react").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=SectionHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SectionHeader.d.ts","sourceRoot":"","sources":["../src/SectionHeader.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,kBAAkB,+BAqBhE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Group, Text } from "@mantine/core";
|
|
3
|
+
/**
|
|
4
|
+
* The Recent Work section header: the section title on the left and the
|
|
5
|
+
* continue-where-you-left-off meta on the right. The header is part of the
|
|
6
|
+
* component (Prism has no composed title node), so titles render from
|
|
7
|
+
* component configuration keys. No palette is hardcoded — colors resolve to
|
|
8
|
+
* semantic theme tokens supplied by the host's MantineProvider.
|
|
9
|
+
*/
|
|
10
|
+
export function SectionHeader({ title, meta }) {
|
|
11
|
+
return (_jsxs(Group, { justify: "space-between", align: "baseline", wrap: "nowrap", gap: 8, children: [_jsx(Text, { fz: 12, fw: 600, style: { color: "var(--mantine-color-text-filled)", letterSpacing: "0.02em" }, "data-testid": "recent-work-section-title", children: title }), _jsx(Text, { ff: "var(--mantine-font-family-monospace)", fz: 9, style: { color: "var(--mantine-color-muted-filled)" }, "data-testid": "recent-work-section-meta", children: meta })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RecentWorkStrings } from "./locales/index.js";
|
|
2
|
+
import type { FileSyncState, RecentFileEntry } from "./fileRowTypes.js";
|
|
3
|
+
/** Semantic token driving the live dot's color for a sync state. */
|
|
4
|
+
export declare function syncTokenFor(state: FileSyncState): string;
|
|
5
|
+
/** Locale string-table key labeling a sync state (the dot's aria-label). */
|
|
6
|
+
export declare function syncLabelKeyFor(state: FileSyncState): keyof RecentWorkStrings;
|
|
7
|
+
/** Default descriptor line: KIND • STATUS-or-EDITED-ago • DETAIL. */
|
|
8
|
+
export declare function defaultDescriptor(entry: RecentFileEntry, strings: RecentWorkStrings): string;
|
|
9
|
+
//# sourceMappingURL=fileRowFormat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileRowFormat.d.ts","sourceRoot":"","sources":["../src/fileRowFormat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA0BxE,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAEzD;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,iBAAiB,CAE7E;AAyBD,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAM5F"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { formatMessage } from "./locales/index.js";
|
|
2
|
+
/** Sync state → semantic token for the live dot. */
|
|
3
|
+
const SYNC_TOKENS = {
|
|
4
|
+
live: "ok",
|
|
5
|
+
syncing: "warn",
|
|
6
|
+
synced: "ok",
|
|
7
|
+
};
|
|
8
|
+
const SYNC_LABEL_KEYS = {
|
|
9
|
+
live: "syncLive",
|
|
10
|
+
syncing: "syncSyncing",
|
|
11
|
+
synced: "syncSynced",
|
|
12
|
+
};
|
|
13
|
+
const KIND_LABEL_KEYS = {
|
|
14
|
+
folder: "kindFolder",
|
|
15
|
+
dossier: "kindDossier",
|
|
16
|
+
evidence: "kindEvidence",
|
|
17
|
+
reference: "kindReference",
|
|
18
|
+
board: "kindBoard",
|
|
19
|
+
"workflow-board": "kindWorkflowBoard",
|
|
20
|
+
"audit-board": "kindAuditBoard",
|
|
21
|
+
world: "kindWorld",
|
|
22
|
+
};
|
|
23
|
+
/** Semantic token driving the live dot's color for a sync state. */
|
|
24
|
+
export function syncTokenFor(state) {
|
|
25
|
+
return SYNC_TOKENS[state];
|
|
26
|
+
}
|
|
27
|
+
/** Locale string-table key labeling a sync state (the dot's aria-label). */
|
|
28
|
+
export function syncLabelKeyFor(state) {
|
|
29
|
+
return SYNC_LABEL_KEYS[state];
|
|
30
|
+
}
|
|
31
|
+
/** Locale label for an entry kind; unknown kinds fall back to the uppercased kind. */
|
|
32
|
+
function kindLabelFor(kind, strings) {
|
|
33
|
+
const key = KIND_LABEL_KEYS[kind];
|
|
34
|
+
return key === undefined ? kind.toUpperCase() : strings[key];
|
|
35
|
+
}
|
|
36
|
+
/** Compact relative age ("4M" / "2H" / "3D") interpolated through the locale templates. */
|
|
37
|
+
function formatAgo(updatedAt, strings) {
|
|
38
|
+
const time = Date.parse(updatedAt);
|
|
39
|
+
if (Number.isNaN(time)) {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
const minutes = Math.max(1, Math.floor((Date.now() - time) / 60_000));
|
|
43
|
+
if (minutes < 60) {
|
|
44
|
+
return formatMessage(strings.minutesAgo, { count: minutes });
|
|
45
|
+
}
|
|
46
|
+
const hours = Math.floor(minutes / 60);
|
|
47
|
+
if (hours < 24) {
|
|
48
|
+
return formatMessage(strings.hoursAgo, { count: hours });
|
|
49
|
+
}
|
|
50
|
+
return formatMessage(strings.daysAgo, { count: Math.floor(hours / 24) });
|
|
51
|
+
}
|
|
52
|
+
/** Default descriptor line: KIND • STATUS-or-EDITED-ago • DETAIL. */
|
|
53
|
+
export function defaultDescriptor(entry, strings) {
|
|
54
|
+
const middle = entry.status ?? formatMessage(strings.editedAgo, { ago: formatAgo(entry.updatedAt, strings) });
|
|
55
|
+
return [kindLabelFor(entry.kind, strings), middle, entry.detail]
|
|
56
|
+
.filter((part) => part !== undefined && part !== "")
|
|
57
|
+
.join(" • ");
|
|
58
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
type IconComponent = () => ReactElement;
|
|
3
|
+
interface KindVisual {
|
|
4
|
+
readonly Icon: IconComponent;
|
|
5
|
+
readonly token: string;
|
|
6
|
+
}
|
|
7
|
+
/** Resolves an entry kind to its prototype drawing + semantic token. */
|
|
8
|
+
export declare function visualForKind(kind: string): KindVisual;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=fileRowIcons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileRowIcons.d.ts","sourceRoot":"","sources":["../src/fileRowIcons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAwD1C,KAAK,aAAa,GAAG,MAAM,YAAY,CAAC;AAExC,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAoBD,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEtD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Per-kind icons — the exact drawings of the VAULT v9 prototype
|
|
4
|
+
// (vault-standalone.html): folder, dossier doc, workflow board, audit board,
|
|
5
|
+
// world view. Kind families map onto the prototype's distinct drawings; the
|
|
6
|
+
// token (not a hardcoded color) supplies the per-kind hue.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const FolderIcon = () => (_jsxs("svg", { width: 30, height: 26, viewBox: "0 0 48 42", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M3 6h14l4 5h24v25H3z", stroke: "currentColor", strokeWidth: 2, strokeLinejoin: "round" }), _jsx("path", { d: "M3 16h42", stroke: "currentColor", strokeWidth: 2 })] }));
|
|
9
|
+
const DocIcon = () => (_jsxs("svg", { width: 26, height: 30, viewBox: "0 0 28 32", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M4 2h13l7 7v21H4z", stroke: "currentColor", strokeWidth: 2, strokeLinejoin: "round" }), _jsx("path", { d: "M17 2v7h7M8 14h10M8 19h10M8 24h6", stroke: "currentColor", strokeWidth: 1.6 })] }));
|
|
10
|
+
const WorkflowBoardIcon = () => (_jsxs("svg", { width: 28, height: 28, viewBox: "0 0 30 30", fill: "none", "aria-hidden": "true", children: [_jsx("rect", { x: 2, y: 2, width: 9, height: 9, rx: 1.5, stroke: "currentColor", strokeWidth: 2 }), _jsx("rect", { x: 19, y: 11, width: 9, height: 9, rx: 1.5, stroke: "currentColor", strokeWidth: 2 }), _jsx("rect", { x: 2, y: 19, width: 9, height: 9, rx: 1.5, stroke: "currentColor", strokeWidth: 2 }), _jsx("path", { d: "M11 6.5h5.5A2.5 2.5 0 0 1 19 9v2M19 20.5h-5.5A2.5 2.5 0 0 1 11 18v-5", stroke: "currentColor", strokeWidth: 2 })] }));
|
|
11
|
+
const AuditBoardIcon = () => (_jsxs("svg", { width: 28, height: 28, viewBox: "0 0 30 30", fill: "none", "aria-hidden": "true", children: [_jsx("rect", { x: 2, y: 2, width: 9, height: 9, rx: 1.5, stroke: "currentColor", strokeWidth: 2 }), _jsx("rect", { x: 19, y: 2, width: 9, height: 9, rx: 1.5, stroke: "currentColor", strokeWidth: 2 }), _jsx("rect", { x: 10, y: 19, width: 9, height: 9, rx: 1.5, stroke: "currentColor", strokeWidth: 2 }), _jsx("path", { d: "M11 11v3a2 2 0 0 0 2 2h3M15 7h4M7 11v8", stroke: "currentColor", strokeWidth: 2 })] }));
|
|
12
|
+
const WorldIcon = () => (_jsxs("svg", { width: 28, height: 28, viewBox: "0 0 30 30", fill: "none", "aria-hidden": "true", children: [_jsx("circle", { cx: 15, cy: 15, r: 12, stroke: "currentColor", strokeWidth: 2 }), _jsx("path", { d: "M3 15h24M15 3c4 4 4 20 0 24M15 3c-4 4-4 20 0 24", stroke: "currentColor", strokeWidth: 1.6 })] }));
|
|
13
|
+
/**
|
|
14
|
+
* Kind → distinct prototype drawing + semantic token. The prototype palette:
|
|
15
|
+
* folder blue(accent), doc mint(ok), board amber(warn), world purple(signal).
|
|
16
|
+
*/
|
|
17
|
+
const KIND_VISUALS = {
|
|
18
|
+
folder: { Icon: FolderIcon, token: "accent" },
|
|
19
|
+
dossier: { Icon: DocIcon, token: "ok" },
|
|
20
|
+
evidence: { Icon: DocIcon, token: "ok" },
|
|
21
|
+
reference: { Icon: DocIcon, token: "ok" },
|
|
22
|
+
board: { Icon: WorkflowBoardIcon, token: "warn" },
|
|
23
|
+
"workflow-board": { Icon: WorkflowBoardIcon, token: "warn" },
|
|
24
|
+
"audit-board": { Icon: AuditBoardIcon, token: "warn" },
|
|
25
|
+
world: { Icon: WorldIcon, token: "signal" },
|
|
26
|
+
};
|
|
27
|
+
/** Unknown kinds render the generic doc drawing under the accent token. */
|
|
28
|
+
const FALLBACK_VISUAL = { Icon: DocIcon, token: "accent" };
|
|
29
|
+
/** Resolves an entry kind to its prototype drawing + semantic token. */
|
|
30
|
+
export function visualForKind(kind) {
|
|
31
|
+
return KIND_VISUALS[kind] ?? FALLBACK_VISUAL;
|
|
32
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FileEntrySummary } from "@zephytiju/lattice-common-interfaces";
|
|
2
|
+
import type { RecentWorkStrings } from "./locales/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Workspace sync state of a recent file — drives the row's live dot:
|
|
5
|
+
* "live" renders the bright ok token (the prototype's mint dot), "syncing"
|
|
6
|
+
* the warn token, "synced" the dimmed ok token.
|
|
7
|
+
*/
|
|
8
|
+
export type FileSyncState = "live" | "syncing" | "synced";
|
|
9
|
+
/**
|
|
10
|
+
* One recent IFileEntry record: the bounded Lattice file-entry summary
|
|
11
|
+
* (id, name, kind, sizeBytes) plus exactly the recency data the Recent Work
|
|
12
|
+
* design specifies — the updatedAt instant the section orders and relativizes
|
|
13
|
+
* ("EDITED 4M AGO"), the workspace sync state driving the live dot, and the
|
|
14
|
+
* two optional descriptor metadata parts ("LATTICE", "8 BLOCKS").
|
|
15
|
+
*/
|
|
16
|
+
export interface RecentFileEntry extends FileEntrySummary {
|
|
17
|
+
/** ISO-8601 instant of the last edit — rows order by this, descending. */
|
|
18
|
+
readonly updatedAt: string;
|
|
19
|
+
/** Workspace sync state driving the row's live dot (default "live"). */
|
|
20
|
+
readonly sync?: FileSyncState;
|
|
21
|
+
/** Leading metadata status for the descriptor line (e.g. "LATTICE"); replaces the edited-ago part when present. */
|
|
22
|
+
readonly status?: string;
|
|
23
|
+
/** Trailing metadata detail for the descriptor line (e.g. "8 BLOCKS"). */
|
|
24
|
+
readonly detail?: string;
|
|
25
|
+
}
|
|
26
|
+
/** The recent-file projection the component consumes: entries ordered by recency by their publisher. */
|
|
27
|
+
export interface RecentFilesProjection {
|
|
28
|
+
readonly entries: readonly RecentFileEntry[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Payload published on "recent-work.opened-file" when a row is opened — a
|
|
32
|
+
* bounded file reference pinned to the IFileEntry interface. This is the
|
|
33
|
+
* navigation/open INTENT (decision D8): the shell and the File Viewer's path
|
|
34
|
+
* slot receive the identifier and interpret it in their own way (D6);
|
|
35
|
+
* receivers fetch their own domain data (D4). Opening a recent file is NOT
|
|
36
|
+
* audit-worthy.
|
|
37
|
+
*/
|
|
38
|
+
export type OpenedFile = {
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly kind: string;
|
|
42
|
+
readonly sync: FileSyncState;
|
|
43
|
+
readonly ontologyInterface: "IFileEntry";
|
|
44
|
+
};
|
|
45
|
+
export interface FileRowProps {
|
|
46
|
+
/** The recent file entry the row renders. */
|
|
47
|
+
readonly entry: RecentFileEntry;
|
|
48
|
+
/** Locale-resolved string table (supplied by RecentWork or the host). */
|
|
49
|
+
readonly strings: RecentWorkStrings;
|
|
50
|
+
/** Supplies the full descriptor line (defaults to kind • status-or-editedAgo • detail). */
|
|
51
|
+
readonly descriptor?: (entry: RecentFileEntry) => string;
|
|
52
|
+
/** Renders the + insert affordance (insert contexts only; default off). */
|
|
53
|
+
readonly insertAffordance?: boolean;
|
|
54
|
+
/** Label for the insert affordance (defaults to the locale's `insert`). */
|
|
55
|
+
readonly insertLabel?: string;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=fileRowTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileRowTypes.d.ts","sourceRoot":"","sources":["../src/fileRowTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC;IAC9B,mHAAmH;IACnH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wGAAwG;AACxG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;CAC9C;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAC;CAC1C,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,CAAC;IACzD,2EAA2E;IAC3E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { RecentWork } from "./RecentWork.js";
|
|
2
|
+
export type { RecentWorkProps } from "./RecentWork.js";
|
|
3
|
+
export { FileRow } from "./FileRow.js";
|
|
4
|
+
export type { FileRowProps, FileSyncState, RecentFileEntry, RecentFilesProjection, OpenedFile, } from "./FileRow.js";
|
|
5
|
+
export { en, zhCN, locales, stringsForLocale, formatMessage } from "./locales/index.js";
|
|
6
|
+
export type { RecentWorkLocale, RecentWorkStrings } from "./locales/index.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxF,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recent-work": {
|
|
3
|
+
"sectionTitle": "RECENT WORK",
|
|
4
|
+
"sectionMeta": "CONTINUE WHERE YOU LEFT OFF",
|
|
5
|
+
"emptyTitle": "No recent work yet",
|
|
6
|
+
"emptyHint": "Files you open and edit will appear here so you can pick up where you left off.",
|
|
7
|
+
"openFile": "Open file",
|
|
8
|
+
"editedAgo": "EDITED {ago} AGO",
|
|
9
|
+
"minutesAgo": "{count}M",
|
|
10
|
+
"hoursAgo": "{count}H",
|
|
11
|
+
"daysAgo": "{count}D",
|
|
12
|
+
"syncLive": "Live",
|
|
13
|
+
"syncSyncing": "Syncing",
|
|
14
|
+
"syncSynced": "Synced",
|
|
15
|
+
"insert": "+",
|
|
16
|
+
"kindFolder": "FOLDER",
|
|
17
|
+
"kindDossier": "DOSSIER FILE",
|
|
18
|
+
"kindEvidence": "EVIDENCE FILE",
|
|
19
|
+
"kindReference": "REFERENCE",
|
|
20
|
+
"kindBoard": "BOARD",
|
|
21
|
+
"kindWorkflowBoard": "WORKFLOW BOARD",
|
|
22
|
+
"kindAuditBoard": "AUDIT BOARD",
|
|
23
|
+
"kindWorld": "GEOVISION FILE"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import enBundle from "./en.json";
|
|
2
|
+
/**
|
|
3
|
+
* Locales the recent-work micro-UI bundles. Every component-fixed UI string
|
|
4
|
+
* is resolved from these bundles (statically imported, resolveJsonModule); the
|
|
5
|
+
* component never hardcodes copy. Configuration-provided strings (an explicit
|
|
6
|
+
* sectionTitle override, …) stay composition-authored and are localized by the
|
|
7
|
+
* composer.
|
|
8
|
+
*/
|
|
9
|
+
export type RecentWorkLocale = "en" | "zh-CN";
|
|
10
|
+
/**
|
|
11
|
+
* The per-locale string table — the keys of the en bundle. Declared widened
|
|
12
|
+
* (string values) so the zh-CN bundle is checked for exact key parity at
|
|
13
|
+
* compile time while keeping literal JSON types out of the public surface.
|
|
14
|
+
*/
|
|
15
|
+
export type RecentWorkStrings = {
|
|
16
|
+
readonly [K in keyof typeof enBundle["recent-work"]]: string;
|
|
17
|
+
};
|
|
18
|
+
/** Parsed en locale bundle (namespaced under the component id "recent-work"). */
|
|
19
|
+
export declare const en: {
|
|
20
|
+
"recent-work": {
|
|
21
|
+
sectionTitle: string;
|
|
22
|
+
sectionMeta: string;
|
|
23
|
+
emptyTitle: string;
|
|
24
|
+
emptyHint: string;
|
|
25
|
+
openFile: string;
|
|
26
|
+
editedAgo: string;
|
|
27
|
+
minutesAgo: string;
|
|
28
|
+
hoursAgo: string;
|
|
29
|
+
daysAgo: string;
|
|
30
|
+
syncLive: string;
|
|
31
|
+
syncSyncing: string;
|
|
32
|
+
syncSynced: string;
|
|
33
|
+
insert: string;
|
|
34
|
+
kindFolder: string;
|
|
35
|
+
kindDossier: string;
|
|
36
|
+
kindEvidence: string;
|
|
37
|
+
kindReference: string;
|
|
38
|
+
kindBoard: string;
|
|
39
|
+
kindWorkflowBoard: string;
|
|
40
|
+
kindAuditBoard: string;
|
|
41
|
+
kindWorld: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/** Parsed zh-CN locale bundle (namespaced under the component id "recent-work"). */
|
|
45
|
+
export declare const zhCN: {
|
|
46
|
+
"recent-work": {
|
|
47
|
+
sectionTitle: string;
|
|
48
|
+
sectionMeta: string;
|
|
49
|
+
emptyTitle: string;
|
|
50
|
+
emptyHint: string;
|
|
51
|
+
openFile: string;
|
|
52
|
+
editedAgo: string;
|
|
53
|
+
minutesAgo: string;
|
|
54
|
+
hoursAgo: string;
|
|
55
|
+
daysAgo: string;
|
|
56
|
+
syncLive: string;
|
|
57
|
+
syncSyncing: string;
|
|
58
|
+
syncSynced: string;
|
|
59
|
+
insert: string;
|
|
60
|
+
kindFolder: string;
|
|
61
|
+
kindDossier: string;
|
|
62
|
+
kindEvidence: string;
|
|
63
|
+
kindReference: string;
|
|
64
|
+
kindBoard: string;
|
|
65
|
+
kindWorkflowBoard: string;
|
|
66
|
+
kindAuditBoard: string;
|
|
67
|
+
kindWorld: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/** All bundled locale bundles keyed by locale id — for downstream deep-merge. */
|
|
71
|
+
export declare const locales: Record<RecentWorkLocale, {
|
|
72
|
+
readonly "recent-work": RecentWorkStrings;
|
|
73
|
+
}>;
|
|
74
|
+
/** Resolves the string table for a locale. */
|
|
75
|
+
export declare function stringsForLocale(locale: RecentWorkLocale): RecentWorkStrings;
|
|
76
|
+
/**
|
|
77
|
+
* Simple `{placeholder}` interpolation (e.g. `"EDITED {ago} AGO"` + ago).
|
|
78
|
+
* Placeholder-for-value substitution only — no ICU, no regexes.
|
|
79
|
+
*/
|
|
80
|
+
export declare function formatMessage(template: string, values: Readonly<Record<string, string | number>>): string;
|
|
81
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/locales/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAC;AAGjC;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC;AAE9C;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,CAAC,IAAI,MAAM,OAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,MAAM;CAC7D,CAAC;AAOF,iFAAiF;AACjF,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;CAAW,CAAC;AAE3B,oFAAoF;AACpF,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;CAAa,CAAC;AAE/B,iFAAiF;AACjF,eAAO,MAAM,OAAO;4BAZ8C,iBAAiB;EAYrD,CAAC;AAE/B,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB,CAE5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GAChD,MAAM,CAMR"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import enBundle from "./en.json";
|
|
2
|
+
import zhCNBundle from "./zh-CN.json";
|
|
3
|
+
const bundles = {
|
|
4
|
+
en: enBundle,
|
|
5
|
+
"zh-CN": zhCNBundle,
|
|
6
|
+
};
|
|
7
|
+
/** Parsed en locale bundle (namespaced under the component id "recent-work"). */
|
|
8
|
+
export const en = enBundle;
|
|
9
|
+
/** Parsed zh-CN locale bundle (namespaced under the component id "recent-work"). */
|
|
10
|
+
export const zhCN = zhCNBundle;
|
|
11
|
+
/** All bundled locale bundles keyed by locale id — for downstream deep-merge. */
|
|
12
|
+
export const locales = bundles;
|
|
13
|
+
/** Resolves the string table for a locale. */
|
|
14
|
+
export function stringsForLocale(locale) {
|
|
15
|
+
return bundles[locale]["recent-work"];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Simple `{placeholder}` interpolation (e.g. `"EDITED {ago} AGO"` + ago).
|
|
19
|
+
* Placeholder-for-value substitution only — no ICU, no regexes.
|
|
20
|
+
*/
|
|
21
|
+
export function formatMessage(template, values) {
|
|
22
|
+
let result = template;
|
|
23
|
+
for (const [key, value] of Object.entries(values)) {
|
|
24
|
+
result = result.replaceAll(`{${key}}`, String(value));
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recent-work": {
|
|
3
|
+
"sectionTitle": "最近工作",
|
|
4
|
+
"sectionMeta": "从上次中断处继续",
|
|
5
|
+
"emptyTitle": "暂无最近工作",
|
|
6
|
+
"emptyHint": "打开和编辑过的文件会出现在这里,便于快速继续上次的工作。",
|
|
7
|
+
"openFile": "打开文件",
|
|
8
|
+
"editedAgo": "编辑于 {ago}前",
|
|
9
|
+
"minutesAgo": "{count}分钟",
|
|
10
|
+
"hoursAgo": "{count}小时",
|
|
11
|
+
"daysAgo": "{count}天",
|
|
12
|
+
"syncLive": "实时",
|
|
13
|
+
"syncSyncing": "同步中",
|
|
14
|
+
"syncSynced": "已同步",
|
|
15
|
+
"insert": "+",
|
|
16
|
+
"kindFolder": "文件夹",
|
|
17
|
+
"kindDossier": "案卷文件",
|
|
18
|
+
"kindEvidence": "证据文件",
|
|
19
|
+
"kindReference": "参考资料",
|
|
20
|
+
"kindBoard": "看板",
|
|
21
|
+
"kindWorkflowBoard": "工作流板",
|
|
22
|
+
"kindAuditBoard": "审计板",
|
|
23
|
+
"kindWorld": "GEOVISION 文件"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/locales/en.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recent-work": {
|
|
3
|
+
"sectionTitle": "RECENT WORK",
|
|
4
|
+
"sectionMeta": "CONTINUE WHERE YOU LEFT OFF",
|
|
5
|
+
"emptyTitle": "No recent work yet",
|
|
6
|
+
"emptyHint": "Files you open and edit will appear here so you can pick up where you left off.",
|
|
7
|
+
"openFile": "Open file",
|
|
8
|
+
"editedAgo": "EDITED {ago} AGO",
|
|
9
|
+
"minutesAgo": "{count}M",
|
|
10
|
+
"hoursAgo": "{count}H",
|
|
11
|
+
"daysAgo": "{count}D",
|
|
12
|
+
"syncLive": "Live",
|
|
13
|
+
"syncSyncing": "Syncing",
|
|
14
|
+
"syncSynced": "Synced",
|
|
15
|
+
"insert": "+",
|
|
16
|
+
"kindFolder": "FOLDER",
|
|
17
|
+
"kindDossier": "DOSSIER FILE",
|
|
18
|
+
"kindEvidence": "EVIDENCE FILE",
|
|
19
|
+
"kindReference": "REFERENCE",
|
|
20
|
+
"kindBoard": "BOARD",
|
|
21
|
+
"kindWorkflowBoard": "WORKFLOW BOARD",
|
|
22
|
+
"kindAuditBoard": "AUDIT BOARD",
|
|
23
|
+
"kindWorld": "GEOVISION FILE"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recent-work": {
|
|
3
|
+
"sectionTitle": "最近工作",
|
|
4
|
+
"sectionMeta": "从上次中断处继续",
|
|
5
|
+
"emptyTitle": "暂无最近工作",
|
|
6
|
+
"emptyHint": "打开和编辑过的文件会出现在这里,便于快速继续上次的工作。",
|
|
7
|
+
"openFile": "打开文件",
|
|
8
|
+
"editedAgo": "编辑于 {ago}前",
|
|
9
|
+
"minutesAgo": "{count}分钟",
|
|
10
|
+
"hoursAgo": "{count}小时",
|
|
11
|
+
"daysAgo": "{count}天",
|
|
12
|
+
"syncLive": "实时",
|
|
13
|
+
"syncSyncing": "同步中",
|
|
14
|
+
"syncSynced": "已同步",
|
|
15
|
+
"insert": "+",
|
|
16
|
+
"kindFolder": "文件夹",
|
|
17
|
+
"kindDossier": "案卷文件",
|
|
18
|
+
"kindEvidence": "证据文件",
|
|
19
|
+
"kindReference": "参考资料",
|
|
20
|
+
"kindBoard": "看板",
|
|
21
|
+
"kindWorkflowBoard": "工作流板",
|
|
22
|
+
"kindAuditBoard": "审计板",
|
|
23
|
+
"kindWorld": "GEOVISION 文件"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zephytiju/prism-recent-work",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Platform Prism recent-work micro-UI (component id \"recent-work\"): the VAULT root-level continuation strip — RECENT WORK / CONTINUE WHERE YOU LEFT OFF header plus per-prototype file-row members (kind icon, name, descriptor, live sync dot) rendering recent IFileEntry records 0..N at app runtime, ordered by updatedAt with an optional recency window and a configurable column grid. Consumes the recent-work.file-entries channel; opening a row publishes the navigation/open intent bounded payload for the shell and the File Viewer's path slot. No Lattice calls. Ships en + zh-CN locale bundles.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./locales/*": "./locales/*"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": { "access": "public" },
|
|
17
|
+
"files": ["dist", "locales"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite --host 127.0.0.1",
|
|
20
|
+
"build": "tsc -p tsconfig.build.json && node scripts/copy-locales.mjs",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"shot-demo": "node scripts/shot-demo.mjs"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@mantine/core": "^8.1.1",
|
|
27
|
+
"@zephytiju/lattice-common-interfaces": ">=0.1.0",
|
|
28
|
+
"@zephytiju/prism-react": ">=0.1.0",
|
|
29
|
+
"react": "^19.1.0",
|
|
30
|
+
"react-dom": "^19.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@mantine/core": "8.1.1",
|
|
34
|
+
"@testing-library/dom": "^10.4.0",
|
|
35
|
+
"@testing-library/react": "^16.1.0",
|
|
36
|
+
"@types/node": "^22.10.2",
|
|
37
|
+
"@types/react": "^19.1.0",
|
|
38
|
+
"@types/react-dom": "^19.1.0",
|
|
39
|
+
"@zephytiju/lattice-common-interfaces": "0.1.0",
|
|
40
|
+
"@zephytiju/prism-react": "0.1.0",
|
|
41
|
+
"jsdom": "^25.0.1",
|
|
42
|
+
"puppeteer-core": "^24.10.0",
|
|
43
|
+
"react": "19.1.0",
|
|
44
|
+
"react-dom": "19.1.0",
|
|
45
|
+
"typescript": "^5.7.2",
|
|
46
|
+
"vite": "6.0.5",
|
|
47
|
+
"vitest": "^2.1.8"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20"
|
|
51
|
+
}
|
|
52
|
+
}
|