efficient-shell 4.168.0 → 4.173.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 +406 -9
- package/dist/{Browser-PZPREN6H.js → Browser-X7UTQJOX.js} +13 -13
- package/dist/Calculator-IAJLLX67.js +29 -0
- package/dist/ConfirmDialog-EBZ5LHVS.js +1 -0
- package/dist/{CurrencyConverter-3BTIZ2LT.js → CurrencyConverter-RODHLQMG.js} +5 -5
- package/dist/{Documents-YAOM3L47.js → Documents-BJFZHHBZ.js} +8 -8
- package/dist/{Files-2UHYDMLL.js → Files-PL5NE4PQ.js} +27 -27
- package/dist/{Notepad-OWL75AYX.js → Notepad-52LDWEK2.js} +10 -10
- package/dist/PdfViewer-IIXXT7SF.js +1 -0
- package/dist/{PomodoroTimer-SCPEOG6W.js → PomodoroTimer-C3KCTCMB.js} +30 -30
- package/dist/{Preview-5R3QZSJU.js → Preview-O3N26GG6.js} +31 -31
- package/dist/{Sidebar-C5RXL72L.js → Sidebar-QNGQA6J5.js} +24 -24
- package/dist/Spreadsheet-HVKVNQN6.js +37 -0
- package/dist/{Stock-UGDIALNB.js → Stock-YOGVYL6F.js} +6 -6
- package/dist/{Weather-CR2MN5XF.js → Weather-4OQOBM4C.js} +13 -13
- package/dist/{WorldClock-JBNJ7C7J.js → WorldClock-K5KLOOD7.js} +5 -5
- package/dist/apps/index.js +1 -1
- package/dist/bin/ui-check.js +1 -1
- package/dist/brand.css +1 -1
- package/dist/chunk-6TDPY7WG.js +3 -0
- package/dist/{chunk-SOI4LR67.js → chunk-A6ZY4XSQ.js} +7 -7
- package/dist/{chunk-4P3IWZUC.js → chunk-AYZ4LP2A.js} +9 -9
- package/dist/{chunk-A7Q4BCPI.js → chunk-CQN5CFKA.js} +6 -6
- package/dist/chunk-EXXHI5DO.js +12 -0
- package/dist/{chunk-M4DB7UBZ.js → chunk-FIGVDYJR.js} +16 -16
- package/dist/chunk-FWOPZB6I.js +1 -0
- package/dist/{chunk-XKHAP6N2.js → chunk-MFR4GZSB.js} +15 -15
- package/dist/{chunk-JLPJBO7K.js → chunk-MVUYJAZT.js} +1 -1
- package/dist/{chunk-YWXYBJ5Y.js → chunk-MYNLN25H.js} +2 -2
- package/dist/{chunk-KSSAVLO7.js → chunk-OZMDKNVR.js} +1 -1
- package/dist/{chunk-N3ZXQE4M.js → chunk-PGVUO7GB.js} +5 -5
- package/dist/chunk-RN5V7MDO.js +995 -0
- package/dist/chunk-S5QF4VFG.js +1 -0
- package/dist/{chunk-5J2TKKGH.js → chunk-SXRJYJ7M.js} +1 -1
- package/dist/{chunk-QZG72OVX.js → chunk-TLYGAHZN.js} +1 -1
- package/dist/{chunk-CUX4XGLS.js → chunk-U2SEGSNG.js} +1 -1
- package/dist/{chunk-FZRQBHTK.js → chunk-U6IUYOYS.js} +1 -1
- package/dist/{chunk-3U47BTEV.js → chunk-UOZMVS4Y.js} +11 -11
- package/dist/{chunk-S2AZZGFF.js → chunk-W32SKK3D.js} +1 -1
- package/dist/chunk-W33ZFDRR.js +1 -0
- package/dist/chunk-X5LH3UOJ.js +112 -0
- package/dist/{chunk-6MBERZOS.js → chunk-XHWT4DH2.js} +3 -3
- package/dist/chunk-XJX3NA37.js +1 -0
- package/dist/chunk-YKZHOXI4.js +2 -0
- package/dist/{chunk-FVXBJGY5.js → chunk-Z3JO7MDE.js} +5 -5
- package/dist/{chunk-PLX2N4N6.js → chunk-ZDCOC4IH.js} +9 -9
- package/dist/file-intake/index.js +1 -1
- package/dist/format/index.d.ts +1 -1
- package/dist/format/index.js +1 -1
- package/dist/icons/efficient.d.ts +355 -0
- package/dist/icons/efficient.js +470 -0
- package/dist/icons/heroicons.d.ts +6 -0
- package/dist/icons/heroicons.js +1 -0
- package/dist/icons/index.d.ts +64 -0
- package/dist/icons/index.js +2 -0
- package/dist/icons/lucide.d.ts +6 -0
- package/dist/icons/lucide.js +1 -0
- package/dist/{index-CbLbd__n.d.ts → index-Dod08dsC.d.ts} +302 -11
- package/dist/{index-DCUzlKPZ.d.ts → index-DpUDK9zG.d.ts} +5 -0
- package/dist/index.d.ts +16 -6
- package/dist/index.js +650 -645
- package/dist/map/index.js +3 -3
- package/dist/page-tabs/index.js +5 -5
- package/dist/pdf/index.js +1 -1
- package/dist/registry-FHdUeVww.d.ts +71 -0
- package/dist/sounds-FO772MN2.js +1 -0
- package/dist/ui/index.d.ts +4 -2
- package/dist/ui/index.js +1 -1
- package/dist/ui.css +81 -0
- package/package.json +27 -1
- package/dist/Calculator-NXMUEH5D.js +0 -29
- package/dist/ConfirmDialog-ZDVMT74Y.js +0 -1
- package/dist/PdfViewer-S2B2DZYB.js +0 -1
- package/dist/Spreadsheet-XT4HF7MQ.js +0 -37
- package/dist/chunk-6KO2U66R.js +0 -3
- package/dist/chunk-7U3F53IG.js +0 -1
- package/dist/chunk-BUTSU7FO.js +0 -1
- package/dist/chunk-GVZLCY3B.js +0 -994
- package/dist/chunk-JYLI2WXW.js +0 -12
- package/dist/chunk-LW27IM3S.js +0 -112
- package/dist/chunk-NCQLGQYR.js +0 -1
- package/dist/sounds-OMHXVVQ4.js +0 -1
package/README.md
CHANGED
|
@@ -965,10 +965,11 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
|
|
|
965
965
|
| `<ShellAuthProvider value={{ hasAnyPerm }}>` | Permission-filter nav items. |
|
|
966
966
|
| `<ShellPrefsProvider value={{ prefs, save }}>` | Where the shell reads/writes user prefs (theme, taskbar pos, sticky notes, …). Use `useLocalStoragePrefs(key)` for a backend-less default. Since 4.92.0 it is also how `<EntityList>` / `<ResizableTable>` column config and sort persist: with no provider mounted, `useShellPrefs()` reads empty and drops saves, so both silently degrade to localStorage-only and never reach the user's profile. Session restore reads `session_windows` once, at mount: hand the provider prefs that are already loaded by then (an adapter whose prefs arrive later misses that load's replay, though the saved set is kept). |
|
|
967
967
|
| `<ShellStringsProvider value={{ … }} locale?>` | Translates every word the shell and the kit draw on their own account — window controls, taskbar hints, dialogs, pickers, pagers, list footers, upload zones, charts, timelines. English works with NO provider; the override is a typed partial merged per section, so an incomplete catalog falls back rather than breaking. Prop-level text (`emptyText`, placeholders) always wins over the catalog. `locale` (a BCP 47 tag) writes dates and numbers that locale's way; without one nothing moves. See [Languages, dates and numbers](#languages-dates-and-numbers--shellstringsprovider) for the full catalog. |
|
|
968
|
+
| `<ShellLanguageProvider languages language strings? onChange? reload?>` | Which interface language is in force. Takes the tag `resolveShellLanguage` chose before mount and that language's shell catalog, mounts `ShellStringsProvider` with both, stamps `<html lang>`, and gives `useShellLanguage()` its `setLanguage`, which saves the choice and reloads. See [Choosing the language](#choosing-the-language--shelllanguageprovider). |
|
|
968
969
|
| `<ShellEntityFetcherProvider value={(endpoint, id) => …}>` | How the modal stack fetches entity data. |
|
|
969
970
|
| `<BugReportConfigProvider value={{ submit, list?, resolve? }}>` | Wire the bug-report flow to your backend. |
|
|
970
971
|
| `<DesktopHostProvider value={{ stickyResolver?, saveShortcuts?, … }}>` | Sticky-note ref resolver + persistence callbacks. |
|
|
971
|
-
| `<StatusBadgeProvider groups={{ status: 'success' \| ... }}
|
|
972
|
+
| `<StatusBadgeProvider groups={{ status: 'success' \| ... }} labelFor?>` | Status string → semantic group. `labelFor(status)` gives every badge under it the host's words for a status (an interface in another language passes it once); a status it returns `undefined` for keeps the derived label, and a badge's own `label` still wins. `useStatusLabel()` says a status the same way outside a badge: a filter option, a search result's second line. |
|
|
972
973
|
| `<PortalBrandingProvider load={...} fallback={...}>` | Loads public hostname-scoped Company Profile identity before authentication, applies its favicon and optional derived document title, and exposes it through `usePortalBranding()`. |
|
|
973
974
|
| `setShellApiClient(axios)` | Module-level: register your axios instance once. |
|
|
974
975
|
| `setShellAuthBridge({ user, logout })` | Module-level: register user identity / logout handler. |
|
|
@@ -995,6 +996,7 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
|
|
|
995
996
|
| `useUndoCanEdit(canEdit)` | A form's own word on whether the record may be edited right now — `useUndoCanEdit(!isLocked)`. While false the stack records nothing, ⌘Z is left alone and the footer shows no controls. A nested `<UndoProvider canEdit={false}>` in the form's JSX says the same thing: it shadows the stack for its children AND forwards the read-only claim to the window's stack, which is where the form's own `useUndoableState` calls (made above it in the tree) register. Either form of the claim makes the whole window read-only. The positive claim, `useUndoCanEdit(true)`, is how a window that edits in place outside Edit mode — a detail with inline cells behind a permission — asks the footer for the pair. |
|
|
996
997
|
| `useMultiModal()` | Manages multi-window stacking + activate/blur. |
|
|
997
998
|
| `useShellAuth() / useShellPrefs() / useShellEntityFetcher() / useBugReport() / useDesktopHost()` | Context readers — the shell uses these internally; consumers may also call them. |
|
|
999
|
+
| `useShellLanguage()` | `{ language, languages, setLanguage }` — the active language's tag, the ones on offer, and the switch. English with nothing to switch to when no `ShellLanguageProvider` is mounted. |
|
|
998
1000
|
| `useShellStrings() / useShellLocale()` | The active catalog, and the provider's `locale` (`undefined` without one) — for a host cell that should write a date or a number the way the kit does. |
|
|
999
1001
|
|
|
1000
1002
|
### Apps barrel — `efficient-shell/apps`
|
|
@@ -1475,6 +1477,35 @@ classes in `ui.css` (the timeline card's) carry pixel radii of their own.
|
|
|
1475
1477
|
}
|
|
1476
1478
|
```
|
|
1477
1479
|
|
|
1480
|
+
**Corners.** A component does not pick a step of the scale for itself; it asks
|
|
1481
|
+
for the radius of what it is. Four role tokens, each defaulting to the step that
|
|
1482
|
+
role has always drawn, so they change nothing until you set one:
|
|
1483
|
+
|
|
1484
|
+
| Token | Default | Drawn by |
|
|
1485
|
+
|---|---|---|
|
|
1486
|
+
| `--radius-control` | `--radius-md` (6px) | buttons, fields, a segmented control's options |
|
|
1487
|
+
| `--radius-card` | `--radius-lg` (8px) | `Card`, `Dialog`, a segmented control's track |
|
|
1488
|
+
| `--radius-popup` | `--radius-2xl` (16px) | menus, select lists, the start menu, the search palette |
|
|
1489
|
+
| `--radius-window` | `--radius-2xl` (16px) | windows and their title bars |
|
|
1490
|
+
|
|
1491
|
+
They are plain custom properties: `:root { --radius-control: 2px; }` restyles
|
|
1492
|
+
one role and leaves the rest. Pills (switches, status badges, avatars) are round
|
|
1493
|
+
at every setting. Popovers, tooltips, drawers and menu rows still sit on their
|
|
1494
|
+
own scale steps, because each draws a different radius today and moving them
|
|
1495
|
+
onto a role would change how they look.
|
|
1496
|
+
|
|
1497
|
+
**Corners is also a setting of the theme.** The `corners` preference
|
|
1498
|
+
(`'square' | 'soft' | 'round'`, in Customization beside the colour theme) is
|
|
1499
|
+
stamped on `<html>` as `data-corners` by `useTheme` and
|
|
1500
|
+
`applyThemePrefs({ corners })`; a host with its own switcher calls
|
|
1501
|
+
`applyCorners(value)`. It rescales the whole radius scale, so the roles follow
|
|
1502
|
+
it and so does every scale utility in your own markup, and it remaps bare
|
|
1503
|
+
`rounded`, which the scale cannot reach. `soft` is the absence of the attribute:
|
|
1504
|
+
no rule applies and your own `@theme` radii still decide. `square` and `round`
|
|
1505
|
+
are a person's own choice, so they win over the host's theme, the way a chosen
|
|
1506
|
+
colour theme does. Do not write literal radii (an arbitrary-value radius class,
|
|
1507
|
+
an inline `borderRadius`): they are the one thing the setting cannot reach.
|
|
1508
|
+
|
|
1478
1509
|
**Glass.** Menus, popups, listboxes and tooltips are frosted glass
|
|
1479
1510
|
(`glassStyle()`). `--menu-opacity` (0–1, default `0.95`) sets how much shows
|
|
1480
1511
|
through. `rosh-reduce-transparency` on `<html>` makes every popup one solid
|
|
@@ -1595,6 +1626,72 @@ the field, because what is typed there is parsed back.
|
|
|
1595
1626
|
`formatDate(value, { locale, style })` or `fmtSliderDate(ms, locale)`, or hand it
|
|
1596
1627
|
to `Intl` directly — so a host's dates and numbers read like the kit's.
|
|
1597
1628
|
|
|
1629
|
+
#### Choosing the language — `ShellLanguageProvider`
|
|
1630
|
+
|
|
1631
|
+
`ShellStringsProvider` can speak another language; `ShellLanguageProvider` is
|
|
1632
|
+
what decides which. It holds the languages you offer, the one in force and the
|
|
1633
|
+
reader's saved choice, stamps `<html lang>`, and mounts `ShellStringsProvider`
|
|
1634
|
+
for you with that language's catalog and locale.
|
|
1635
|
+
|
|
1636
|
+
A language is fixed for the life of a page. Resolve it before you mount, load
|
|
1637
|
+
its copy, and hand both over; `setLanguage` saves the choice and reloads. That
|
|
1638
|
+
is deliberate: column sets, navigation and status buckets usually read their
|
|
1639
|
+
copy when they are imported, a list snapshots its columns when it mounts, and
|
|
1640
|
+
a reload is the one switch that leaves none of them in the old language.
|
|
1641
|
+
|
|
1642
|
+
```tsx
|
|
1643
|
+
import {
|
|
1644
|
+
ENGLISH, ShellLanguageProvider, resolveShellLanguage, useShellLanguage,
|
|
1645
|
+
type ShellLanguage,
|
|
1646
|
+
} from 'efficient-shell/ui';
|
|
1647
|
+
|
|
1648
|
+
const LANGUAGES: ShellLanguage[] = [ENGLISH, { tag: 'zh-Hans', name: '简体中文' }];
|
|
1649
|
+
|
|
1650
|
+
// Before React mounts: the saved choice, then the browser's list, then the first offered.
|
|
1651
|
+
const language = resolveShellLanguage({ offered: LANGUAGES });
|
|
1652
|
+
|
|
1653
|
+
<ShellLanguageProvider
|
|
1654
|
+
languages={LANGUAGES}
|
|
1655
|
+
language={language}
|
|
1656
|
+
strings={shellCatalogForThatLanguage} // omit for English
|
|
1657
|
+
onChange={tag => saveToTheAccount(tag)} // awaited, then the page reloads
|
|
1658
|
+
>
|
|
1659
|
+
<App />
|
|
1660
|
+
</ShellLanguageProvider>
|
|
1661
|
+
|
|
1662
|
+
const { language, languages, setLanguage } = useShellLanguage();
|
|
1663
|
+
```
|
|
1664
|
+
|
|
1665
|
+
- **Resolution.** `resolveShellLanguage` reads the choice saved on the device
|
|
1666
|
+
(`SHELL_LANGUAGE_STORAGE_KEY`, `react-os-shell:language`), then
|
|
1667
|
+
`navigator.languages`, then falls back to the first language offered. A
|
|
1668
|
+
browser asking for `zh-CN` or `zh-SG` gets `zh-Hans`; with both Chineses on
|
|
1669
|
+
offer `zh-TW` gets `zh-Hant`, and with only Simplified it gets that rather
|
|
1670
|
+
than English. A saved choice that is no longer offered is skipped.
|
|
1671
|
+
`matchShellLanguage(wanted, offered)` is the matching on its own.
|
|
1672
|
+
- **English keeps its dates and numbers.** A language's `locale` defaults to
|
|
1673
|
+
its tag. `ENGLISH` carries `locale: null`, so mounting the provider for
|
|
1674
|
+
English moves no date and no number: the kit writes them as it does with no
|
|
1675
|
+
provider at all.
|
|
1676
|
+
- **The account.** `onChange` is where a host with accounts keeps the choice.
|
|
1677
|
+
It is awaited before the reload, and a failure does not stop the switch: the
|
|
1678
|
+
device has the choice. `saveShellLanguage(tag)` returns `true` only when the
|
|
1679
|
+
write reads back, for a sign-in sync that would otherwise reload on a write
|
|
1680
|
+
that never landed.
|
|
1681
|
+
- **`reload={false}`** leaves the switch to you: the choice is saved and
|
|
1682
|
+
`onChange` runs, and you re-render the provider with the new `language` and
|
|
1683
|
+
`strings`.
|
|
1684
|
+
|
|
1685
|
+
**The windowed shell's lists, records and forms are in the catalog too.**
|
|
1686
|
+
`EntityList`'s row menu and its copy and export toasts (`entityList`),
|
|
1687
|
+
`ResizableTable`'s column chooser (`columns`), a record window with no record
|
|
1688
|
+
to draw (`record`), `ChangePasswordForm` (`password`), `PdfActionButton`
|
|
1689
|
+
(`pdfAction`), `UndoControls` (`undo`), the copy icon beside a record's number
|
|
1690
|
+
(`copy`), the plain "Loading..." line (`loading`) and `SoundsPanel`'s scheme
|
|
1691
|
+
and event names (`sounds`). Their own props — `submitLabel`, `doneLabel`, the
|
|
1692
|
+
PDF button's `label`, a list's `errorTitle` — still win, and the keys a sound
|
|
1693
|
+
scheme is stored under are identifiers, never translated.
|
|
1694
|
+
|
|
1598
1695
|
**What is not in the catalog, on purpose:** month and weekday names (the
|
|
1599
1696
|
`locale` supplies them); file-format names in `BrandAssetEditor`'s default
|
|
1600
1697
|
`acceptHint` and the `20ft`/`40ft` container codes (notation, not language); the
|
|
@@ -1603,7 +1700,7 @@ tables read); the sample content of the starter templates (`DashboardTemplate`,
|
|
|
1603
1700
|
`AuthScreen` and the rest are demos to copy, not components to configure); and
|
|
1604
1701
|
console warnings, which are for developers.
|
|
1605
1702
|
|
|
1606
|
-
**The full catalog** —
|
|
1703
|
+
**The full catalog** — 55 sections, 522 keys. A function's default is shown
|
|
1607
1704
|
with sample arguments.
|
|
1608
1705
|
|
|
1609
1706
|
<details><summary><code>window</code> — 17 keys</summary>
|
|
@@ -1741,7 +1838,7 @@ with sample arguments.
|
|
|
1741
1838
|
|
|
1742
1839
|
</details>
|
|
1743
1840
|
|
|
1744
|
-
<details><summary><code>pageTabs</code> —
|
|
1841
|
+
<details><summary><code>pageTabs</code> — 16 keys</summary>
|
|
1745
1842
|
|
|
1746
1843
|
| Key | Type | English default |
|
|
1747
1844
|
|---|---|---|
|
|
@@ -1752,6 +1849,9 @@ with sample arguments.
|
|
|
1752
1849
|
| `closeRight` | `string` | `Close tabs to the right` |
|
|
1753
1850
|
| `closeAll` | `string` | `Close all tabs` |
|
|
1754
1851
|
| `unsaved` | `string` | `Unsaved changes` |
|
|
1852
|
+
| `copy` | `(text: string) => string` | `Copy {text}` |
|
|
1853
|
+
| `copied` | `string` | `Copied` |
|
|
1854
|
+
| `tabMenu` | `string` | `Window menu` |
|
|
1755
1855
|
| `discardTitle` | `string` | `Discard unsaved changes?` |
|
|
1756
1856
|
| `discardOne` | `string` | `This page has changes that have not been saved. Close it anyway?` |
|
|
1757
1857
|
| `discardMany` | `string` | `pages have changes that have not been saved. Close them anyway?` |
|
|
@@ -1845,11 +1945,12 @@ with sample arguments.
|
|
|
1845
1945
|
|
|
1846
1946
|
</details>
|
|
1847
1947
|
|
|
1848
|
-
<details><summary><code>list</code> —
|
|
1948
|
+
<details><summary><code>list</code> — 9 keys</summary>
|
|
1849
1949
|
|
|
1850
1950
|
| Key | Type | English default |
|
|
1851
1951
|
|---|---|---|
|
|
1852
1952
|
| `selected` | `(count: number) => string` | `3 selected` |
|
|
1953
|
+
| `selectedNotShown` | `(count: number, notShown: number) => string` | `3 selected, 3 not shown` |
|
|
1853
1954
|
| `allLoaded` | `(total: number, label: string) => string` | `All 3 {label} loaded` |
|
|
1854
1955
|
| `count` | `(total: number, label: string) => string` | `3 {label}` |
|
|
1855
1956
|
| `loadedOf` | `(loaded: number, total: number, label: string) => string` | `3 of 3 {label}` |
|
|
@@ -1929,13 +2030,15 @@ with sample arguments.
|
|
|
1929
2030
|
|
|
1930
2031
|
</details>
|
|
1931
2032
|
|
|
1932
|
-
<details><summary><code>copy</code> —
|
|
2033
|
+
<details><summary><code>copy</code> — 7 keys</summary>
|
|
1933
2034
|
|
|
1934
2035
|
| Key | Type | English default |
|
|
1935
2036
|
|---|---|---|
|
|
1936
2037
|
| `copy` | `string` | `Copy` |
|
|
1937
2038
|
| `copyLabel` | `(label: string) => string` | `Copy {label}` |
|
|
1938
2039
|
| `copied` | `string` | `Copied` |
|
|
2040
|
+
| `copyValue` | `(value: string) => string` | `Copy {value}` |
|
|
2041
|
+
| `copiedValue` | `string` | `Copied!` |
|
|
1939
2042
|
| `copiedLabel` | `(label: string) => string` | `{label} copied` |
|
|
1940
2043
|
| `failed` | `string` | `Could not copy to the clipboard` |
|
|
1941
2044
|
|
|
@@ -2052,6 +2155,60 @@ with sample arguments.
|
|
|
2052
2155
|
|
|
2053
2156
|
</details>
|
|
2054
2157
|
|
|
2158
|
+
<details><summary><code>resizeHandle</code> — 2 keys</summary>
|
|
2159
|
+
|
|
2160
|
+
| Key | Type | English default |
|
|
2161
|
+
|---|---|---|
|
|
2162
|
+
| `label` | `string` | `Resize panel` |
|
|
2163
|
+
| `hint` | `string` | `Drag to resize · double-click to reset` |
|
|
2164
|
+
|
|
2165
|
+
</details>
|
|
2166
|
+
|
|
2167
|
+
<details><summary><code>number</code> — 2 keys</summary>
|
|
2168
|
+
|
|
2169
|
+
| Key | Type | English default |
|
|
2170
|
+
|---|---|---|
|
|
2171
|
+
| `increase` | `string` | `Increase` |
|
|
2172
|
+
| `decrease` | `string` | `Decrease` |
|
|
2173
|
+
|
|
2174
|
+
</details>
|
|
2175
|
+
|
|
2176
|
+
<details><summary><code>slider</code> — 1 keys</summary>
|
|
2177
|
+
|
|
2178
|
+
| Key | Type | English default |
|
|
2179
|
+
|---|---|---|
|
|
2180
|
+
| `value` | `(label: string) => string` | `{label} value` |
|
|
2181
|
+
|
|
2182
|
+
</details>
|
|
2183
|
+
|
|
2184
|
+
<details><summary><code>color</code> — 9 keys</summary>
|
|
2185
|
+
|
|
2186
|
+
| Key | Type | English default |
|
|
2187
|
+
|---|---|---|
|
|
2188
|
+
| `swatch` | `(label: string, value: string) => string` | `{label}: {value}` |
|
|
2189
|
+
| `none` | `string` | `None` |
|
|
2190
|
+
| `panel` | `(label: string) => string` | `{label} picker` |
|
|
2191
|
+
| `square` | `string` | `Saturation and brightness` |
|
|
2192
|
+
| `squareValue` | `(saturation: number, brightness: number) => string` | `Saturation 3%, brightness 3%` |
|
|
2193
|
+
| `hue` | `string` | `Hue` |
|
|
2194
|
+
| `hex` | `(label: string) => string` | `{label} hex code` |
|
|
2195
|
+
| `presets` | `string` | `Presets` |
|
|
2196
|
+
| `clear` | `string` | `No color` |
|
|
2197
|
+
|
|
2198
|
+
</details>
|
|
2199
|
+
|
|
2200
|
+
<details><summary><code>tree</code> — 5 keys</summary>
|
|
2201
|
+
|
|
2202
|
+
| Key | Type | English default |
|
|
2203
|
+
|---|---|---|
|
|
2204
|
+
| `empty` | `string` | `Nothing here yet` |
|
|
2205
|
+
| `dropHere` | `string` | `Drop here` |
|
|
2206
|
+
| `dragHint` | `string` | `Drag to move` |
|
|
2207
|
+
| `moveHint` | `string` | `Alt+Arrow Up or Alt+Arrow Down moves it` |
|
|
2208
|
+
| `insertHere` | `string` | `Insert here` |
|
|
2209
|
+
|
|
2210
|
+
</details>
|
|
2211
|
+
|
|
2055
2212
|
<details><summary><code>layout</code> — 17 keys</summary>
|
|
2056
2213
|
|
|
2057
2214
|
| Key | Type | English default |
|
|
@@ -2276,6 +2433,183 @@ with sample arguments.
|
|
|
2276
2433
|
|
|
2277
2434
|
</details>
|
|
2278
2435
|
|
|
2436
|
+
<details><summary><code>map</code> — 14 keys</summary>
|
|
2437
|
+
|
|
2438
|
+
| Key | Type | English default |
|
|
2439
|
+
|---|---|---|
|
|
2440
|
+
| `failed` | `string` | `The map could not be drawn here. The list has everything it shows.` |
|
|
2441
|
+
| `approximate` | `(label: string) => string` | `{label}, approximate location` |
|
|
2442
|
+
| `approximateTitle` | `(label: string) => string` | `{label} (approximate)` |
|
|
2443
|
+
| `places` | `(count: number) => string` | `places` (`place` for one) |
|
|
2444
|
+
| `group` | `(count: number, noun: string) => string` | `3 {noun} here` |
|
|
2445
|
+
| `stops` | `(numbers: string) => string` | `Stops {numbers}` |
|
|
2446
|
+
| `closeBy` | `(count: number, noun: string) => string` | `3 more {noun} close by, drawn when the map is closer` |
|
|
2447
|
+
| `spread` | `string` | `Press to see them apart` |
|
|
2448
|
+
| `cycle` | `string` | `They stand on one spot: press for the next of them` |
|
|
2449
|
+
| `fullScreen` | `string` | `Full screen` |
|
|
2450
|
+
| `leaveFullScreen` | `string` | `Leave full screen` |
|
|
2451
|
+
| `chosen` | `(label: string) => string` | `{label}, chosen on the map` |
|
|
2452
|
+
| `closeCard` | `string` | `Close` |
|
|
2453
|
+
| `openRecord` | `string` | `Open` |
|
|
2454
|
+
|
|
2455
|
+
</details>
|
|
2456
|
+
|
|
2457
|
+
<details><summary><code>pdf</code> — 16 keys</summary>
|
|
2458
|
+
|
|
2459
|
+
| Key | Type | English default |
|
|
2460
|
+
|---|---|---|
|
|
2461
|
+
| `previousPage` | `string` | `Previous page` |
|
|
2462
|
+
| `nextPage` | `string` | `Next page` |
|
|
2463
|
+
| `pageOf` | `(page: number, total: number) => string` | `2 / 5` |
|
|
2464
|
+
| `zoomOut` | `string` | `Zoom out` |
|
|
2465
|
+
| `zoomIn` | `string` | `Zoom in` |
|
|
2466
|
+
| `zoom` | `string` | `Zoom` |
|
|
2467
|
+
| `fitWidth` | `string` | `Fit width` |
|
|
2468
|
+
| `fitPage` | `string` | `Fit page` |
|
|
2469
|
+
| `fit` | `string` | `Fit` |
|
|
2470
|
+
| `fitHint` | `string` | `Fit to the pane: follows its size until you zoom by hand` |
|
|
2471
|
+
| `print` | `string` | `Print` |
|
|
2472
|
+
| `download` | `string` | `Download` |
|
|
2473
|
+
| `email` | `string` | `Email` |
|
|
2474
|
+
| `loading` | `string` | `Loading PDF…` |
|
|
2475
|
+
| `loadFailed` | `string` | `Failed to load PDF` |
|
|
2476
|
+
| `allowPopups` | `string` | `Allow popups to print` |
|
|
2477
|
+
|
|
2478
|
+
</details>
|
|
2479
|
+
|
|
2480
|
+
<details><summary><code>loading</code> — 1 keys</summary>
|
|
2481
|
+
|
|
2482
|
+
| Key | Type | English default |
|
|
2483
|
+
|---|---|---|
|
|
2484
|
+
| `label` | `string` | `Loading...` |
|
|
2485
|
+
|
|
2486
|
+
</details>
|
|
2487
|
+
|
|
2488
|
+
<details><summary><code>entityList</code> — 15 keys</summary>
|
|
2489
|
+
|
|
2490
|
+
| Key | Type | English default |
|
|
2491
|
+
|---|---|---|
|
|
2492
|
+
| `open` | `string` | `Open` |
|
|
2493
|
+
| `copy` | `string` | `Copy` |
|
|
2494
|
+
| `copyColumn` | `(column: string) => string` | `Copy {column}` |
|
|
2495
|
+
| `copyRows` | `(count: number) => string` | `Copy 3 rows` (`Copy row` for one) |
|
|
2496
|
+
| `exportSelected` | `string` | `Export selected to CSV` |
|
|
2497
|
+
| `selectAll` | `string` | `Select all` |
|
|
2498
|
+
| `selectAllLoaded` | `(loaded: number) => string` | `Select all 3 loaded` |
|
|
2499
|
+
| `clearSelection` | `string` | `Clear selection` |
|
|
2500
|
+
| `refresh` | `string` | `Refresh` |
|
|
2501
|
+
| `copiedText` | `string` | `Text` |
|
|
2502
|
+
| `copiedRows` | `(count: number) => string` | `3 rows` (`Row` for one) |
|
|
2503
|
+
| `exported` | `(count: number) => string` | `Exported 3 rows.` (`Exported 1 row.` for one) |
|
|
2504
|
+
| `exportTruncated` | `(kept: number, asked: number) => string` | `Exported 5000 of 8000 rows. The server capped this export, so the file is incomplete.` |
|
|
2505
|
+
| `exportTruncatedUnknown` | `(asked: number) => string` | `Exported only some of the 8000 rows. The server capped this export, so the file is incomplete.` |
|
|
2506
|
+
| `exportFailed` | `string` | `CSV export failed.` |
|
|
2507
|
+
|
|
2508
|
+
</details>
|
|
2509
|
+
|
|
2510
|
+
<details><summary><code>columns</code> — 9 keys</summary>
|
|
2511
|
+
|
|
2512
|
+
| Key | Type | English default |
|
|
2513
|
+
|---|---|---|
|
|
2514
|
+
| `choose` | `string` | `Choose columns` |
|
|
2515
|
+
| `heading` | `string` | `Columns` |
|
|
2516
|
+
| `reset` | `string` | `Reset` |
|
|
2517
|
+
| `saveDefault` | `string` | `Save as default for all users` |
|
|
2518
|
+
| `savedDefault` | `string` | `Saved as default` |
|
|
2519
|
+
| `select` | `string` | `Select` |
|
|
2520
|
+
| `selectDone` | `string` | `Done` |
|
|
2521
|
+
| `showSelection` | `string` | `Show selection column` |
|
|
2522
|
+
| `hideSelection` | `string` | `Hide selection column` |
|
|
2523
|
+
|
|
2524
|
+
</details>
|
|
2525
|
+
|
|
2526
|
+
<details><summary><code>record</code> — 8 keys</summary>
|
|
2527
|
+
|
|
2528
|
+
| Key | Type | English default |
|
|
2529
|
+
|---|---|---|
|
|
2530
|
+
| `loading` | `string` | `Loading record…` |
|
|
2531
|
+
| `signInRequired` | `string` | `Sign in required` |
|
|
2532
|
+
| `signInBody` | `string` | `Your session may have expired. Sign in again to open this record.` |
|
|
2533
|
+
| `notFound` | `string` | `Record not found` |
|
|
2534
|
+
| `loadFailed` | `string` | `Couldn't load record` |
|
|
2535
|
+
| `checkConnection` | `string` | `Check your connection and try again.` |
|
|
2536
|
+
| `rejected` | `string` | `The server rejected this request.` |
|
|
2537
|
+
| `retry` | `string` | `Try again` |
|
|
2538
|
+
|
|
2539
|
+
</details>
|
|
2540
|
+
|
|
2541
|
+
<details><summary><code>password</code> — 11 keys</summary>
|
|
2542
|
+
|
|
2543
|
+
| Key | Type | English default |
|
|
2544
|
+
|---|---|---|
|
|
2545
|
+
| `current` | `string` | `Current Password` |
|
|
2546
|
+
| `newPassword` | `string` | `New Password` |
|
|
2547
|
+
| `confirm` | `string` | `Confirm Password` |
|
|
2548
|
+
| `submit` | `string` | `Change Password` |
|
|
2549
|
+
| `submitting` | `string` | `Changing...` |
|
|
2550
|
+
| `done` | `string` | `Done` |
|
|
2551
|
+
| `currentRequired` | `string` | `Current password is required.` |
|
|
2552
|
+
| `tooShort` | `(minLength: number) => string` | `New password must be at least 8 characters.` |
|
|
2553
|
+
| `mismatch` | `string` | `Passwords do not match.` |
|
|
2554
|
+
| `failed` | `string` | `Failed to change password.` |
|
|
2555
|
+
| `changed` | `string` | `Your password has been changed successfully.` |
|
|
2556
|
+
|
|
2557
|
+
</details>
|
|
2558
|
+
|
|
2559
|
+
<details><summary><code>pdfAction</code> — 10 keys</summary>
|
|
2560
|
+
|
|
2561
|
+
| Key | Type | English default |
|
|
2562
|
+
|---|---|---|
|
|
2563
|
+
| `label` | `string` | `PDF` |
|
|
2564
|
+
| `loading` | `string` | `Loading...` |
|
|
2565
|
+
| `preview` | `string` | `Preview` |
|
|
2566
|
+
| `download` | `string` | `Download` |
|
|
2567
|
+
| `email` | `string` | `Send by Email` |
|
|
2568
|
+
| `downloaded` | `(filename: string) => string` | `{filename} downloaded.` |
|
|
2569
|
+
| `tookTooLong` | `(filename: string) => string` | `{filename} took too long to load. Please try again.` |
|
|
2570
|
+
| `previewLoading` | `string` | `LOADING PDF` |
|
|
2571
|
+
| `previewTookTooLong` | `string` | `This document took too long to load. Close this window and try again.` |
|
|
2572
|
+
| `previewFailed` | `string` | `Failed to load PDF.` |
|
|
2573
|
+
|
|
2574
|
+
</details>
|
|
2575
|
+
|
|
2576
|
+
<details><summary><code>undo</code> — 6 keys</summary>
|
|
2577
|
+
|
|
2578
|
+
| Key | Type | English default |
|
|
2579
|
+
|---|---|---|
|
|
2580
|
+
| `undo` | `string` | `Undo` |
|
|
2581
|
+
| `redo` | `string` | `Redo` |
|
|
2582
|
+
| `undoStep` | `(label: string, keys: string) => string` | `Undo {label} ({keys})` |
|
|
2583
|
+
| `redoStep` | `(label: string, keys: string) => string` | `Redo {label} ({keys})` |
|
|
2584
|
+
| `nothingToUndo` | `string` | `Nothing to undo` |
|
|
2585
|
+
| `nothingToRedo` | `string` | `Nothing to redo` |
|
|
2586
|
+
|
|
2587
|
+
</details>
|
|
2588
|
+
|
|
2589
|
+
<details><summary><code>sounds</code> — 17 keys</summary>
|
|
2590
|
+
|
|
2591
|
+
| Key | Type | English default |
|
|
2592
|
+
|---|---|---|
|
|
2593
|
+
| `title` | `string` | `Sounds` |
|
|
2594
|
+
| `enable` | `string` | `Sound effects` |
|
|
2595
|
+
| `autosave` | `string` | `Changes are saved automatically` |
|
|
2596
|
+
| `choice` | `(event: string, pack: string) => string` | `{event} — {pack}` |
|
|
2597
|
+
| `packClassic` | `string` | `Classic` |
|
|
2598
|
+
| `packMinimal` | `string` | `Minimal` |
|
|
2599
|
+
| `packRetro` | `string` | `Retro` |
|
|
2600
|
+
| `packSoft` | `string` | `Soft` |
|
|
2601
|
+
| `packArcade` | `string` | `Arcade` |
|
|
2602
|
+
| `packSilent` | `string` | `Silent` |
|
|
2603
|
+
| `eventClick` | `string` | `Click` |
|
|
2604
|
+
| `eventSuccess` | `string` | `Success` |
|
|
2605
|
+
| `eventError` | `string` | `Error` |
|
|
2606
|
+
| `eventNotification` | `string` | `Notification` |
|
|
2607
|
+
| `eventStartup` | `string` | `Startup` |
|
|
2608
|
+
| `eventTimerDone` | `string` | `Timer` |
|
|
2609
|
+
| `eventLogout` | `string` | `Logout` |
|
|
2610
|
+
|
|
2611
|
+
</details>
|
|
2612
|
+
|
|
2279
2613
|
### Brand surfaces — `efficient-shell/brand.css`
|
|
2280
2614
|
|
|
2281
2615
|
For output that is not a portal window — a report, a published artifact, a
|
|
@@ -2553,8 +2887,8 @@ missing or cannot be read. All of them are also exported from
|
|
|
2553
2887
|
| `formatTime(value, options?)` | `9:30 am` — twelve-hour, small am/pm; a `locale` gives the locale's own clock. |
|
|
2554
2888
|
| `getUserTimezone()`, `setUserTimezone(zone)`, `syncTimezoneFromProfile(zone)`, `TZ_AUTO` | The zone setting. `undefined` / `'auto'` follow the browser. Call `syncTimezoneFromProfile` when the signed-in user loads; a blank value changes nothing. |
|
|
2555
2889
|
| `getUserDateFormat()`, `setUserDateFormat(key)`, `DATE_FORMAT_OPTIONS` | The format setting and the 16 formats a settings page offers. |
|
|
2556
|
-
| `timeAgo(value, { precise? })` | `just now` under a minute, then `5m ago`, `3h ago`, `2d ago`; from seven days on the date (`formatDate`). A future time is `just now`. `precise` counts seconds (`12s ago`) for a status screen. Takes a string, a `Date` or epoch ms. |
|
|
2557
|
-
| `<TimeAgo value precise? className? />` | `timeAgo` in a `<time>` whose hover (`title`) is the full `formatDateTime`. Use it wherever a relative time is shown. |
|
|
2890
|
+
| `timeAgo(value, { precise? })` | `just now` under a minute, then `5m ago`, `3h ago`, `2d ago`; from seven days on the date (`formatDate`). A future time is `just now`. `precise` counts seconds (`12s ago`) for a status screen. Takes a string, a `Date` or epoch ms. With `locale` (a BCP 47 tag) the words are `Intl.RelativeTimeFormat`'s in that language, and the fallback date is written in it too. |
|
|
2891
|
+
| `<TimeAgo value precise? locale? className? />` | `timeAgo` in a `<time>` whose hover (`title`) is the full `formatDateTime`. Use it wherever a relative time is shown. It follows `ShellStringsProvider`'s `locale`; pass `locale` to override. |
|
|
2558
2892
|
| `setCurrencyFormats(catalog)` | Hand the kit the currency catalog (`/api/currencies/`: `{ AUD: { symbol: 'A$', decimals: 2 }, … }`) once a session starts. Until then amounts show their code. |
|
|
2559
2893
|
| `formatMoney(amount, currency?, { whole? })` | `A$1,234.50`, `-€50.00`, `CHF 1,200.00`. The catalog's symbol — an unknown code prints as the code, never a guessed `$`. Minus first, commas, the catalog's decimals (2 by default); `whole` drops them (`A$3,841`) for a storefront card. No currency prints the bare figure. Takes a number or the API's string. |
|
|
2560
2894
|
| `currencySymbol(code)`, `currencyDecimals(code)` | The two catalog lookups `formatMoney` uses. |
|
|
@@ -2581,6 +2915,68 @@ missing or cannot be read. All of them are also exported from
|
|
|
2581
2915
|
| `dismissPopups()`, `OVERLAY_OPEN_EVENT` | An overlay calls `dismissPopups()` as it opens; every open kit popup (`Select`, `SearchableSelect`, `TagInput`, `DatePicker`, `DateRangePicker`, `PopupMenu` via its `onClose`, `ServerStatusIndicator`) closes rather than floating over the overlay. The kit's own overlays already call it. A consumer's own popup listens for `OVERLAY_OPEN_EVENT` on `window` while open. |
|
|
2582
2916
|
| `Kbd` constants — `MOD`, `ALT`, `SHIFT`, `ENTER`, `ALT_SHIFT_E`, `CMD_K`, … | Symbol constants for rendering keyboard shortcuts. |
|
|
2583
2917
|
|
|
2918
|
+
### Icons — `efficient-shell/icons`
|
|
2919
|
+
|
|
2920
|
+
Every icon comes through this package (harness UI-24). A component asks for an
|
|
2921
|
+
icon by what it **means**, and the set the theme chose draws it:
|
|
2922
|
+
|
|
2923
|
+
```tsx
|
|
2924
|
+
import { Icon, registerIconSet } from 'efficient-shell/icons';
|
|
2925
|
+
import { heroicons } from 'efficient-shell/icons/heroicons';
|
|
2926
|
+
|
|
2927
|
+
registerIconSet(heroicons); // once, at startup, in client code
|
|
2928
|
+
|
|
2929
|
+
<Icon name="trash" /> // 1em square, the text's colour
|
|
2930
|
+
<Icon name="search" size={20} tone="muted" />
|
|
2931
|
+
<Icon name="warning" title="Overdue" /> // named: an image, not decoration
|
|
2932
|
+
```
|
|
2933
|
+
|
|
2934
|
+
`Icon` and the registry are also exported from `efficient-shell/ui` and the
|
|
2935
|
+
root entry. They import React and nothing else.
|
|
2936
|
+
|
|
2937
|
+
**The catalogue.** `IconName` is 197 meanings (`add`, `close`, `edit`, `trash`,
|
|
2938
|
+
`search`, `download`, `chevron-down`, `warning`, …), built from the icons the
|
|
2939
|
+
EFFICIENT frontends import today. `<Icon name>` is typed to it, so a misspelt
|
|
2940
|
+
meaning does not compile. To add one, add it to `src/icons/catalogue.json` with
|
|
2941
|
+
its icon in every package and run `npm run icons`.
|
|
2942
|
+
|
|
2943
|
+
**The sets.** Each is its own subpath, so a host bundles only what it
|
|
2944
|
+
registers, and the icon package behind it is an optional peer:
|
|
2945
|
+
|
|
2946
|
+
| Import | Set | Draws | Needs |
|
|
2947
|
+
|---|---|---|---|
|
|
2948
|
+
| `efficient-shell/icons/heroicons` | `heroicons` | the whole catalogue | `@heroicons/react` |
|
|
2949
|
+
| `efficient-shell/icons/lucide` | `lucide` | the whole catalogue | `lucide-react` (0.577 or later) |
|
|
2950
|
+
| `efficient-shell/icons/efficient` | `efficient` | the 165 house glyphs, which draw 58 of the catalogue meanings | nothing |
|
|
2951
|
+
|
|
2952
|
+
The first set registered is the default (`setDefaultIconSet(name)` changes
|
|
2953
|
+
that). A meaning the chosen set does not draw falls back to the default set, so
|
|
2954
|
+
a partial set never leaves a hole.
|
|
2955
|
+
|
|
2956
|
+
**Part of the theme.** The `icon_set` preference sits beside the colour theme
|
|
2957
|
+
and the corners: `useTheme` and `applyThemePrefs({ icon_set })` choose the set
|
|
2958
|
+
and stamp `data-icon-set` on `<html>`, and every `<Icon>` on the page redraws.
|
|
2959
|
+
A host with its own switcher calls `applyIconSet(name)` or `setIconSet(name)`;
|
|
2960
|
+
`iconSets()` lists what is registered. Like `corners`, a call without the key
|
|
2961
|
+
leaves the choice alone.
|
|
2962
|
+
|
|
2963
|
+
**A product's own glyphs.** An editor's block types and settings rows are not
|
|
2964
|
+
in the catalogue. Register them and draw them by `glyph`, with no icon package
|
|
2965
|
+
imported:
|
|
2966
|
+
|
|
2967
|
+
```tsx
|
|
2968
|
+
registerIcons('editor', { 'block-product': BlockProductGlyph });
|
|
2969
|
+
<Icon glyph="block-product" />
|
|
2970
|
+
```
|
|
2971
|
+
|
|
2972
|
+
They are found under any theme. `efficient-shell/icons/efficient` also exports
|
|
2973
|
+
each house glyph as a component (`TrashIcon`, `StylesIcon`, `PaddingTopIcon`,
|
|
2974
|
+
…, `size` 18 by default) for code that wants one drawing whatever the theme.
|
|
2975
|
+
|
|
2976
|
+
With no set registered, `<Icon>` draws nothing and warns once per meaning. The
|
|
2977
|
+
kit's own components (a window's close button, a filter chip) keep drawing
|
|
2978
|
+
through a built-in fallback, so a host that registers nothing sees no change.
|
|
2979
|
+
|
|
2584
2980
|
### The UI kit check — `efficient-shell-ui-check`
|
|
2585
2981
|
|
|
2586
2982
|
Every surface a person operates takes its UI from this package (harness
|
|
@@ -2616,9 +3012,10 @@ itself where the kit has an answer:
|
|
|
2616
3012
|
| `native` | `window.confirm` / `alert` / `prompt`, or a bare call nothing in the file binds | `confirm`, `confirmDestructive`, `prompt`, `toast` |
|
|
2617
3013
|
| `dark` | a `dark:` variant in a class string | the light class alone; the kit remaps it |
|
|
2618
3014
|
| `hex` | a hex colour in a `.tsx` string (not in `href`, `to`, `id`, `key`, `htmlFor`) | a token or a palette class |
|
|
3015
|
+
| `radius` | a literal corner radius: an arbitrary-value radius class (`rounded-[4px]`) or an inline `borderRadius` number or length (not `inherit`, `var(…)`, `50%` or `9999`) | a scale utility or a role token; the theme's Corners setting shapes them |
|
|
2619
3016
|
| `kit` | an import of a second UI kit (`@headlessui`, `@radix-ui`, `@mui`, `antd`, `sonner`, …) | the kit's component |
|
|
2620
|
-
| `icon` | an import of an icon package (`@heroicons`, `lucide-react`, `@tabler/icons`, …) |
|
|
2621
|
-
| `kit-dep` | one of those packages declared in `package.json` (not `devDependencies`) | remove it |
|
|
3017
|
+
| `icon` | an import of an icon package (`@heroicons`, `lucide-react`, `@tabler/icons`, …) | `<Icon name>` from `efficient-shell/icons` |
|
|
3018
|
+
| `kit-dep` | one of those packages declared in `package.json` (not `devDependencies`, and not one the shell needs from an entry the repository imports: `@headlessui/react` and `@heroicons/react` for the root entry, an icon set's own package) | remove it |
|
|
2622
3019
|
|
|
2623
3020
|
Known gaps: `rgb()` / `hsl()` colours, colours in `.ts` maps, and
|
|
2624
3021
|
`createElement('button')` are not counted.
|
|
@@ -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-XHWT4DH2.js";import{WindowTitle as r}from"./chunk-X5LH3UOJ.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TDPY7WG.js";import"./chunk-XJX3NA37.js";import"./chunk-CQN5CFKA.js";import"./chunk-5A7MWTXX.js";import"./chunk-EXXHI5DO.js";import"./chunk-PGVUO7GB.js";import"./chunk-HZZNJHRH.js";import"./chunk-D65T4H6J.js";import{confirm as n}from"./chunk-FIGVDYJR.js";import"./chunk-AFYQTVZO.js";import"./chunk-X4FJ54PP.js";import"./chunk-S5QF4VFG.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,H]=s(0),[M,W]=s(0),[S,R]=s(f),[A,D]=s(!1),[B,O]=s(null),X=a(null),E=a(null);i(()=>{try{localStorage.setItem(d,JSON.stringify(S))}catch{}},[S]);const T=e=>{const t=b(e);t&&(w(t),L(t),C(e=>{const o=e.slice(0,z+1);return o.push(t),o}),H(e=>e+1),D(!1))},F=()=>{"undefined"!=typeof window&&window.open(k,"_blank","noopener,noreferrer")},I=()=>{const e=B?.trim();e?(R(t=>[...t,{label:e,url:k}]),O(null)):O(null)};i(()=>{if(null===B)return;X.current?.focus(),X.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||O(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==B]);const P=e=>{R(t=>t.filter((t,o)=>o!==e))},V=z>0,G=z<j.length-1,J=S.some(e=>e.url===k),U="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;
|
|
7
|
-
/* @__PURE__ */c("button",{onClick:()=>{if(z<j.length-1){const e=z+1;
|
|
8
|
-
/* @__PURE__ */c("button",{onClick:()=>
|
|
9
|
-
/* @__PURE__ */c("button",{onClick:()=>T(p),className:
|
|
6
|
+
/* @__PURE__ */c("button",{onClick:()=>{if(z>0){const e=z-1;H(e),w(j[e]),L(j[e]),D(!1)}},disabled:!V,className:U,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;H(e),w(j[e]),L(j[e]),D(!1)}},disabled:!G,className:U,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:()=>W(e=>e+1),className:U,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:()=>T(p),className:U,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
10
|
/* @__PURE__ */c("form",{onSubmit:e=>{e.preventDefault(),T(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:()=>
|
|
13
|
+
/* @__PURE__ */c("button",{"data-bookmark-popover":!0,onClick:()=>J?P(S.findIndex(e=>e.url===k)):void O(v(k)),className:U+" "+(J?"text-yellow-500":""),title:J?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:J?"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:U,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:()=>D(e=>!e),className:U,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:X,type:"text",value:B,onChange:e=>O(e.target.value),onKeyDown:e=>{"Enter"===e.key?I():"Escape"===e.key&&O(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:()=>O(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
|
|
21
|
+
/* @__PURE__ */c("button",{onClick:I,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:()=>T(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:M,iframeRef:E,openExternal:F,showHelp:A,dismissHelp:()=>D(!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 ",
|