efficient-shell 4.175.0 → 4.179.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 +426 -6
- package/dist/{Browser-PZIVSYLM.js → Browser-LZMLL4CZ.js} +14 -14
- package/dist/Calculator-R5OEHQ67.js +29 -0
- package/dist/ConfirmDialog-CMNQZQA3.js +1 -0
- package/dist/{CurrencyConverter-4RZZ5STO.js → CurrencyConverter-AMXUOQR2.js} +7 -7
- package/dist/{Documents-ERSFDOCV.js → Documents-M4HKZAPJ.js} +12 -12
- package/dist/{Files-3J2DW6ET.js → Files-OY5GKCXA.js} +24 -24
- package/dist/Notepad-JLDNTMVB.js +25 -0
- package/dist/PdfViewer-3SC3LE4U.js +1 -0
- package/dist/{PomodoroTimer-D6VQJICB.js → PomodoroTimer-LE6J2KUU.js} +24 -24
- package/dist/Preview-2KHLL6NP.js +188 -0
- package/dist/{Sidebar-2BZJ2MEI.js → Sidebar-BQ4P5LBI.js} +25 -25
- package/dist/Spreadsheet-B5Z5MSX4.js +37 -0
- package/dist/{Stock-KRPM6NSB.js → Stock-GWVG2INU.js} +4 -4
- package/dist/{Weather-HRH4JHTF.js → Weather-APBCG4TO.js} +13 -13
- package/dist/{WorldClock-OUSV52XM.js → WorldClock-2LMZYKWR.js} +7 -7
- package/dist/apps/index.d.ts +4 -2
- package/dist/apps/index.js +1 -1
- package/dist/bin/ui-check.js +1 -1
- package/dist/{chunk-L3U2R7DH.js → chunk-3DGTIWZP.js} +1 -1
- package/dist/{chunk-6BZCJQCT.js → chunk-47VJNVB6.js} +643 -643
- package/dist/{chunk-KS7OSQ3Y.js → chunk-5ZDM6WIC.js} +11 -11
- package/dist/{chunk-E2D54MVV.js → chunk-6CFBF543.js} +2 -2
- package/dist/{chunk-LCTSBQBJ.js → chunk-7WZEFHPS.js} +1 -1
- package/dist/{chunk-PGVUO7GB.js → chunk-AA4TMQOS.js} +3 -3
- package/dist/chunk-AZURXTRB.js +1 -0
- package/dist/chunk-C3QOHMR6.js +1 -0
- package/dist/{chunk-YJRC6IKD.js → chunk-CIXQKCFA.js} +4 -4
- package/dist/chunk-CZK2VKIO.js +9 -0
- package/dist/{chunk-KG4NRNFS.js → chunk-FORN7LG7.js} +1 -1
- package/dist/chunk-J3LIIIHJ.js +1 -0
- package/dist/chunk-KDRAU4XQ.js +27 -0
- package/dist/{chunk-ZOPYAOUP.js → chunk-KVOFUFPP.js} +1 -1
- package/dist/{chunk-KDHXRJIX.js → chunk-LK7V32MR.js} +1 -1
- package/dist/{chunk-GCO3A63X.js → chunk-N6STBYXR.js} +111 -111
- package/dist/{chunk-54OP3M3K.js → chunk-NHIGOEZF.js} +1 -1
- package/dist/{chunk-EXXHI5DO.js → chunk-OGXU7QFW.js} +2 -2
- package/dist/{chunk-JOOXVX4A.js → chunk-PE4F4MJE.js} +4 -4
- package/dist/chunk-R7EBJKN2.js +69 -0
- package/dist/{chunk-TMQEH7WA.js → chunk-SVO3ES2E.js} +1 -1
- package/dist/{chunk-RKR5V4QR.js → chunk-T4CFSGSL.js} +6 -6
- package/dist/{chunk-D3UQLWIZ.js → chunk-UAIVAA5X.js} +11 -11
- package/dist/{chunk-REXHKIIJ.js → chunk-WNEO7TSP.js} +1 -1
- package/dist/{chunk-XJYXKO7K.js → chunk-ZAE6ROV7.js} +15 -15
- package/dist/file-intake/index.js +1 -1
- package/dist/i18n/index.d.ts +79 -0
- package/dist/i18n/index.js +2 -0
- package/dist/{index-DIaav9a1.d.ts → index-DcUj0nXY.d.ts} +39 -927
- package/dist/index.d.ts +3 -2
- package/dist/index.js +656 -656
- package/dist/locales/zh-Hans.d.ts +59 -0
- package/dist/locales/zh-Hans.js +1 -0
- package/dist/map/index.js +3 -3
- package/dist/page-tabs/index.js +5 -5
- package/dist/pdf/index.js +1 -1
- package/dist/strings-ZbotP_I6.d.ts +1292 -0
- package/dist/ui/index.d.ts +2 -1
- package/dist/ui/index.js +1 -1
- package/package.json +21 -1
- package/dist/Calculator-6BJVESFN.js +0 -29
- package/dist/ConfirmDialog-6PVUVYXZ.js +0 -1
- package/dist/Notepad-MVMFJE2F.js +0 -25
- package/dist/PdfViewer-RKRGO7Q2.js +0 -1
- package/dist/Preview-VIHMVC7B.js +0 -187
- package/dist/Spreadsheet-KTB6JLBV.js +0 -37
- package/dist/chunk-3YTHR7IC.js +0 -1
- package/dist/chunk-7LAPNAKS.js +0 -1
- package/dist/chunk-FEK5ELXS.js +0 -27
- package/dist/chunk-VIIBZ6DE.js +0 -69
- package/dist/chunk-VRLHUZSC.js +0 -9
package/README.md
CHANGED
|
@@ -920,7 +920,7 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
|
|
|
920
920
|
| `Popover` | A button and the panel it opens, right under it, placed by `DropdownMenu`'s rule (left edges together, or right with `align="end"`, flipping and shifting to stay on screen and in its window, never wider than a phone allows): a non-modal `role="dialog"` named by `title` or `aria-label`, for check boxes, a date, a short form. A kit popup opened inside it (a `DatePicker`'s calendar, a `Select`'s list) does not close it. Focus moves in as it opens; Tab reads it as if it sat right after its trigger; Escape closes it and gives focus back. See [Popovers](#popovers). |
|
|
921
921
|
| `Dialog`, `Drawer` | A modal sheet that is not a shell window — centred (`Dialog`) or pinned to an edge (`Drawer`, `side` right / left / bottom) — for a till, a routed page or a form: trapped focus, a locked page, Escape and a backdrop click to close (`blocking` turns both off), a close cross when there is no `footer`. A `Dialog` is never taller than the screen less its gutter: a longer form — on a phone, most of them — scrolls inside the panel, footer included, and a footer wider than the panel wraps. `title` names it; `description` is a line under the title that DESCRIBES it to a screen reader — without one the body does, which suits a confirm whose body is the question and not a form, whose every label would be read out. `role="alertdialog"` (Dialog) for a confirmation that interrupts. A heading or note that lives in the body — a form that loads the record its heading names — names and describes it through `aria-labelledby` / `aria-describedby`. Two open at once — the second inside the first, or beside it on `<body>` where a host portals its dialogs — each hold focus, and only the one in front moves Tab (`useFocusTrap` keeps a stack). |
|
|
922
922
|
| `ConfirmProvider`, `confirm` | Imperative `confirm({ title, body })` returning a Promise<boolean>. |
|
|
923
|
-
| `GlobalSearch` | Cmd-K command palette. Pass `providers: SearchProvider[]` to add results; a provider is called `(query, { signal })` and the `signal` aborts when a newer query, closing the palette or unmount supersedes the call, so pass it to the request. Only the latest query's response is shown, whether or not a provider honours the signal. A result whose `entity_type` is `__page__` opens a page; an `entity_id` of `/path?search=text` (URL-encoded) opens that page's list with its search box filled in. Portalled to `<body>` on the overlay layer (`Z_LAYERS.overlay`), so it covers every window, pinned or not; opening it closes any open popup (`dismissPopups`). Results are grouped under a header per `result.type` (with a count), in the order each type first appears in the merged responses — providers answer best match first, so the group holding an exact match leads. ↑/↓ run through every row across groups, Tab / Shift+Tab jump to the first row of the next / previous group, Enter opens; the input is an ARIA combobox over a listbox with `role="group"` sections. With fewer than two characters typed it shows an empty state instead: **Recent** (the last 8 results picked, most recent first, deduplicated by `entity_type` + `entity_id`), **Favorites** and **Actions** — each section only when it has rows. `recent` + `onRecord` and `favorites` are props, so a routed app that passes none gets no Recent / Favorites (`pushRecentSearch(recent, picked)` keeps the list for it); `Layout` passes them from the user's preferences (`search_recent`, `favorite_documents`). Picking a result or a Recent row calls `onRecord(result)` then `onSelect(result)`; picking a Favorite or an Action does not record. **While searching**, the previous answer's rows stay drawn (with a spinner in the input row) until the next one lands; "Searching…" shows only when there is nothing to draw yet. **Failures are not "no results"**: an aborted call is silent, but a provider that rejects marks the search failed — every provider down shows `strings.search.searchFailed` instead of "No results", some down shows the rows that arrived plus a one-line `partialFailure` warning. **"See all"**: a provider may answer `SearchResult[]` or a `SearchResponse` (`{ results, truncatedTypes? }`); a group whose type is in `truncatedTypes` counts as "5+", and when `SearchConfig.listRoutes` maps its type to a `SearchListRoute` (`{ path, label }`, label the plural the consumer names its list by, e.g. `customers`) it ends in a "See all {label}" row that calls `onSelect` with a `__page__` result for `${path}?search=${encodeURIComponent(query)}` (so the page must read `usePageSearch()`); it is never recorded in Recent, and a group that is not truncated gets neither the "+" nor the row. `SearchConfig.quickActions` (`{ label, icon?, keywords?, onSelect }[]`) are the Actions: always in the empty state, and in a typed search an "Actions" group at the end for those whose label or keywords contain the query. |
|
|
923
|
+
| `GlobalSearch` | Cmd-K command palette. Pass `providers: SearchProvider[]` to add results; a provider is called `(query, { signal })` and the `signal` aborts when a newer query, closing the palette or unmount supersedes the call, so pass it to the request. Only the latest query's response is shown, whether or not a provider honours the signal. A result whose `entity_type` is `__page__` opens a page; an `entity_id` of `/path?search=text` (URL-encoded) opens that page's list with its search box filled in. Portalled to `<body>` on the overlay layer (`Z_LAYERS.overlay`), so it covers every window, pinned or not; opening it closes any open popup (`dismissPopups`). Results are grouped under a header per `result.type` (with a count), in the order each type first appears in the merged responses — providers answer best match first, so the group holding an exact match leads. ↑/↓ run through every row across groups, Tab / Shift+Tab jump to the first row of the next / previous group, Enter opens; the input is an ARIA combobox over a listbox with `role="group"` sections. With fewer than two characters typed it shows an empty state instead: **Recent** (the last 8 results picked, most recent first, deduplicated by `entity_type` + `entity_id`), **Favorites** and **Actions** — each section only when it has rows. `recent` + `onRecord` and `favorites` are props, so a routed app that passes none gets no Recent / Favorites (`pushRecentSearch(recent, picked)` keeps the list for it); `Layout` passes them from the user's preferences (`search_recent`, `favorite_documents`). Picking a result or a Recent row calls `onRecord(result)` then `onSelect(result)`; picking a Favorite or an Action does not record. **While searching**, the previous answer's rows stay drawn (with a spinner in the input row) until the next one lands; "Searching…" shows only when there is nothing to draw yet. **Failures are not "no results"**: an aborted call is silent, but a provider that rejects marks the search failed — every provider down shows `strings.search.searchFailed` instead of "No results", some down shows the rows that arrived plus a one-line `partialFailure` warning. **"See all"**: a provider may answer `SearchResult[]` or a `SearchResponse` (`{ results, truncatedTypes? }`); a group whose type is in `truncatedTypes` counts as "5+", and when `SearchConfig.listRoutes` maps its type to a `SearchListRoute` (`{ path, label }`, label the plural the consumer names its list by, e.g. `customers`) it ends in a "See all {label}" row that calls `onSelect` with a `__page__` result for `${path}?search=${encodeURIComponent(query)}` (so the page must read `usePageSearch()`); it is never recorded in Recent, and a group that is not truncated gets neither the "+" nor the row. `SearchConfig.quickActions` (`{ label, icon?, keywords?, onSelect }[]`) are the Actions: always in the empty state, and in a typed search an "Actions" group at the end for those whose label or keywords contain the query. **A result's kind and its words are separate**: `result.type` is the heading the reader sees, and an optional `result.typeKey` (`sales_order`) is the identity results group by and `typeIcons`, `listRoutes` and `truncatedTypes` are keyed by. Without `typeKey` the heading is the key, as before; a host with a translated interface sets it, because the same kind arrives under a different heading in each language. `SearchConfig.typeLabels` maps a type key to today's words, so a group's heading and the badge on a Recent row saved in another language read in the language the page is in. |
|
|
924
924
|
| `ShortcutHelp` | The keyboard cheatsheet shown on `?`. |
|
|
925
925
|
| `NotificationBell` | Taskbar bell — config via `<Layout notifications={…}>`. |
|
|
926
926
|
| `BugReportDetail` | Used inside an entity-window registry entry; reads from `<BugReportConfigProvider>`. |
|
|
@@ -951,7 +951,7 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
|
|
|
951
951
|
| `DataTable` | Read-only table for server-driven lists: it renders and reports, and never sorts or pages its own `data`. Controlled sort and pager, row selection, column groups, pinned columns, windowed rendering. `onRow` gives a row `onClick`, a `className`, any `data-*` attributes for its `<tr>` — a test's or a stylesheet's hook (`data-testid`, `data-state`) — the pointer and the keyboard coming in and out (`onMouseEnter` … `onBlur`) and `current`. `rowHref` makes each row one real link (`onRowPress` a button) with its controls still pressable — see [DataTable rows that are links](#datatable-rows-that-are-links). `stackOnNarrow` turns each row into a labelled card on a narrow container — see [DataTable on a narrow screen](#datatable-on-a-narrow-screen). |
|
|
952
952
|
| `Pagination` | Numbered page control (pairs with tables; complements `ListFooter`). |
|
|
953
953
|
| `Sparkline`, `LineChart`, `BarChart`, `DonutChart`, `ScatterChart` | Dependency-free inline-SVG charts (`currentColor`-themed). `LineChart` is the multi-series trend with optional scale, legend, dots and area fill. `ScatterChart` takes `xDomain` / `yDomain` to override the derived axis, and `xScale` / `yScale` of `'log'` for a long tail; a point outside a supplied domain is dropped and counted in the accessible label. |
|
|
954
|
-
| `BulkImportGrid` | Paste-or-upload bulk entry with column mapping, duplicate review and optional sum-merge. Hands resolved rows to `onImport`; owns no persistence. The whole panel takes a dropped CSV; the Upload CSV button opens the same dialog. |
|
|
954
|
+
| `BulkImportGrid` | Paste-or-upload bulk entry with column mapping, duplicate review and optional sum-merge. Hands resolved rows to `onImport`; owns no persistence. The whole panel takes a dropped CSV; the Upload CSV button opens the same dialog. Its words are the strings catalog's `bulkImport` section (a `description` prop wins), and its totals are written in the provider's `locale`. |
|
|
955
955
|
| `UndoProvider`, `UndoControls` | One undo stack per form window, covering its fields, line items and bulk imports. Register state with `useUndoable` and the shell shows the Undo/Redo pair itself, leftmost in the window footer, as soon as the form has state and the window is editing (a draft, a duplicate, Edit mode) or the form has said `useUndoCanEdit(true)` — a view that merely holds state gets nothing — it joins a footer that is there for other reasons and never creates one, and the mobile chrome (footer hidden) gets nothing. A form mounts `UndoControls` by hand only for a place the footer is not (a plain overlay, a section header); a hand mount claims the stack and the shell then mounts nothing for it, so a form shows one pair wherever it chose to put it. Binds ⌘Z / ⇧⌘Z (and Ctrl+Y) except while the caret is in a field, where the browser's own undo wins. `WindowManager` already mounts one per window, scoped with `windowId` so a keypress reaches only the frontmost window; pass `windowId` yourself for any provider you mount outside a `<Modal>`, or two open windows will step back together. Offered to anyone who may edit the record — gate with `canEdit` and/or `perms`; a reader sees no controls and records no history. The shell-level provider cannot know the record's permissions, so a read-only form nests its own `<UndoProvider canEdit={false}>` to shadow it. A provider nested inside a window puts its own pair in the footer on the same footer terms — it joins a footer that is there for other reasons, never creates one or holds it open, and shows nothing on the mobile chrome — but its `canEdit` takes the place of the editing check: the form that nested it has already said whether the record may be edited, so pass `canEdit={isEditing}` (or the lock) rather than leaving the default. |
|
|
956
956
|
| `Calendar` | Month grid with full keyboard navigation (arrows, Home/End, PageUp/Down) and `role="grid"` semantics. Single or range. Days of the months either side are quieter than the month's own but keep AA contrast: they are dates a person can press. The shared grid behind `DatePicker` and `DateRangePicker`. `weekStartsOn` defaults to the provider locale's first day of the week (`firstDayOfWeek`), Sunday without one. |
|
|
957
957
|
| `DashboardTemplate`, `DataTablePage`, `FormLayoutPage`, `CheckoutTemplate`, `EmailTemplate`, `ChatTemplate`, `GalleryTemplate`, `AuthScreen`, `ErrorPage` | Zero-prop starter page templates composed from the primitives. |
|
|
@@ -1007,7 +1007,7 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
|
|
|
1007
1007
|
| `utilityApps`, `documentApps`, `webApps` | Subsets of `bundledApps`. |
|
|
1008
1008
|
| `Calculator`, `Spreadsheet`, `Weather`, `CurrencyConverter`, `PomodoroTimer`, `TodoList`, `Browser` | Lazy components — use directly in custom registry entries. |
|
|
1009
1009
|
| `PdfViewer` | Lazy component — the PDF reader on its own, for embedding. See below. |
|
|
1010
|
-
| `BUILTIN_APP_INFO` | Per-app metadata for the document/web apps (Spreadsheets, Notepad, Documents, Preview, Files, Browser): display name, independent app version and one-line description. Drives each app's "About" dialog (window title menu → About <App>), which also shows the shell version. |
|
|
1010
|
+
| `BUILTIN_APP_INFO` | Per-app metadata for the document/web apps (Spreadsheets, Notepad, Documents, Preview, Files, Browser): display name, independent app version and one-line description. Drives each app's "About" dialog (window title menu → About <App>), which also shows the shell version. The dialog's words, and each app's description, are the strings catalog's `aboutApp` section; `description` here is its English default. |
|
|
1011
1011
|
|
|
1012
1012
|
#### An embedded PDF — `PdfViewer`
|
|
1013
1013
|
|
|
@@ -1695,6 +1695,94 @@ const { language, languages, setLanguage } = useShellLanguage();
|
|
|
1695
1695
|
except after a language change, when the tab takes its route's title until
|
|
1696
1696
|
its page mounts and titles it again.
|
|
1697
1697
|
|
|
1698
|
+
#### Shipped languages — `efficient-shell/locales/zh-Hans`
|
|
1699
|
+
|
|
1700
|
+
The shell ships its own catalogue for Simplified Chinese (mainland usage), so
|
|
1701
|
+
its window controls, pagers and dialogs read the same in every host:
|
|
1702
|
+
|
|
1703
|
+
```tsx
|
|
1704
|
+
import { zhHansStrings } from 'efficient-shell/locales/zh-Hans';
|
|
1705
|
+
|
|
1706
|
+
<ShellLanguageProvider languages={LANGUAGES} language="zh-Hans" strings={zhHansStrings()}>
|
|
1707
|
+
<App />
|
|
1708
|
+
</ShellLanguageProvider>
|
|
1709
|
+
|
|
1710
|
+
// or, without the language provider:
|
|
1711
|
+
<ShellStringsProvider value={zhHansStrings()} locale="zh-Hans">…</ShellStringsProvider>
|
|
1712
|
+
```
|
|
1713
|
+
|
|
1714
|
+
- **It is a draft.** No native speaker has reviewed it yet.
|
|
1715
|
+
`ZH_HANS_REVIEWED_SECTIONS` (same subpath) lists the catalogue sections one
|
|
1716
|
+
has signed off, and it is empty: the whole catalogue is draft. The shell only
|
|
1717
|
+
reports this; whether to offer the language meanwhile is the host's call.
|
|
1718
|
+
- **It is the whole catalogue.** `zhHansStrings(locale = 'zh-Hans')` returns a
|
|
1719
|
+
complete `ShellStrings`, the word-order entries included, so no key falls
|
|
1720
|
+
back to English. Its counts are written in `locale`. A key you want said
|
|
1721
|
+
differently is one spread away: `{ ...zh, dialog: { ...zh.dialog, ok: '好' } }`.
|
|
1722
|
+
- **A caller's words are spaced the Chinese way.** A label or a name you pass
|
|
1723
|
+
in is closed up against Chinese and set off by a space when it is Latin
|
|
1724
|
+
(`复制采购订单号`, `复制 SO-0041`), so a host part-way through its own
|
|
1725
|
+
translation reads correctly in both.
|
|
1726
|
+
- **What stays in Latin letters** is notation: file formats (`PDF`, `CSV`,
|
|
1727
|
+
`DXF`), key names (`Esc`, `Enter`), units, the drawing panel's commands and
|
|
1728
|
+
package names.
|
|
1729
|
+
- **Nothing loads it by accident.** The subpath is its own entry with no React
|
|
1730
|
+
and no peer, safe to call from server code, and neither `efficient-shell` nor
|
|
1731
|
+
`efficient-shell/ui` imports it: a host that offers one language downloads
|
|
1732
|
+
none of it. `createShellI18n` (below) fetches it with a dynamic import when
|
|
1733
|
+
`zh-Hans` is activated.
|
|
1734
|
+
|
|
1735
|
+
#### Your own copy — `efficient-shell/i18n`
|
|
1736
|
+
|
|
1737
|
+
The catalog above is the shell's words. A host's own copy (its column
|
|
1738
|
+
headings, page titles, toasts) goes through [i18next](https://www.i18next.com/),
|
|
1739
|
+
and this entry sets it up the same way in every host, driven by the same
|
|
1740
|
+
language. `i18next` and `react-i18next` are optional peers of this entry alone:
|
|
1741
|
+
a host that offers one language installs neither.
|
|
1742
|
+
|
|
1743
|
+
```ts
|
|
1744
|
+
// src/i18n/index.ts — module scope
|
|
1745
|
+
import { createShellI18n } from 'efficient-shell/i18n';
|
|
1746
|
+
import common from '../locales/en/common.json';
|
|
1747
|
+
import orders from '../locales/en/orders.json';
|
|
1748
|
+
|
|
1749
|
+
export const { i18n, t, activate } = createShellI18n({
|
|
1750
|
+
copy: { common, orders }, // English, bundled
|
|
1751
|
+
load: language => import(`../locales/${language}/index.ts`).then(m => m.default),
|
|
1752
|
+
// loadShellStrings: omitted, the shell loads the catalogue it ships for the language
|
|
1753
|
+
});
|
|
1754
|
+
```
|
|
1755
|
+
|
|
1756
|
+
```ts
|
|
1757
|
+
// boot, BEFORE the app is imported
|
|
1758
|
+
const language = resolveShellLanguage({ offered: LANGUAGES });
|
|
1759
|
+
const activation = await activate(language); // never rejects
|
|
1760
|
+
const { default: App } = await import('./App');
|
|
1761
|
+
// <ShellLanguageProvider language={activation.language} strings={activation.strings} …>
|
|
1762
|
+
```
|
|
1763
|
+
|
|
1764
|
+
- **`t` answers at once.** `createShellI18n` initialises synchronously with the
|
|
1765
|
+
source-language copy, so `t('orders:title')` is English from the next line. A
|
|
1766
|
+
unit test that imports a column set gets English with no setup.
|
|
1767
|
+
- **`activate(language)`** loads that language's copy and the shell's catalog
|
|
1768
|
+
together, then switches `t`. A key the language lacks falls back to the
|
|
1769
|
+
source language. If either load fails the page stays in the source language
|
|
1770
|
+
and the result says `failed: true`; it never rejects into a blank page.
|
|
1771
|
+
- **The shell's catalogue comes by default.** With no `loadShellStrings`,
|
|
1772
|
+
`activate` loads the catalogue the shell ships for the language
|
|
1773
|
+
(`efficient-shell/locales/zh-Hans`, lazily) and hands it back as
|
|
1774
|
+
`activation.strings`; a language the shell does not ship activates with
|
|
1775
|
+
`strings: undefined` and the shell's own words stay in English. Pass
|
|
1776
|
+
`loadShellStrings` to supply your own instead: it wins.
|
|
1777
|
+
- **Import the app after `activate`.** Module-level constants call `t` when
|
|
1778
|
+
they are imported and keep what it said, so copy read before `activate` stays
|
|
1779
|
+
in the source language for the life of the page. `activate` reports that on
|
|
1780
|
+
the console when it happens.
|
|
1781
|
+
- **`useTranslation`** from `react-i18next` reads the same instance, with no
|
|
1782
|
+
provider to mount. Type your keys the i18next way, by augmenting
|
|
1783
|
+
`CustomTypeOptions` with your English resources, and a misspelt key stops
|
|
1784
|
+
compiling.
|
|
1785
|
+
|
|
1698
1786
|
**The windowed shell's lists, records and forms are in the catalog too.**
|
|
1699
1787
|
`EntityList`'s row menu and its copy and export toasts (`entityList`),
|
|
1700
1788
|
`ResizableTable`'s column chooser (`columns`), a record window with no record
|
|
@@ -1705,15 +1793,35 @@ and event names (`sounds`). Their own props — `submitLabel`, `doneLabel`, the
|
|
|
1705
1793
|
PDF button's `label`, a list's `errorTitle` — still win, and the keys a sound
|
|
1706
1794
|
scheme is stored under are identifiers, never translated.
|
|
1707
1795
|
|
|
1796
|
+
**The status and file surfaces are in the catalog too.**
|
|
1797
|
+
`ServerStatusIndicator`'s tooltip and card (`serverStatus`), `BulkImportGrid`
|
|
1798
|
+
with its column mapper and both duplicate reviews (`bulkImport`), the Preview
|
|
1799
|
+
window (`preview`) with its drawing panel and command bar (`drawing`) and its
|
|
1800
|
+
3D panel (`model`), `ImageAnnotator` (`annotator`), what `openAttachment` says
|
|
1801
|
+
when a stored file cannot be shown in a window (`attachment`) and a bundled
|
|
1802
|
+
app's About dialog (`aboutApp`). The PDF panel was already `pdf`. A caller's own
|
|
1803
|
+
words still win: the bulk grid's `description`, a staged `convertingMessage`,
|
|
1804
|
+
`openAttachment`'s `loadingMessage`, a fetch error's own message. The times on
|
|
1805
|
+
the status card and the totals over the bulk grid are written in `locale`, and
|
|
1806
|
+
exactly as before without one.
|
|
1807
|
+
|
|
1708
1808
|
**What is not in the catalog, on purpose:** month and weekday names (the
|
|
1709
1809
|
`locale` supplies them); file-format names in `BrandAssetEditor`'s default
|
|
1710
1810
|
`acceptHint` and the `20ft`/`40ft` container codes (notation, not language); the
|
|
1711
1811
|
`Kbd` shortcut constants (key symbols); `STAGES` labels (data a consumer's own
|
|
1712
1812
|
tables read); the sample content of the starter templates (`DashboardTemplate`,
|
|
1713
1813
|
`AuthScreen` and the rest are demos to copy, not components to configure); and
|
|
1714
|
-
console warnings, which are for developers.
|
|
1715
|
-
|
|
1716
|
-
|
|
1814
|
+
console warnings, which are for developers. In the Preview window the same
|
|
1815
|
+
goes for file extensions and the two optional packages' names, the drawing
|
|
1816
|
+
panel's command names (`DI`, `DIM`, `H`, `V`, `Z`, `LA`: what is typed is the
|
|
1817
|
+
same in every language) and its `ΔX`/`ΔY`, the axis letters and `⊥` of the 3D
|
|
1818
|
+
panel, a measurement's unit (`18.56 mm`, `1.250 m`) and the status card's
|
|
1819
|
+
latency (`42 ms`), a colour swatch's hex code, and the names of the files a
|
|
1820
|
+
snapshot or an annotated image is saved under. A bundled app's NAME is its
|
|
1821
|
+
window-registry label and is not in the catalog either; its one-line
|
|
1822
|
+
description is.
|
|
1823
|
+
|
|
1824
|
+
**The full catalog** — 64 sections, 781 keys. A function's default is shown
|
|
1717
1825
|
with sample arguments.
|
|
1718
1826
|
|
|
1719
1827
|
<details><summary><code>window</code> — 17 keys</summary>
|
|
@@ -2633,6 +2741,318 @@ with sample arguments.
|
|
|
2633
2741
|
|
|
2634
2742
|
</details>
|
|
2635
2743
|
|
|
2744
|
+
<details><summary><code>serverStatus</code> — 27 keys</summary>
|
|
2745
|
+
|
|
2746
|
+
| Key | Type | English default |
|
|
2747
|
+
|---|---|---|
|
|
2748
|
+
| `connected` | `string` | `Connected` |
|
|
2749
|
+
| `disconnected` | `string` | `Disconnected` |
|
|
2750
|
+
| `checking` | `string` | `Checking…` |
|
|
2751
|
+
| `lastOkTitle` | `(status: string, time: string) => string` | `{status} · last ok {time}` |
|
|
2752
|
+
| `details` | `string` | `Server connection details` |
|
|
2753
|
+
| `lastPoll` | `(ago: string) => string` | `Last poll {ago}.` |
|
|
2754
|
+
| `lastFailure` | `(ago: string) => string` | `Last failure {ago}.` |
|
|
2755
|
+
| `firstPoll` | `string` | `First poll in flight…` |
|
|
2756
|
+
| `server` | `string` | `Server` |
|
|
2757
|
+
| `protocol` | `string` | `Protocol` |
|
|
2758
|
+
| `browser` | `string` | `Browser` |
|
|
2759
|
+
| `online` | `string` | `Online` |
|
|
2760
|
+
| `offline` | `string` | `Offline` |
|
|
2761
|
+
| `lastOk` | `string` | `Last OK` |
|
|
2762
|
+
| `lastFail` | `string` | `Last fail` |
|
|
2763
|
+
| `latency` | `string` | `Latency` |
|
|
2764
|
+
| `poll` | `string` | `Poll` |
|
|
2765
|
+
| `pollEvery` | `(seconds: number) => string` | `every 3 s` |
|
|
2766
|
+
| `session` | `string` | `Session` |
|
|
2767
|
+
| `user` | `string` | `User` |
|
|
2768
|
+
| `notSignedIn` | `string` | `Not signed in` |
|
|
2769
|
+
| `role` | `string` | `Role` |
|
|
2770
|
+
| `build` | `string` | `Build` |
|
|
2771
|
+
| `app` | `string` | `App` |
|
|
2772
|
+
| `userAgent` | `string` | `UA` |
|
|
2773
|
+
| `pingHint` | `string` | `Click ↻ to ping now` |
|
|
2774
|
+
| `refresh` | `string` | `Refresh` |
|
|
2775
|
+
|
|
2776
|
+
</details>
|
|
2777
|
+
|
|
2778
|
+
<details><summary><code>bulkImport</code> — 29 keys</summary>
|
|
2779
|
+
|
|
2780
|
+
| Key | Type | English default |
|
|
2781
|
+
|---|---|---|
|
|
2782
|
+
| `description` | `string` | `Paste from spreadsheets, type directly, or upload a CSV. Rows don't need to be in order — they'll be matched. Values not in the list will be added to the end. Fields left blank will not be affected.` |
|
|
2783
|
+
| `lines` | `(count: number) => string` | `3 lines` |
|
|
2784
|
+
| `total` | `(label: string, value: string) => string` | `{label}: {value}` |
|
|
2785
|
+
| `clear` | `string` | `Clear` |
|
|
2786
|
+
| `upload` | `string` | `Upload CSV` |
|
|
2787
|
+
| `importLines` | `(count: number) => string` | `Import 3 Lines` |
|
|
2788
|
+
| `importing` | `string` | `Importing...` |
|
|
2789
|
+
| `importFailed` | `string` | `Import failed.` |
|
|
2790
|
+
| `enterAtLeastOne` | `(noun: string, title: string) => string` | `Enter at least one {noun}.` (`Enter at least one value.` with no title) |
|
|
2791
|
+
| `allSkipped` | `string` | `All rows skipped.` |
|
|
2792
|
+
| `cancel` | `string` | `Cancel` |
|
|
2793
|
+
| `mapTitle` | `string` | `Map CSV columns to fields` |
|
|
2794
|
+
| `skip` | `string` | `— Skip —` |
|
|
2795
|
+
| `columnOption` | `(column: number, sample: string) => string` | `Column 3 (e.g. {sample})` |
|
|
2796
|
+
| `rowsDetected` | `(count: number) => string` | `3 rows detected` |
|
|
2797
|
+
| `confirmMapping` | `string` | `Confirm Mapping` |
|
|
2798
|
+
| `duplicatesTitle` | `string` | `Duplicate entries detected` |
|
|
2799
|
+
| `duplicatesFound` | `(count: number) => string` | `3 values appear more than once in your import. Choose how to handle each before proceeding.` (`1 value appears…` for one) |
|
|
2800
|
+
| `occurrences` | `(count: number) => string` | `3 occurrences` |
|
|
2801
|
+
| `keepFirst` | `string` | `Keep first` |
|
|
2802
|
+
| `keepLast` | `string` | `Keep last` |
|
|
2803
|
+
| `keepAll` | `string` | `Keep all` |
|
|
2804
|
+
| `skipAll` | `string` | `Skip all` |
|
|
2805
|
+
| `keptDropped` | `(kept: number, dropped: number) => string` | `3 kept · 3 dropped` |
|
|
2806
|
+
| `proceed` | `string` | `Proceed` |
|
|
2807
|
+
| `duplicatesMerged` | `(count: number) => string` | `3 values appeared more than once. Each is merged into a single line with its quantity summed — review before proceeding.` (`1 value appeared…` for one) |
|
|
2808
|
+
| `mergedFrom` | `string` | `Merged from` |
|
|
2809
|
+
| `mergedRows` | `(count: number) => string` | `3 rows` |
|
|
2810
|
+
| `mergeSummary` | `(rows: number, lines: number) => string` | `3 duplicate rows → 3 merged lines` (`… → 1 merged line` for one) |
|
|
2811
|
+
|
|
2812
|
+
</details>
|
|
2813
|
+
|
|
2814
|
+
<details><summary><code>preview</code> — 29 keys</summary>
|
|
2815
|
+
|
|
2816
|
+
| Key | Type | English default |
|
|
2817
|
+
|---|---|---|
|
|
2818
|
+
| `windowTitle` | `(name: string) => string` | `{name} - Preview` |
|
|
2819
|
+
| `untitled` | `string` | `Untitled` |
|
|
2820
|
+
| `open` | `string` | `Open` |
|
|
2821
|
+
| `formats` | `string` | `PDF · DXF · 3D · Images` |
|
|
2822
|
+
| `dropHere` | `(open: string) => string` | `Drop a file here, or click {open}.` |
|
|
2823
|
+
| `supportedFormats` | `string` | `Supported formats` |
|
|
2824
|
+
| `formatPdf` | `string` | `multi-page document viewer` |
|
|
2825
|
+
| `formatDxf` | `(packageName: string) => string` | `vector CAD drawings (optional {packageName} peer dep)` |
|
|
2826
|
+
| `format3d` | `(packageName: string) => string` | `3D models (optional {packageName} peer dep)` |
|
|
2827
|
+
| `formatImage` | `string` | `raster images` |
|
|
2828
|
+
| `dwgNote` | `string` | `DWG files need to be converted to PDF or DXF first (server-side).` |
|
|
2829
|
+
| `dropToOpen` | `string` | `Drop to open` |
|
|
2830
|
+
| `dwgUnsupported` | `string` | `DWG files need server-side conversion. Convert to PDF or DXF first.` |
|
|
2831
|
+
| `unsupportedType` | `(extension: string) => string` | `Unsupported file type: .{extension}` (`.unknown` with none) |
|
|
2832
|
+
| `converting` | `string` | `Converting file` |
|
|
2833
|
+
| `download` | `string` | `Download` |
|
|
2834
|
+
| `email` | `string` | `Email` |
|
|
2835
|
+
| `howToNavigate` | `string` | `How to navigate` |
|
|
2836
|
+
| `stopMeasuring` | `string` | `Stop measuring (Esc)` |
|
|
2837
|
+
| `scrollToZoom` | `string` | `Scroll to zoom` |
|
|
2838
|
+
| `closePanel` | `string` | `Close` |
|
|
2839
|
+
| `annotate` | `string` | `Annotate` |
|
|
2840
|
+
| `annotateHint` | `string` | `Annotate this image` |
|
|
2841
|
+
| `view` | `string` | `View` |
|
|
2842
|
+
| `viewHint` | `string` | `Back to viewer` |
|
|
2843
|
+
| `copy` | `string` | `Copy` |
|
|
2844
|
+
| `save` | `string` | `Save` |
|
|
2845
|
+
| `imageFailed` | `string` | `Failed to load image.` |
|
|
2846
|
+
| `downloadFailed` | `string` | `Download failed — couldn't reach the file.` |
|
|
2847
|
+
|
|
2848
|
+
</details>
|
|
2849
|
+
|
|
2850
|
+
<details><summary><code>drawing</code> — 49 keys</summary>
|
|
2851
|
+
|
|
2852
|
+
| Key | Type | English default |
|
|
2853
|
+
|---|---|---|
|
|
2854
|
+
| `loading` | `string` | `Loading drawing…` |
|
|
2855
|
+
| `notInstalled` | `string` | `dxf-viewer is not installed in this app.` |
|
|
2856
|
+
| `renderFailed` | `string` | `Failed to render DXF.` |
|
|
2857
|
+
| `layers` | `string` | `Layers` |
|
|
2858
|
+
| `layersHint` | `string` | `Toggle layer visibility` |
|
|
2859
|
+
| `allLayers` | `string` | `All` |
|
|
2860
|
+
| `noLayers` | `string` | `None` |
|
|
2861
|
+
| `measure` | `string` | `Measure` |
|
|
2862
|
+
| `measureHint` | `string` | `Measure distance — click two points on the drawing, or type DI / DIM below` |
|
|
2863
|
+
| `auto` | `string` | `Auto` |
|
|
2864
|
+
| `autoHint` | `string` | `Auto — AutoCAD DIMLINEAR: measures ΔX or ΔY, whichever is larger between the two picks` |
|
|
2865
|
+
| `horizontalHint` | `string` | `Horizontal — distance along the X axis between two picks (AutoCAD DIMLINEAR horizontal)` |
|
|
2866
|
+
| `verticalHint` | `string` | `Vertical — distance along the Y axis between two picks (AutoCAD DIMLINEAR vertical)` |
|
|
2867
|
+
| `point` | `string` | `Point` |
|
|
2868
|
+
| `pointHint` | `string` | `Point — straight-line (Euclidean) distance between two picks (AutoCAD DIST)` |
|
|
2869
|
+
| `fixX` | `string` | `fix Δx` |
|
|
2870
|
+
| `fixY` | `string` | `fix Δy` |
|
|
2871
|
+
| `fixHint` | `string` | `Lock the second pick's axis coord to first pick + this value (mm). The reported measurement becomes the perpendicular distance.` |
|
|
2872
|
+
| `clearFixed` | `string` | `Clear fixed distance` |
|
|
2873
|
+
| `horizontalDistance` | `(auto: boolean) => string` | `Horizontal distance (Δx) between the two picked points` (plus ` — Auto resolved to the X axis` when `auto`) |
|
|
2874
|
+
| `verticalDistance` | `(auto: boolean) => string` | `Vertical distance (Δy) between the two picked points` (plus ` — Auto resolved to the Y axis` when `auto`) |
|
|
2875
|
+
| `straightDistance` | `string` | `Straight-line distance between the two picked points` |
|
|
2876
|
+
| `fit` | `string` | `Fit` |
|
|
2877
|
+
| `fitHint` | `string` | `Fit drawing to view` |
|
|
2878
|
+
| `dragToPan` | `string` | `Drag to pan` |
|
|
2879
|
+
| `fitToReset` | `string` | `Fit to reset` |
|
|
2880
|
+
| `typeToMeasure` | `(commands: string) => string` | `Type {commands} to measure` |
|
|
2881
|
+
| `commandPlaceholder` | `string` | `Type a command — DI, DIM, H, V, Z, LA (? for help)` |
|
|
2882
|
+
| `commandHelp` | `string` | `Commands: DI distance · DIM/DLI linear dim · H / V force axis · <number> lock Δ · U undo pick · Z fit · LA layers · Esc exit` |
|
|
2883
|
+
| `echoDistance` | `(distance: string, deltaX: string, deltaY: string) => string` | `Distance = {distance} ΔX = {deltaX} ΔY = {deltaY}` |
|
|
2884
|
+
| `echoLinear` | `(distance: string, axis: string) => string` | `Linear dimension = {distance} ({axis})` |
|
|
2885
|
+
| `echoLocked` | `(axis: string, distance: string, lockedAxis: string, locked: string) => string` | `{axis} = {distance} with {lockedAxis} locked to {locked}` |
|
|
2886
|
+
| `axisLocked` | `(axis: string, distance: string) => string` | `{axis} locked to {distance} — the label shows the perpendicular distance` |
|
|
2887
|
+
| `fixedCleared` | `string` | `Fixed distance cleared.` |
|
|
2888
|
+
| `fixedNeedsAxis` | `string` | `Fixed distances apply in H or V mode — type H or V first.` |
|
|
2889
|
+
| `echoDist` | `string` | `DIST — click two points; straight-line distance (Esc to exit)` |
|
|
2890
|
+
| `echoDimLinear` | `string` | `DIMLINEAR — click two points; measures ΔX or ΔY, whichever is larger (H / V to force)` |
|
|
2891
|
+
| `echoHorizontal` | `string` | `Horizontal — ΔX between the two picks` |
|
|
2892
|
+
| `echoVertical` | `string` | `Vertical — ΔY between the two picks` |
|
|
2893
|
+
| `echoAuto` | `string` | `Auto (DIMLINEAR) — measures along the dominant axis of the two picks` |
|
|
2894
|
+
| `measureOn` | `string` | `Measure on — click two points.` |
|
|
2895
|
+
| `measureOff` | `string` | `Measure off.` |
|
|
2896
|
+
| `pickRemoved` | `string` | `Last pick removed.` |
|
|
2897
|
+
| `zoomExtents` | `string` | `Zoom extents.` |
|
|
2898
|
+
| `layersToggled` | `string` | `Layer panel toggled.` |
|
|
2899
|
+
| `measurementCleared` | `string` | `Measurement cleared.` |
|
|
2900
|
+
| `readOnly` | `(command: string) => string` | `{command} is a drawing command — Preview is a read-only viewer. Measuring: DI, DIM, H, V (? for help)` |
|
|
2901
|
+
| `unknownCommand` | `(command: string) => string` | `Unknown command "{command}" — try DI, DIM, H, V, Z, LA (? for help)` |
|
|
2902
|
+
| `cancelled` | `string` | `*Cancel*` |
|
|
2903
|
+
|
|
2904
|
+
</details>
|
|
2905
|
+
|
|
2906
|
+
<details><summary><code>model</code> — 53 keys</summary>
|
|
2907
|
+
|
|
2908
|
+
| Key | Type | English default |
|
|
2909
|
+
|---|---|---|
|
|
2910
|
+
| `loading` | `string` | `Loading 3D model…` |
|
|
2911
|
+
| `stepNote` | `string` | `STEP files load OpenCascade WASM (~5 MB) on first use.` |
|
|
2912
|
+
| `notInstalled` | `string` | `online-3d-viewer is not installed in this app. Add it to enable 3D file viewing.` |
|
|
2913
|
+
| `loadFailed` | `string` | `Failed to load 3D model.` |
|
|
2914
|
+
| `rootNode` | `string` | `Root` |
|
|
2915
|
+
| `node` | `string` | `Node` |
|
|
2916
|
+
| `collapse` | `string` | `Collapse` |
|
|
2917
|
+
| `expand` | `string` | `Expand` |
|
|
2918
|
+
| `fitToView` | `string` | `Fit to view` |
|
|
2919
|
+
| `hide` | `string` | `Hide` |
|
|
2920
|
+
| `show` | `string` | `Show` |
|
|
2921
|
+
| `isometric` | `string` | `ISO` |
|
|
2922
|
+
| `isometricView` | `string` | `Isometric view` |
|
|
2923
|
+
| `top` | `string` | `TOP` |
|
|
2924
|
+
| `topView` | `string` | `Top view` |
|
|
2925
|
+
| `front` | `string` | `FRT` |
|
|
2926
|
+
| `frontView` | `string` | `Front view` |
|
|
2927
|
+
| `side` | `string` | `SDE` |
|
|
2928
|
+
| `sideView` | `string` | `Side view` |
|
|
2929
|
+
| `perspective` | `string` | `Perspective` |
|
|
2930
|
+
| `orthographic` | `string` | `Orthographic` |
|
|
2931
|
+
| `toPerspective` | `string` | `Switch to perspective view` |
|
|
2932
|
+
| `toOrthographic` | `string` | `Switch to orthographic view` |
|
|
2933
|
+
| `snapshot` | `string` | `Save snapshot as PNG` |
|
|
2934
|
+
| `measureHint` | `string` | `Measure distance — click two points on the model` |
|
|
2935
|
+
| `point` | `string` | `Point` |
|
|
2936
|
+
| `pointHint` | `string` | `Point — measure straight-line distance between two picked points` |
|
|
2937
|
+
| `perpendicularHint` | `string` | `Perpendicular — pick two surfaces and measure the perpendicular gap between them` |
|
|
2938
|
+
| `straightDistance` | `string` | `Straight-line distance between the two picked points` |
|
|
2939
|
+
| `perpendicularDistance` | `string` | `Perpendicular distance between the two picked surfaces` |
|
|
2940
|
+
| `meshesHint` | `string` | `Toggle meshes panel` |
|
|
2941
|
+
| `displayHint` | `string` | `Toggle display panel` |
|
|
2942
|
+
| `downloadOriginal` | `string` | `Download original file` |
|
|
2943
|
+
| `meshes` | `string` | `Meshes` |
|
|
2944
|
+
| `readingModel` | `string` | `Reading model…` |
|
|
2945
|
+
| `noStructure` | `string` | `No structure available` |
|
|
2946
|
+
| `allVisible` | `string` | `All visible` |
|
|
2947
|
+
| `hiddenCount` | `(count: number) => string` | `3 hidden` |
|
|
2948
|
+
| `dragToRotate` | `string` | `Drag to rotate` |
|
|
2949
|
+
| `rightDragToPan` | `string` | `Right-click drag to pan` |
|
|
2950
|
+
| `display` | `string` | `Model Display` |
|
|
2951
|
+
| `backgroundColor` | `string` | `Background Color` |
|
|
2952
|
+
| `showEdges` | `string` | `Show Edges` |
|
|
2953
|
+
| `edgeColor` | `string` | `Edge Color` |
|
|
2954
|
+
| `edgeThreshold` | `string` | `Edge Threshold` |
|
|
2955
|
+
| `sectionView` | `string` | `Section View` |
|
|
2956
|
+
| `angle` | `string` | `Angle` |
|
|
2957
|
+
| `axis` | `string` | `Axis` |
|
|
2958
|
+
| `flipHint` | `string` | `Flip section direction` |
|
|
2959
|
+
| `flip` | `string` | `flip →` |
|
|
2960
|
+
| `flipped` | `string` | `← flipped` |
|
|
2961
|
+
| `position` | `string` | `Position` |
|
|
2962
|
+
| `resetDisplay` | `string` | `Reset to Default` |
|
|
2963
|
+
|
|
2964
|
+
</details>
|
|
2965
|
+
|
|
2966
|
+
<details><summary><code>annotator</code> — 52 keys</summary>
|
|
2967
|
+
|
|
2968
|
+
| Key | Type | English default |
|
|
2969
|
+
|---|---|---|
|
|
2970
|
+
| `toolSelect` | `string` | `Select / Move` |
|
|
2971
|
+
| `toolDraw` | `string` | `Pen / Draw` |
|
|
2972
|
+
| `toolRect` | `string` | `Rectangle` |
|
|
2973
|
+
| `toolEllipse` | `string` | `Ellipse` |
|
|
2974
|
+
| `toolArrow` | `string` | `Arrow` |
|
|
2975
|
+
| `toolMosaic` | `string` | `Mosaic` |
|
|
2976
|
+
| `toolText` | `string` | `Text` |
|
|
2977
|
+
| `toolCrop` | `string` | `Crop` |
|
|
2978
|
+
| `weight` | `string` | `Weight` |
|
|
2979
|
+
| `radius` | `string` | `Radius` |
|
|
2980
|
+
| `size` | `string` | `Size` |
|
|
2981
|
+
| `padding` | `string` | `Pad` |
|
|
2982
|
+
| `fontSystem` | `string` | `System` |
|
|
2983
|
+
| `fontSerif` | `string` | `Serif` |
|
|
2984
|
+
| `fontMono` | `string` | `Mono` |
|
|
2985
|
+
| `fontCursive` | `string` | `Cursive` |
|
|
2986
|
+
| `bold` | `string` | `Bold` |
|
|
2987
|
+
| `boldLetter` | `string` | `B` |
|
|
2988
|
+
| `italic` | `string` | `Italic` |
|
|
2989
|
+
| `italicLetter` | `string` | `I` |
|
|
2990
|
+
| `underline` | `string` | `Underline` |
|
|
2991
|
+
| `underlineLetter` | `string` | `U` |
|
|
2992
|
+
| `border` | `string` | `Border` |
|
|
2993
|
+
| `borderColor` | `string` | `Text-box border color` |
|
|
2994
|
+
| `addBorder` | `string` | `Add a border` |
|
|
2995
|
+
| `removeBorder` | `string` | `Remove border` |
|
|
2996
|
+
| `fill` | `string` | `Fill` |
|
|
2997
|
+
| `fillColor` | `string` | `Text-box background color` |
|
|
2998
|
+
| `addFill` | `string` | `Add a background fill` |
|
|
2999
|
+
| `removeFill` | `string` | `Remove fill` |
|
|
3000
|
+
| `add` | `string` | `Add` |
|
|
3001
|
+
| `zoomOut` | `string` | `Zoom out` |
|
|
3002
|
+
| `zoomIn` | `string` | `Zoom in` |
|
|
3003
|
+
| `fit` | `string` | `Fit` |
|
|
3004
|
+
| `fitHint` | `string` | `Fit to area` |
|
|
3005
|
+
| `undo` | `string` | `Undo` |
|
|
3006
|
+
| `applyCrop` | `string` | `Apply Crop` |
|
|
3007
|
+
| `cancel` | `string` | `Cancel` |
|
|
3008
|
+
| `apply` | `string` | `Apply` |
|
|
3009
|
+
| `hintSelected` | `string` | `Drag to move. Drag a corner to resize. Delete / Backspace removes. Click outside to deselect.` |
|
|
3010
|
+
| `hintSelect` | `string` | `Tap a shape to select. Double-click text to edit.` |
|
|
3011
|
+
| `hintText` | `string` | `Click to drop a text label.` |
|
|
3012
|
+
| `hintCrop` | `string` | `Drag a rectangle to set the crop region.` |
|
|
3013
|
+
| `hintDraw` | `string` | `Drag to draw freehand.` |
|
|
3014
|
+
| `hintShape` | `string` | `Drag on the image to draw.` |
|
|
3015
|
+
| `textPlaceholder` | `string` | `Type then Enter…` |
|
|
3016
|
+
| `loadFailed` | `string` | `Failed to load image` |
|
|
3017
|
+
| `exportFailed` | `string` | `Failed to export` |
|
|
3018
|
+
| `clipboardUnsupported` | `string` | `Clipboard images not supported in this browser` |
|
|
3019
|
+
| `copyFailed` | `string` | `Failed to copy` |
|
|
3020
|
+
| `copied` | `string` | `Copied to clipboard` |
|
|
3021
|
+
| `copyRefused` | `string` | `Copy failed (clipboard permission?)` |
|
|
3022
|
+
|
|
3023
|
+
</details>
|
|
3024
|
+
|
|
3025
|
+
<details><summary><code>attachment</code> — 7 keys</summary>
|
|
3026
|
+
|
|
3027
|
+
| Key | Type | English default |
|
|
3028
|
+
|---|---|---|
|
|
3029
|
+
| `fallbackName` | `string` | `attachment` |
|
|
3030
|
+
| `noLink` | `string` | `This attachment has no download link.` |
|
|
3031
|
+
| `spreadsheetFailed` | `string` | `Could not open the spreadsheet — opening the file instead.` |
|
|
3032
|
+
| `workbookUnavailable` | `string` | `Spreadsheet preview is unavailable for this file type. Opening the file instead.` |
|
|
3033
|
+
| `noViewer` | `string` | `Opening this file outside the app — no viewer can show it here.` |
|
|
3034
|
+
| `loading` | `string` | `LOADING FILE` |
|
|
3035
|
+
| `loadFailed` | `string` | `Failed to load this file.` |
|
|
3036
|
+
|
|
3037
|
+
</details>
|
|
3038
|
+
|
|
3039
|
+
<details><summary><code>aboutApp</code> — 10 keys</summary>
|
|
3040
|
+
|
|
3041
|
+
| Key | Type | English default |
|
|
3042
|
+
|---|---|---|
|
|
3043
|
+
| `title` | `(name: string) => string` | `About {name}` |
|
|
3044
|
+
| `version` | `(version: string) => string` | `Version {version}` |
|
|
3045
|
+
| `partOf` | `(shell: string) => string` | `Part of the {shell} desktop environment` |
|
|
3046
|
+
| `shellVersion` | `(version: string) => string` | `shell v{version}` |
|
|
3047
|
+
| `spreadsheet` | `string` | `Multi-sheet spreadsheet editor with CSV / TSV import and export.` |
|
|
3048
|
+
| `notepad` | `string` | `Color-coded notes with checklists, desktop stickies and entity autolinking.` |
|
|
3049
|
+
| `documents` | `string` | `Letter-page editor for Word documents and plain-text files — images, lists, alignment.` |
|
|
3050
|
+
| `preview` | `string` | `Viewer for PDF documents, images, DXF drawings and 3D models — CAD-style measuring with object snap and an AutoCAD-like command bar.` |
|
|
3051
|
+
| `files` | `string` | `Personal file manager with folders, uploads, quota, trash and desktop shortcut folders.` |
|
|
3052
|
+
| `browser` | `string` | `Minimal web browser with bookmarks, history and navigation bar.` |
|
|
3053
|
+
|
|
3054
|
+
</details>
|
|
3055
|
+
|
|
2636
3056
|
### Brand surfaces — `efficient-shell/brand.css`
|
|
2637
3057
|
|
|
2638
3058
|
For output that is not a portal window — a report, a published artifact, a
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-
|
|
1
|
+
import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-CZK2VKIO.js";import{WindowTitle as r}from"./chunk-N6STBYXR.js";import"./chunk-DSSLOQDW.js";import"./chunk-KVOFUFPP.js";import"./chunk-AZURXTRB.js";import"./chunk-CIXQKCFA.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import{confirm as n}from"./chunk-ZAE6ROV7.js";import"./chunk-AA4TMQOS.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import"./chunk-J3LIIIHJ.js";import"./chunk-C3QOHMR6.js";import"./chunk-YKZHOXI4.js";import{useRef as a,useEffect as i,useState as s}from"react";import{jsxs as l,jsx as c,Fragment as m}from"react/jsx-runtime";var d="react-os-shell:browser-bookmarks",u="react-os-shell:browser-homepage",h="https://en.wikipedia.org/wiki/Main_Page",p=[{label:"Wikipedia",url:"https://en.wikipedia.org/wiki/Main_Page"},{label:"DevDocs",url:"https://devdocs.io"},{label:"Example",url:"https://example.com"}],g=new Set(["ac ad ae af ag ai al am ao aq ar as at au aw ax az ba bb bd be bf bg bh bi","bj bm bn bo br bs bt bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv","cw cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gd","ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il","im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc","li lk lr ls lt lu lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu","mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl","pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sk sl sm sn","so sr ss st su sv sx sy sz tc td tf tg th tj tk tl tm tn to tr tt tv tw tz","ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw","com net org edu gov mil int info biz name pro app dev xyz online site tech","store shop cloud design digital agency studio media news blog live life","world today email systems solutions services software page link space host","press wiki team group works careers finance capital energy academy"].join(" ").split(" "));function b(e){let t=e.trim();return t?/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:function(e){const t=/^([\w-]+\.)+([a-z]{2,})(\/.*)?$/i.exec(e);return null!==t&&g.has(t[2].toLowerCase())}(t)?"https://"+t:"https://duckduckgo.com/?q="+encodeURIComponent(t):""}var k=["duckduckgo.com","developer.mozilla.org","google.com","gmail.com","youtube.com","facebook.com","instagram.com","whatsapp.com","twitter.com","x.com","github.com","gitlab.com","linkedin.com","reddit.com","pinterest.com","amazon.com","amazon.ca","amazon.co.uk","apple.com","icloud.com","microsoft.com","outlook.com","live.com","office.com","netflix.com","spotify.com","paypal.com","stripe.com","chat.openai.com","chatgpt.com","claude.ai"];function f(){if("undefined"==typeof window)return p;try{const e=localStorage.getItem(d);if(!e)return p;const t=JSON.parse(e);if(Array.isArray(t)&&t.every(e=>"string"==typeof e?.url))return t}catch{}return p}function x(){return"undefined"==typeof window?h:localStorage.getItem(u)||h}function w(){const m=a(void 0);void 0===m.current&&(m.current=e()),i(()=>{null!=m.current&&t(m.current)},[]);const h=m.current?b(m.current.url):"",[p,g]=s(x),[k,w]=s(h||p),[N,L]=s(k),[j,C]=s([k]),[z,M]=s(0),[R,H]=s(0),[S,W]=s(f),[A,O]=s(!1),[B,I]=s(null),T=a(null),E=a(null);i(()=>{try{localStorage.setItem(d,JSON.stringify(S))}catch{}},[S]);const D=e=>{const t=b(e);t&&(w(t),L(t),C(e=>{const o=e.slice(0,z+1);return o.push(t),o}),M(e=>e+1),O(!1))},F=()=>{"undefined"!=typeof window&&window.open(k,"_blank","noopener,noreferrer")},X=()=>{const e=B?.trim();e?(W(t=>[...t,{label:e,url:k}]),I(null)):I(null)};i(()=>{if(null===B)return;T.current?.focus(),T.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||I(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==B]);const P=e=>{W(t=>t.filter((t,o)=>o!==e))},U=z>0,V=z<j.length-1,K=S.some(e=>e.url===k),J="p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30";/* @__PURE__ */
|
|
2
2
|
return l("div",{className:"relative flex flex-col h-full bg-white",children:[
|
|
3
3
|
/* @__PURE__ */c(r,{title:`Browser - ${v(k)}`}),
|
|
4
4
|
/* @__PURE__ */c(o,{app:"browser"}),
|
|
5
5
|
/* @__PURE__ */l("div",{className:"flex items-center gap-1 px-2 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0",children:[
|
|
6
|
-
/* @__PURE__ */c("button",{onClick:()=>{if(z>0){const e=z-1;M(e),w(
|
|
7
|
-
/* @__PURE__ */c("button",{onClick:()=>{if(z<
|
|
8
|
-
/* @__PURE__ */c("button",{onClick:()=>
|
|
9
|
-
/* @__PURE__ */c("button",{onClick:()=>
|
|
10
|
-
/* @__PURE__ */c("form",{onSubmit:e=>{e.preventDefault(),
|
|
6
|
+
/* @__PURE__ */c("button",{onClick:()=>{if(z>0){const e=z-1;M(e),w(j[e]),L(j[e]),O(!1)}},disabled:!U,className:J,title:"Back",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.75 19.5L8.25 12l7.5-7.5"})})}),
|
|
7
|
+
/* @__PURE__ */c("button",{onClick:()=>{if(z<j.length-1){const e=z+1;M(e),w(j[e]),L(j[e]),O(!1)}},disabled:!V,className:J,title:"Forward",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8.25 4.5l7.5 7.5-7.5 7.5"})})}),
|
|
8
|
+
/* @__PURE__ */c("button",{onClick:()=>H(e=>e+1),className:J,title:"Refresh",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M16.023 9.348h4.992V4.356M2.985 19.644v-4.992h4.992M3.05 9.348a9 9 0 0114.85-3.36L21.015 9.348m0 5.304a9 9 0 01-14.85 3.36l-3.115-3.36"})})}),
|
|
9
|
+
/* @__PURE__ */c("button",{onClick:()=>D(p),className:J,title:"Home",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.25 12l8.954-8.955a1.5 1.5 0 012.122 0l8.954 8.955M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"})})}),
|
|
10
|
+
/* @__PURE__ */c("form",{onSubmit:e=>{e.preventDefault(),D(N)},className:"flex-1 flex items-center mx-1",children:/* @__PURE__ */l("div",{className:"flex items-center w-full bg-white border border-gray-300 rounded focus-within:border-blue-400 focus-within:ring-1 focus-within:ring-blue-200",children:[
|
|
11
11
|
/* @__PURE__ */c("span",{className:"px-2 text-gray-400 text-xs",children:k.startsWith("https://")?"🔒":"⚠️"}),
|
|
12
12
|
/* @__PURE__ */c("input",{type:"text",value:N,onChange:e=>L(e.target.value),onFocus:e=>e.target.select(),className:"flex-1 px-1 py-1 text-sm bg-transparent outline-none font-mono",spellCheck:!1,placeholder:"Enter URL or search…"}),k!==N&&/* @__PURE__ */c("button",{type:"button",onClick:()=>L(k),className:"px-1.5 text-[10px] text-gray-400 hover:text-gray-700",title:"Reset to current URL",children:"×"})]})}),
|
|
13
|
-
/* @__PURE__ */c("button",{"data-bookmark-popover":!0,onClick:()=>
|
|
14
|
-
/* @__PURE__ */c("button",{onClick:
|
|
15
|
-
/* @__PURE__ */c("button",{onClick:()=>O(e=>!e),className:
|
|
13
|
+
/* @__PURE__ */c("button",{"data-bookmark-popover":!0,onClick:()=>K?P(S.findIndex(e=>e.url===k)):void I(v(k)),className:J+" "+(K?"text-yellow-500":""),title:K?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:K?"currentColor":"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z"})})}),
|
|
14
|
+
/* @__PURE__ */c("button",{onClick:F,className:J,title:"Open in new tab",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-7.5-9L21 3m0 0v6m0-6L9.75 14.25"})})}),
|
|
15
|
+
/* @__PURE__ */c("button",{onClick:()=>O(e=>!e),className:J,title:"Embedding help",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z"})})})]}),null!==B&&/* @__PURE__ */l("div",{"data-bookmark-popover":!0,className:"absolute right-3 top-12 z-30 w-72 bg-white border border-gray-200 rounded-md shadow-lg p-3",children:[
|
|
16
16
|
/* @__PURE__ */c("div",{className:"text-[11px] font-semibold uppercase tracking-wide text-gray-500 mb-1",children:"Bookmark this page"}),
|
|
17
17
|
/* @__PURE__ */c("div",{className:"text-[11px] text-gray-400 truncate mb-2 font-mono",title:k,children:k}),
|
|
18
|
-
/* @__PURE__ */c("input",{ref:
|
|
18
|
+
/* @__PURE__ */c("input",{ref:T,type:"text",value:B,onChange:e=>I(e.target.value),onKeyDown:e=>{"Enter"===e.key?X():"Escape"===e.key&&I(null)},placeholder:"Name",className:"w-full text-sm px-2 py-1.5 border border-gray-300 rounded focus:outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-200"}),
|
|
19
19
|
/* @__PURE__ */l("div",{className:"flex justify-end gap-1 mt-2",children:[
|
|
20
|
-
/* @__PURE__ */c("button",{onClick:()=>
|
|
21
|
-
/* @__PURE__ */c("button",{onClick:
|
|
22
|
-
/* @__PURE__ */l("div",{className:"flex items-center gap-0.5 px-2 py-1 border-b border-gray-200 bg-white shrink-0 overflow-x-auto",children:[0===
|
|
20
|
+
/* @__PURE__ */c("button",{onClick:()=>I(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
|
|
21
|
+
/* @__PURE__ */c("button",{onClick:X,disabled:!B.trim(),className:"px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-40",children:"Save"})]})]}),
|
|
22
|
+
/* @__PURE__ */l("div",{className:"flex items-center gap-0.5 px-2 py-1 border-b border-gray-200 bg-white shrink-0 overflow-x-auto",children:[0===S.length&&/* @__PURE__ */c("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),S.map((e,t)=>/* @__PURE__ */l("button",{onClick:()=>D(e.url),onContextMenu:async o=>{o.preventDefault();await n({title:"Remove bookmark",message:`"${e.label}" will be removed from your bookmarks.`,confirmLabel:"Remove",variant:"danger"})&&P(t)},className:"flex items-center gap-1.5 px-2 py-0.5 rounded text-[12px] text-gray-700 hover:bg-gray-100 whitespace-nowrap",title:`${e.url}\n(right-click to remove)`,children:[
|
|
23
23
|
/* @__PURE__ */c("img",{src:`https://icons.duckduckgo.com/ip3/${new URL(e.url).hostname}.ico`,alt:"",className:"h-3.5 w-3.5",onError:e=>{e.target.style.visibility="hidden"}}),e.label]},t)),
|
|
24
24
|
/* @__PURE__ */c("div",{className:"flex-1"}),
|
|
25
25
|
/* @__PURE__ */c("button",{onClick:()=>{g(k);try{localStorage.setItem(u,k)}catch{}},className:"px-2 py-0.5 text-[11px] text-gray-500 hover:text-gray-800",title:"Set current page as homepage",children:"Set as home"})]}),
|
|
26
|
-
/* @__PURE__ */c("div",{className:"flex-1 relative min-h-0 bg-gray-50",children:/* @__PURE__ */c(y,{url:k,iframeKey:
|
|
26
|
+
/* @__PURE__ */c("div",{className:"flex-1 relative min-h-0 bg-gray-50",children:/* @__PURE__ */c(y,{url:k,iframeKey:R,iframeRef:E,openExternal:F,showHelp:A,dismissHelp:()=>O(!1)})})]})}function v(e){try{return new URL(e).hostname.replace(/^www\./,"")}catch{return e}}function y({url:e,iframeKey:t,iframeRef:o,openExternal:r,showHelp:n,dismissHelp:a}){const[d,u]=s(!1);i(()=>{u(!1)},[e]);return function(e){try{const t=new URL(e).hostname.toLowerCase();return k.some(e=>t===e||t.endsWith("."+e))}catch{return!1}}(e)&&!d?/* @__PURE__ */c("div",{className:"absolute inset-0 flex items-center justify-center bg-white p-8",children:/* @__PURE__ */l("div",{className:"max-w-md text-center",children:[
|
|
27
27
|
/* @__PURE__ */c("svg",{className:"h-14 w-14 mx-auto text-gray-300 mb-3",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L21 21M5.636 5.636L3 3m9 9a9 9 0 110-18 9 9 0 010 18z"})}),
|
|
28
28
|
/* @__PURE__ */l("h3",{className:"text-base font-semibold text-gray-800 mb-1",children:[v(e)," can't be embedded"]}),
|
|
29
29
|
/* @__PURE__ */l("p",{className:"text-sm text-gray-500 mb-4",children:["This site sends an ",
|