efficient-shell 4.181.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +30 -3
  2. package/dist/{Browser-QYHW6I6J.js → Browser-WUVL7ZSM.js} +17 -17
  3. package/dist/Calculator-E2CBADVF.js +29 -0
  4. package/dist/ConfirmDialog-ZM364NPI.js +1 -0
  5. package/dist/{CurrencyConverter-QEG6RFAS.js → CurrencyConverter-LF2WHJW4.js} +7 -7
  6. package/dist/{Documents-SFNMDRYU.js → Documents-MB4TJGNY.js} +27 -27
  7. package/dist/Files-JQYIBZEP.js +87 -0
  8. package/dist/{Notepad-CNZEWFCR.js → Notepad-UQSG5BE3.js} +11 -11
  9. package/dist/PdfViewer-4VGNBBED.js +1 -0
  10. package/dist/{PomodoroTimer-T6YAQFIE.js → PomodoroTimer-XARF7DO3.js} +50 -50
  11. package/dist/{Preview-OOVIIQQG.js → Preview-LETK6AJS.js} +127 -127
  12. package/dist/Sidebar-VR4QYFQU.js +44 -0
  13. package/dist/{Spreadsheet-7NUDGOSX.js → Spreadsheet-RCHOM2RE.js} +14 -14
  14. package/dist/{Stock-BZFC2ROU.js → Stock-25FQMI2G.js} +3 -3
  15. package/dist/{Weather-S3N5OWYF.js → Weather-KLQ55R64.js} +17 -17
  16. package/dist/{WorldClock-ACC3S6OA.js → WorldClock-RMPU7D2M.js} +6 -6
  17. package/dist/apps/index.js +1 -1
  18. package/dist/bin/ui-check.js +1 -1
  19. package/dist/brand.css +1 -1
  20. package/dist/{chunk-2DX65GOO.js → chunk-2KTBZE5F.js} +1 -1
  21. package/dist/{chunk-PQLVWCUI.js → chunk-2TP4GK53.js} +5 -5
  22. package/dist/chunk-3JK2LSFU.js +1 -0
  23. package/dist/{chunk-R7EBJKN2.js → chunk-4KLBITSY.js} +28 -28
  24. package/dist/{chunk-5IWSOLLY.js → chunk-5LCZFVE4.js} +2 -2
  25. package/dist/{chunk-GMMTTWHB.js → chunk-6TCHOT4R.js} +1 -1
  26. package/dist/{chunk-3DGTIWZP.js → chunk-DBB4DB7J.js} +1 -1
  27. package/dist/{chunk-KDRAU4XQ.js → chunk-DXNCJBGY.js} +7 -7
  28. package/dist/{chunk-WNEO7TSP.js → chunk-FA6E75W5.js} +1 -1
  29. package/dist/{chunk-MSIN5HKX.js → chunk-GPNNQIRY.js} +2 -2
  30. package/dist/{chunk-LK7V32MR.js → chunk-H5NEXQTX.js} +1 -1
  31. package/dist/{chunk-T4CFSGSL.js → chunk-HKLW5KTW.js} +6 -6
  32. package/dist/{chunk-NHIGOEZF.js → chunk-JK2KQ4PF.js} +1 -1
  33. package/dist/chunk-LOXV3J6X.js +1005 -0
  34. package/dist/{chunk-QRV7RNQX.js → chunk-LTGIQ7HO.js} +14 -14
  35. package/dist/{chunk-MIAJQGWE.js → chunk-LY2G6LHW.js} +1 -1
  36. package/dist/{chunk-PE4F4MJE.js → chunk-MDI265RK.js} +4 -4
  37. package/dist/{chunk-FOEVG7RT.js → chunk-N3W6PH55.js} +11 -11
  38. package/dist/chunk-OBMR6BEP.js +1 -0
  39. package/dist/{chunk-SBDIHKLU.js → chunk-TMZVVRTA.js} +20 -20
  40. package/dist/{chunk-ZZKIKW62.js → chunk-VOY6TCS2.js} +19 -19
  41. package/dist/{chunk-VJP3R52B.js → chunk-XNHYCOPO.js} +1 -1
  42. package/dist/file-intake/index.js +1 -1
  43. package/dist/i18n/index.d.ts +1 -1
  44. package/dist/{index-HAwjA-hn.d.ts → index-CzMz_Y9A.d.ts} +168 -15
  45. package/dist/index.d.ts +3 -3
  46. package/dist/index.js +78 -78
  47. package/dist/locales/zh-Hans.d.ts +1 -1
  48. package/dist/locales/zh-Hans.js +1 -1
  49. package/dist/map/index.js +5 -5
  50. package/dist/page-tabs/index.js +5 -5
  51. package/dist/pdf/index.js +1 -1
  52. package/dist/{strings-ZbotP_I6.d.ts → strings-BEuOOURI.d.ts} +21 -0
  53. package/dist/ui/index.d.ts +2 -2
  54. package/dist/ui/index.js +1 -1
  55. package/package.json +1 -1
  56. package/dist/Calculator-4K53PFBL.js +0 -29
  57. package/dist/ConfirmDialog-X2DPOYWO.js +0 -1
  58. package/dist/Files-OX2UBSQQ.js +0 -87
  59. package/dist/PdfViewer-UZGDE4HD.js +0 -1
  60. package/dist/Sidebar-3NZRIL7F.js +0 -44
  61. package/dist/chunk-J3LIIIHJ.js +0 -1
  62. package/dist/chunk-OD5KLVU5.js +0 -1
  63. package/dist/chunk-R3PCJLQF.js +0 -993
package/README.md CHANGED
@@ -582,7 +582,7 @@ menu open; a radio closes it, as a plain item does.
582
582
  </PopupSubmenu>
583
583
  ```
584
584
 
585
- ### Editor controls — `Slider`, `ColorPicker`, `Tree`, `SortableList`, `IconRail`, `ResizeHandle`
585
+ ### Editor controls — `Slider`, `ColorPicker`, `ImageFocusPoint`, `Tree`, `SortableList`, `IconRail`, `ResizeHandle`
586
586
 
587
587
  These were folded in from `efficient-easel` under harness UI-24, because the
588
588
  website editor and the email designer each drew their own. All of them are on
@@ -650,6 +650,32 @@ Escape). `ColorSwatch` is a button painted with a colour (`selected` sets
650
650
  `aria-pressed`). Beside them are `colorSwatchClasses`, `colorFill`, `HexInput`,
651
651
  and the maths: `parseHexColor`, `hexToHsv`, `hsvToHex`.
652
652
 
653
+ **`ImageFocusPoint`** is a picture with a dot on it. Drag the dot, or press
654
+ anywhere on the picture, to choose which part of the picture stays in view when
655
+ it is cropped with `object-fit: cover`. It came from efficient-web-builder's
656
+ `ImageFocusRow`.
657
+
658
+ ```tsx
659
+ <ImageFocusPoint label="Image focus" value={focal} onChange={setFocal} image={src} />
660
+ <img src={src} style={{ objectFit: 'cover', objectPosition: focal }} />
661
+ ```
662
+
663
+ The value is a CSS `object-position` string, `"38% 64%"`, so the host renders
664
+ it as it is. A value it cannot read (empty, `left top`, `12px 4rem`) is the
665
+ centre. The picture is shown whole (`contain`), and the press is measured on the
666
+ picture itself, not on its frame, so a tall picture in a wide frame still puts
667
+ the dot under the pointer. A drag that leaves the picture keeps going and
668
+ clamps at the edge. The picture is a real `<button>`: Tab reaches it, the arrow
669
+ keys move the point 1%, Shift+arrow 10%, and every other key is left alone.
670
+ Enter and Space do nothing (a key press has no point to go to). Its name is the
671
+ label and the point: "Image focus: 38% 64%". `hint` replaces the line under the
672
+ picture (`null` hides it; the keys are still told to a screen reader),
673
+ `actions` sits at the end of that line (a "Done" button), `emptyText` shows
674
+ while there is no `image`, and `disabled` makes it read-only. The control holds
675
+ no open state: the host decides when it is on screen. The maths is exported
676
+ too: `parseFocal`, `formatFocal`, `nudgeFocal`, `focalFromRect`,
677
+ `FOCAL_DEFAULT`, `FOCAL_NUDGE_STEP`, `FOCAL_NUDGE_COARSE`.
678
+
653
679
  **`Tree`**, **`TreeRow`**, **`TreeGroup`**, **`TreeGroupLabel`**,
654
680
  **`TreeEmptySlot`** and **`TreeSeam`** draw a layers panel or an outline. They follow the WAI-ARIA
655
681
  tree pattern.
@@ -934,18 +960,19 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
934
960
  | `TimelineTrack` | The time axis both timelines are drawn on, and the reason they look and move alike rather than merely similar. A 6 px rail with a fill, a **date ruler** (month ticks always, week ticks where they clear 14 px, a month label only where it clears 72 px of the last one), dots whose shape and glyph say what kind of event they are — every one of them in the theme accent at its 600 step, the colour a primary `Button` wears, so a shape survives the greyscale print and the colour-blind reader that a hue never did, and one of two label strategies: `labels="lanes"` packs them into two rows and folds runs of the same kind into `×N` pills, `labels="active"` draws the one label a scrubber needs, `labels="none"` draws none — the bare rail, where an item's `caption` is the only text near its mark, the edge captions flank the rail on its own row, and the `pending` things are one dashed cap past a dashed tail rather than a column. `axis="compressed"` CUTS any stretch holding more than 30% of the window's time down to a fixed notch with a break glyph saying how many days it hides, and shares the rest of the track proportionally; `axis="linear"` keeps time proportional throughout; `axis="spread"` cuts nothing and keeps the 48 px floor, the idle stretches paying for it in proportion. `highlightKeys` lights one band from the leftmost named mark to the rightmost and every mark inside it; `onHoverChange` reports the mark under the pointer or the focus. A `provisional` item is hollow, never current, and outside the fill. `endMark` puts a hollow, dashed mark ON a bare rail's right edge for a day the window already runs to — an estimate, a contractual date — with the label and the date in its popover. An optional `thumb` turns it into a scrubber: give it `stops` and it becomes discrete — a drag follows the pointer but snaps to the nearest stop as it goes, arrows step stop to stop, and `valueText` says which one for a screen reader. An optional `playback` then drives that thumb along the rail by itself: the caller owns the button and the `playing` flag, the track owns the travel, because the distance between two stops is a distance in **pixels of its axis** and nobody outside knows how wide the track is. It is paced by that distance — 350 ms per 100 px, clamped to 450–1400 ms, eased in and out — so a compressed `//` notch costs 48 px of travel like any other 48 px instead of swallowing the glide, and the date chip, read back through the axis's inverse mapping, skips exactly the days the notch hides. `onArrive(key, ms)` fires on arrival and never mid-travel; the thumb then rests 700 ms before setting off again. The timing is the **wall clock**, not the frame rate: the rest is waited out on a timer and the travel is measured against the high-resolution timestamp each animation frame carries, so 700 ms is 700 ms on a throttled or occluded surface that hands out one frame a second instead of sixty — and a page that comes back after an idle stretch walks its backlog forward at one report per frame rather than announcing four at once. `onProgress({ fromKey, toKey, t, ms } | null)` hands out the in-flight position on every frame of a glide and `null` the moment there is none (arrival, pause, cancel, end, and a released drag), so a consumer can interpolate a table under the bar on the same eased `t` that places the disc; a drag reports the pointer's linear fraction between its two neighbouring stops. Flipping `playing` off freezes it where it is — keeping what is left of the rest rather than spending it — and flipping it back on carries on from there; taking hold of the thumb — a drag, a click, an arrow key — ends the playback through `onStop`. Under `prefers-reduced-motion` the walk stays, the travel goes, and `onProgress` only ever says `null`. Hovering a `×N` pill **magnifies** its stretch so the members can carry their own labels (or do it from code with `zoomRange`), and an item's `preview` fills the hover/focus popover with the document in miniature, with an "Open" footer that fires its `onOpen` — the popover has already drawn the mark's label and its date above the preview, so a consumer leaves both out of it. Also `markers` — each one free to name its own `glyph` where its kind's is not specific enough — `phases`, `edgeCaptions`, and a `pending` list for the things that have no date. Every mark takes its own pointer, including on a scrubber: the 24 px hit strip that makes a 6 px rail pressable runs UNDER the dots rather than over them, so hover, focus and click reach a report, an inspection or a shipment, while a press on the bare rail between two of them still starts a drag and a drag crossing a mark is not interrupted by it. No two marks are ever drawn on top of each other: a run whose centres are closer than one mark's width — of any kinds, folded or not — becomes ONE mark carrying `×N`, its kind's own shape and glyph where the run shares a kind and a neutral `×N` pill where it does not, named after every member it stands for. A pointer opens it: the axis magnifies around the run and each member is a mark again with its own popover, the same magnification a `×N` label pill asks for. A run that is all one DATE can never be opened — no magnification separates a coordinate from itself — so its popover lists every member with its date and its own preview instead. Folding is a drawing decision only: the thumb's stops, `onActivate` and playback all still see every mark. The marks are an `<ol>` with exactly one `aria-current="step"` and ONE tab stop — arrows traverse, Home/End jump, Enter/Space activates — and every tooltip and pill popover is hoverable, persistent and Escape-dismissible (WCAG 1.4.13). A popover is kept inside the track and inside the shell window that owns it (UI-11), the first day's mark included, and a mark carries no native `title` — its popover is the tooltip, on hover and on focus alike. Under a 320 px track it stops squeezing the axis and switches to a vertical variant. Motion is `rosh-tl-*` classes in `ui.css` behind one `prefers-reduced-motion` rule, replayed on every mount, total budget 600 ms. |
935
961
  | `ProductionTimeline` | The scrubbable production bar, drawn on `TimelineTrack` inside the shared timeline card: a sentence-case heading with the PO as its subject, the window and the lead time as meta, a Play **pill** on the right whose glyph follows its state, and a footer carrying the status line and legend chips drawn with the track's own glyphs — a shipment is a diamond with a truck in it and an inspection a disc with a flask, on the rail and in the chip alike, because every kind is painted in the one accent and the glyph is the only thing left to tell them apart. The thumb only ever rests on a date a report was filed on — drag it and it snaps report to report, arrow it and it steps one report at a time — so the bar never shows an estimate dressed as a fact. Press Play and it **travels** between them: a fresh press rewinds to the first report, the thumb reads each snapshot for 700 ms and then glides to the next with the date chip counting the days it crosses, and the status line under the bar changes on arrival rather than while it is on its way. Pausing freezes it mid-glide and pressing Play again carries on from exactly there; touching the thumb ends the playback and hands it back. The walk keeps time by the clock rather than by the frame rate, so an idle or throttled pane does not stretch it. `onScrubProgress` forwards the track's in-flight fraction — `{ fromKey, toKey, t, ms }` keyed by report id, `null` at a stop — which is how a consumer's items table moves WITH the thumb instead of jumping a whole report's worth of quantities when it lands; it is optional and undefined by default, so a caller that only reads the snapshot needs no change. `onPickReport` selects a report; the optional `onOpenReport` / `onOpenMarker` make the label above the thumb a real button that opens the document behind it. `reportLabel` names a report wherever the bar states its identity — the caption over the thumb, the popover header, each dot's accessible name, the slider's value text and the status line — and defaults to its own `progress_number`, while `resetLabel` replaces the whole of the `Back to …` button: together they are what lets a portal that must not print an internal document number run the same card. A report carries an optional `preview` for its hover/focus popover — the stage row it filed, the note attached to it — and `renderReportPreview` builds one for the reports that carry none, the convenience for a caller whose list arrives straight off an API; the report's own preview wins where both exist, and the popover prints the report number and the date itself. `useProductionTimeline(opts)` owns the slider state and the derived snapshot so the caller's items table and the bar read the same state; `STAGES` / `calcOverall` / `calcReportOverall` are the stage maths it shares with that table. **`variant="rail"`** is the bar alone, for a window whose own header already says what the order is: no card, no heading, no lead-time line, no label under the active report — every report, shipment, inspection and **invoice** (kind `invoice`, a disc with a receipt) is a dot with a popover — the thumb with its date chip, `Start` and `Est. done` flanking the rail, the estimated completion as a hollow mark on the right edge while it is still ahead, and under the rail one row: Play, the return button when the thumb has been moved, and the `<report> · date · N% overall · N pc in stock` line. The rail spreads the reports (the track's `spread` axis) rather than folding two a week apart into a `×2`. `onPlayStart` fires before the thumb moves, on a fresh run and on a resume, so a window that keeps the bar in view while its table shows something else can switch the table first. |
936
962
  | `MilestoneTimeline` | The static sibling, on the same `TimelineTrack` and in the same card: one dot per milestone on a date axis, no scrubber. `heading` and `subject` split the title from the record it is about (`Mould development` · **001F/1813**), and the meta line states when it started, how long it has run and how many milestones are dated. The axis **cuts** rather than squeezes: a stretch holding more than 30% of the window's time becomes a fixed notch with a break glyph saying how many days it hides, and every stretch that still keeps time gets at least 48 px. Labels are packed into **two lanes** by their **measured** width, and a run of same-kind revisions folds into one `DFM ×4` pill whose members keep their dots on the rail: hovering it magnifies that stretch until each member can carry its own label, a click pins the popover listing them, Escape closes it. A milestone carries an optional `glyph` (a `default` one that names `doc` is drawn as the signed drawing it is), a `preview` for the popover, and an `onOpen` that the popover's "Open" button fires — `onClick` selects the dot on the bar, `onOpen` opens the drawing behind it, and a card that declares only the first keeps its Open button because the footer falls back to it. Label and date are printed by the popover above the preview, so a consumer supplies neither. A milestone with **no date gets no position**, because the only position available would be a date the record does not have: it is listed beside the bar under "Not yet reached" (two rows, then a real `+N more` button) and takes no part in the range, the fill or a phase bracket. `phase` groups of 2+ get a bracket marking parallel work, the fill never runs past today, and today gets its dashed rule and tag even when it IS the right edge — the commonest case, because a programme with no finish date ends at today. **`variant="rail"`** is the bar alone, for a window whose stepper already names the stages: no card, no heading, no label under any dot — every milestone is a dot with a popover, a revision carries a two-character `caption` over its dot (`v3`; captions that would overprint collapse to `v1–v4`), `edgeCaptions` flank the rail on its own row (`{ start: 'Start', end: 'Ready' }`), nothing is cut (the `spread` axis: every stretch at least 48 px, the idle ones drawn short rather than notched), and what has not happened is one dashed **cap** at the end of a dashed tail — a real button announced as the last undated milestone, naming the rest in its popover. The stepper and the rail then point at each other: `highlightKeys` lights one soft band from the leftmost named milestone to the rightmost and every dot inside it (a single key rings one dot; the cap answers to the pending milestone's key), and `onHoverChange` reports the milestone under the pointer or the focus, `null` on leave. A `provisional` milestone — a sample ORDERED, drawn where the shipment will be — is hollow, never "where we are", and outside the fill. |
937
- | `SettingRow` | One setting: its name and current value on one baseline, the explanation at full width beneath in a smaller voice. The value never shrinks, so a short badge keeps its line instead of folding into a narrow column. Handles a value that is a control (`controlId` labels it), a read-only fact, a value nobody knows (an em dash in the faint ink, never an empty cell), and a `quiet` row nothing can write yet. `DescriptionList` is the record-detail neighbour and stays that. |
963
+ | `SettingRow` | One setting in a settings card: the name with its explanation stacked under it on the left, the control or value on the right (it never shrinks), and an optional body (`children`) that opens beneath behind its own line. Each row draws its own top line (`divider`) and padding, so rows stack in a card. One prop per slot: `label` (the name) and `value` (a control or a read-only fact). `size="compact"` is the editor-panel row. Handles a value that is a control (`controlId` labels it), a read-only fact, a value nobody knows (an em dash in the faint ink, never an empty cell), and a `quiet` row nothing can write yet. A name over a field (children, no value) draws nothing on the right. `DescriptionList` is the record-detail neighbour and stays that. |
938
964
  | `Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `Radio`, `Switch`, `FormField`, `Label` | Form controls — controlled (`value`/`onChange`); `Input`/`Textarea` forward native props for react-hook-form. `Button` and `Switch` take `disabledReason`: persistent text beside the control wired with `aria-describedby`, never a `title` — a tooltip needs a hover a disabled control does not reliably get. `Switch` also takes `disabledReasonId`, so a panel whose controls are all dead for one reason states it once and points every control at it. A `Select` option carries an optional `description`, rendered as a second muted line **in the open list only** — the closed trigger stays one line, so one explained option cannot change the height of every field in the row. `NativeSelect` (and so `Select` on touch) ignores it: an `<option>` holds text and nothing else. An option also takes `lang` (a BCP 47 tag) when its label is written in another language than the page's — a language picker naming each language in its own words: it goes on the option in the open list, on the native `<option>` and on the closed trigger's label while chosen, so a screen reader and the font fallback treat each name as its own language (WCAG 3.1.2). On the desktop listbox, `title` joins `aria-label` / `aria-labelledby` / `aria-describedby` on the VISIBLE trigger button — it used to ride the native spread onto the `sr-only` `<select>`, where a tooltip has no pointer that can reach it and a fallback name sits on an `aria-hidden` element. `name`, `required` and the forwarded ref stay on the native control, which is what it is for. A `Checkbox` or `Radio` with both a `label` and a `description` is NAMED by the label and DESCRIBED by the description (`aria-labelledby` / `aria-describedby`); the whole row is still its `<label>`, so its words still tick it. |
939
965
  | `buttonClasses`, `iconButtonClasses` | `Button`'s and `IconButton`'s look as a class string, for something that must navigate and so cannot be a `<button>`: `<Link href="/organisations/new" className={buttonClasses({ variant: 'secondary' })}>Add a business</Link>`. Same options and defaults as the component (`variant`, `size`, `block`, `className`), and the components render through these functions, so the two cannot drift. What the string cannot give an `<a>`: a disabled state (`disabled:` never matches an anchor — drop the `href`, add `role="link"` and `aria-disabled="true"`, or render a disabled `Button`), and the cursor (an `<a href>` gets the browser's pointer, a `<button>` the host's preflight; set it once for both). Keep its `inline-flex` — ask for `block` rather than passing a display utility. An icon-only link still needs the `aria-label` `IconButton` would have required. Client references, like every kit function: call them in a `'use client'` file. |
940
966
  | `labelClasses` | `Label`'s look as a class string, for a `<label>` a host lays out itself — beside a check box, in a row with its control, `sr-only`: `<label htmlFor="x" className={labelClasses({ inline: true, className: 'flex gap-2' })}>`. `inline: true` drops `Label`'s block layout (`mb-1 block`) and keeps its size, weight and ink; without it the string is exactly what `Label` renders. A client reference: call it in a `'use client'` file. |
941
967
  | `Segmented` | A row of mutually exclusive options, all visible: a button group, or with `name` a real radio group that submits. Named by `label`, or by an element already on the page (`aria-labelledby`, a list row's own heading) and described by another (`aria-describedby`); an option's `title` says on hover what choosing it means. |
968
+ | `DisplayChips` | Which parts of a section are shown, one chip per part ("Breadcrumb", "Stats", "Intro text"): blue with an eye while shown, grey with a crossed eye while hidden, and a press flips it. The chips wrap, so a long list takes a few short lines instead of one switch row per part. Controlled: `items` (`{ id, label, shown, disabled?, disabledReason? }`) and `onChange(id, shown)`. The group is named by `label` or `aria-labelledby`. Each chip is a real toggle button (`aria-pressed`), named by its part and described by its state in words (`displayChips.shown` / `hidden`), with a hover label that says what a press does; Space and Enter are the button's own. A chip with `disabledReason` stays focusable, its press does nothing and the reason is said, as on `Button`. Not `Segmented` (one choice of several) and not `TagInput` (typing values in). |
942
969
  | `RadioGroup` | One choice from a short visible list, with the group chrome each panel used to rebuild: a `role="radiogroup"` named by its own label (`aria-labelledby`, because `<label for>` cannot name a div), the `hint`/`error`/`required` wiring borrowed from `FormField`, and `orientation` for a row that wraps rather than compresses. The options are real native radios sharing one `name`, which is what gives the group its arrow keys, its disabled-skipping and its single tab stop — all from the user agent, none of it hand-rolled. `value: null` is "nothing chosen yet", not a silent first option. |
943
970
  | `Snippet` | A value the user copies rather than reads — an endpoint, a tenant id, a shell command. Monospace, truncated with the whole value on its `title` (`multiline` wraps instead), and a copy button that flips to a check for two seconds and says "Copied" through an `aria-live` region. `value` is what reaches the clipboard and `children` is what is shown, so a masked key displays masked and copies whole. `symbol` prints a leading glyph that is never copied and has **no default** — a URL has no `$` in front of it. A refused clipboard toasts rather than looking like it worked. Not a read-only `Input`: a value that can only be copied is not a control and should not take a tab stop or invite an edit. `id` and `aria-describedby` land on the COPY BUTTON — the only focusable thing in the row, and a labelable one — so a wrapping `FormField` really does label it and its hint is really announced; with `hideCopyButton` they fall back to the box, because an id that resolves to nothing is not wiring. |
944
971
  | `Kbd` | The badge the shortcut constants (`CMD_ENTER`, `ALT_SHIFT_E`, …) are shown in — a real `<kbd>`, two rungs, `className` layered on so a portal can tint it. It renders the string it is handed; which symbol a platform uses is what the constants already decide. |
945
972
  | `FormErrorSummary` | The error list at the top of a failed form (WCAG 3.3.1, the GOV.UK pattern): takes focus when errors appear, each message is a link that focuses the offending control by its `FormField` id. Renders nothing while `errors` is empty. |
946
973
  | `TagInput` | Multi-value field — chosen values as removable chips, typing filters the option list in the same dropdown SearchableSelect uses. `allowFreeText` admits unlisted entries; Backspace in the empty input removes the last chip. The value array stays duplicate-free by construction. |
947
974
  | `DatePicker`, `TimePicker`, `DateTimePicker`, `DateRangePicker` | Date/time fields, all careful to speak LOCAL dates and wall-clock times (never `toISOString`). `TimePicker` and `DateTimePicker` wrap the platform's own inputs in the kit's field styling; `TimePicker` hands back an `HH:MM` string — a time of day names no calendar day, so it never invents a Date. **`DatePicker`** opens the kit's `Calendar` in a popover (`native` swaps in the platform's date input). Its value posts from a hidden input that takes `name` and `form`; `required` adds "Required" to the trigger's description (`datePicker.required`; a button may not carry `aria-required`) and drops Clear from the footer (`clearable` overrides either way); `invalid` paints the field, and the reader hears the error message the host links through `aria-describedby` (`FormField` does) — the trigger carries no `aria-invalid`, which a button may not either; Today is offered only when `min`/`max` allow today; `aria-*`, `data-*`, `title`, `tabIndex`, `autoFocus`, `onFocus` and `onBlur` reach the trigger, while what only a text box can use (`readOnly`, `pattern`, `autoComplete`…) applies with `native` alone. Named by a `<label htmlFor={id}>` or `aria-labelledby`, the trigger is described by the date spelled out. A browser never validates a hidden input, so `required` cannot block a submit in the popover. **`DateRangePicker`** is a from/to calendar with presets in one popover: side by side on a desktop; where that does not fit the room the box allows (a phone, a narrow shell window — measured, not a media query), the panel takes the full width between the margins and stacks, the presets a wrapping row of chips above the calendar and From/To one line each. `fullWidth` fills a filter-grid cell instead of shrink-wrapping its label. `id` (on the trigger button, for `<label htmlFor>`), `aria-labelledby` and `aria-label` name the trigger, and the chosen range is then its description ("Period, button, 1 August 2026 to 3 October 2026"). `name={{ from, to }}` posts the applied range as two hidden `YYYY-MM-DD` inputs (`''` when unset), so a GET form or a server action reads it as it read two date inputs. `required` adds "Required" to the trigger's description (`dateRange.required`) and, unless `clearable` is given, removes Clear. `min` / `max` (`YYYY-MM-DD`) bound it as they bound `DatePicker`: days outside are disabled, a preset whose range reaches outside is not offered (none at all on a range that starts today — time off, a booking — and then no Custom either), and with nothing chosen the panel opens on the first month that has a day to pick. Both pickers take `weekStartsOn` (0 Sunday, 1 Monday); without it the week starts where the provider's `locale` says. |
948
- | `Card`, `StatCard` | Surface panel (optional header/footer) + dashboard metric tile. |
975
+ | `Card`, `StatCard` | Surface panel (optional header/footer; `description` puts one sentence under the title, outside the heading) + dashboard metric tile. |
949
976
  | `Avatar`, `AvatarGroup` | User avatar with initials fallback + status dot; overlapping stack with +N overflow. |
950
977
  | `Banner` | Static in-flow alert (`tone`: info / success / warning / danger). `action` renders a control at the right edge — after the text, so a screen reader reaches the problem before the remedy, and before the dismiss ×, so "Update card" is never past "close this". Vertically centred against the words and `shrink-0`, so a message that wraps does not drag the button down or squeeze it — and on a narrow banner (a phone, a narrow column: the words keep at least 12rem) it drops UNDER the words rather than leave them a column one word wide. A banner that names a condition and leaves the fix three menus away is the usual shape of a notice nobody acts on. |
951
978
  | `Tabs`, `Accordion`, `Tooltip` | Controlled tab strip, collapsible sections, frosted hover/focus tooltip. A tooltip's `side` is a preference: near an edge the bubble slides along that side to stay 8 px clear of it, and flips to the opposite side when its own has no room, keeping inside the screen and inside the shell window that owns its trigger (UI-11). It never leaves that box: a bubble wider than the room wraps, and one taller than the room is cut off at it. The bubble is drawn on `<body>`, in a `fixed` layer laid over its trigger, so a scrolling pane or an `overflow-hidden` box around the trigger cannot cut it off; it follows the trigger when that moves and closes once the trigger has scrolled out of sight. A small arrow on the edge facing the trigger points at the trigger's centre wherever the bubble ended up, kept clear of the bubble's corners; it is the bubble's own surface (`.rosh-tooltip-arrow` in `ui.css`), solid and seamless under `rosh-reduce-transparency`, and hidden from assistive technology. |
@@ -1,31 +1,31 @@
1
- import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-MSIN5HKX.js";import{WindowTitle as r}from"./chunk-SBDIHKLU.js";import"./chunk-DSSLOQDW.js";import"./chunk-GMMTTWHB.js";import"./chunk-OD5KLVU5.js";import"./chunk-PQLVWCUI.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import{confirm as a}from"./chunk-ZZKIKW62.js";import"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import{ChevronLeftFallback as n,ChevronRightFallback as l,RefreshFallback as s,HomeFallback as i,StarFallback as c,ExternalFallback as m,HelpFallback as d,NoSymbolFallback as u}from"./chunk-XUF7VW4U.js";import"./chunk-J3LIIIHJ.js";import"./chunk-C3QOHMR6.js";import{Icon as h}from"./chunk-YKZHOXI4.js";import{useRef as p,useEffect as b,useState as g}from"react";import{jsxs as f,jsx as k,Fragment as x}from"react/jsx-runtime";var w="react-os-shell:browser-bookmarks",y="react-os-shell:browser-homepage",v="https://en.wikipedia.org/wiki/Main_Page",N=[{label:"Wikipedia",url:"https://en.wikipedia.org/wiki/Main_Page"},{label:"DevDocs",url:"https://devdocs.io"},{label:"Example",url:"https://example.com"}],j=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 C(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&&j.has(t[2].toLowerCase())}(t)?"https://"+t:"https://duckduckgo.com/?q="+encodeURIComponent(t):""}var z=["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 W(){if("undefined"==typeof window)return N;try{const e=localStorage.getItem(w);if(!e)return N;const t=JSON.parse(e);if(Array.isArray(t)&&t.every(e=>"string"==typeof e?.url))return t}catch{}return N}function S(){return"undefined"==typeof window?v:localStorage.getItem(y)||v}function H(){const u=p(void 0);void 0===u.current&&(u.current=e()),b(()=>{null!=u.current&&t(u.current)},[]);const x=u.current?C(u.current.url):"",[v,N]=g(S),[j,z]=g(x||v),[H,I]=g(j),[D,O]=g([j]),[U,T]=g(0),[E,F]=g(0),[K,M]=g(W),[X,P]=g(!1),[J,Z]=g(null),q=p(null),A=p(null);b(()=>{try{localStorage.setItem(w,JSON.stringify(K))}catch{}},[K]);const Q=e=>{const t=C(e);t&&(z(t),I(t),O(e=>{const o=e.slice(0,U+1);return o.push(t),o}),T(e=>e+1),P(!1))},B=()=>{"undefined"!=typeof window&&window.open(j,"_blank","noopener,noreferrer")},G=()=>{const e=J?.trim();e?(M(t=>[...t,{label:e,url:j}]),Z(null)):Z(null)};b(()=>{if(null===J)return;q.current?.focus(),q.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||Z(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==J]);const $=e=>{M(t=>t.filter((t,o)=>o!==e))},V=U>0,Y=U<D.length-1,_=K.some(e=>e.url===j),ee="p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30";/* @__PURE__ */
1
+ import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-GPNNQIRY.js";import{WindowTitle as r}from"./chunk-TMZVVRTA.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TCHOT4R.js";import"./chunk-OBMR6BEP.js";import"./chunk-2TP4GK53.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import{confirm as a}from"./chunk-VOY6TCS2.js";import"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import{ChevronLeftFallback as n,ChevronRightFallback as l,RefreshFallback as s,HomeFallback as i,StarFallback as c,ExternalFallback as m,HelpFallback as d,NoSymbolFallback as u}from"./chunk-XUF7VW4U.js";import"./chunk-3JK2LSFU.js";import"./chunk-C3QOHMR6.js";import{Icon as h}from"./chunk-YKZHOXI4.js";import{useRef as p,useEffect as b,useState as g}from"react";import{jsxs as f,jsx as k,Fragment as x}from"react/jsx-runtime";var w="react-os-shell:browser-bookmarks",y="react-os-shell:browser-homepage",v="https://en.wikipedia.org/wiki/Main_Page",N=[{label:"Wikipedia",url:"https://en.wikipedia.org/wiki/Main_Page"},{label:"DevDocs",url:"https://devdocs.io"},{label:"Example",url:"https://example.com"}],j=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 C(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&&j.has(t[2].toLowerCase())}(t)?"https://"+t:"https://duckduckgo.com/?q="+encodeURIComponent(t):""}var z=["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 R(){if("undefined"==typeof window)return N;try{const e=localStorage.getItem(w);if(!e)return N;const t=JSON.parse(e);if(Array.isArray(t)&&t.every(e=>"string"==typeof e?.url))return t}catch{}return N}function W(){return"undefined"==typeof window?v:localStorage.getItem(y)||v}function S(){const u=p(void 0);void 0===u.current&&(u.current=e()),b(()=>{null!=u.current&&t(u.current)},[]);const x=u.current?C(u.current.url):"",[v,N]=g(W),[j,z]=g(x||v),[S,L]=g(j),[O,D]=g([j]),[E,F]=g(0),[P,U]=g(0),[I,M]=g(R),[X,A]=g(!1),[q,G]=g(null),J=p(null),K=p(null);b(()=>{try{localStorage.setItem(w,JSON.stringify(I))}catch{}},[I]);const Q=e=>{const t=C(e);t&&(z(t),L(t),D(e=>{const o=e.slice(0,E+1);return o.push(t),o}),F(e=>e+1),A(!1))},Z=()=>{"undefined"!=typeof window&&window.open(j,"_blank","noopener,noreferrer")},B=()=>{const e=q?.trim();e?(M(t=>[...t,{label:e,url:j}]),G(null)):G(null)};b(()=>{if(null===q)return;J.current?.focus(),J.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||G(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==q]);const V=e=>{M(t=>t.filter((t,o)=>o!==e))},Y=E>0,$=E<O.length-1,_=I.some(e=>e.url===j),ee="p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30";/* @__PURE__ */
2
2
  return f("div",{className:"relative flex flex-col h-full bg-white",children:[
3
- /* @__PURE__ */k(r,{title:`Browser - ${L(j)}`}),
3
+ /* @__PURE__ */k(r,{title:`Browser - ${T(j)}`}),
4
4
  /* @__PURE__ */k(o,{app:"browser"}),
5
5
  /* @__PURE__ */f("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__ */k("button",{onClick:()=>{if(U>0){const e=U-1;T(e),z(D[e]),I(D[e]),P(!1)}},disabled:!V,className:ee,title:"Back",children:/* @__PURE__ */k(h,{name:"chevron-left",fallback:n,className:"h-4 w-4",strokeWidth:2})}),
7
- /* @__PURE__ */k("button",{onClick:()=>{if(U<D.length-1){const e=U+1;T(e),z(D[e]),I(D[e]),P(!1)}},disabled:!Y,className:ee,title:"Forward",children:/* @__PURE__ */k(h,{name:"chevron-right",fallback:l,className:"h-4 w-4",strokeWidth:2})}),
8
- /* @__PURE__ */k("button",{onClick:()=>F(e=>e+1),className:ee,title:"Refresh",children:/* @__PURE__ */k(h,{name:"refresh",fallback:s,className:"h-4 w-4",strokeWidth:1.8})}),
6
+ /* @__PURE__ */k("button",{onClick:()=>{if(E>0){const e=E-1;F(e),z(O[e]),L(O[e]),A(!1)}},disabled:!Y,className:ee,title:"Back",children:/* @__PURE__ */k(h,{name:"chevron-left",fallback:n,className:"h-4 w-4",strokeWidth:2})}),
7
+ /* @__PURE__ */k("button",{onClick:()=>{if(E<O.length-1){const e=E+1;F(e),z(O[e]),L(O[e]),A(!1)}},disabled:!$,className:ee,title:"Forward",children:/* @__PURE__ */k(h,{name:"chevron-right",fallback:l,className:"h-4 w-4",strokeWidth:2})}),
8
+ /* @__PURE__ */k("button",{onClick:()=>U(e=>e+1),className:ee,title:"Refresh",children:/* @__PURE__ */k(h,{name:"refresh",fallback:s,className:"h-4 w-4",strokeWidth:1.8})}),
9
9
  /* @__PURE__ */k("button",{onClick:()=>Q(v),className:ee,title:"Home",children:/* @__PURE__ */k(h,{name:"home",fallback:i,className:"h-4 w-4",strokeWidth:1.8})}),
10
- /* @__PURE__ */k("form",{onSubmit:e=>{e.preventDefault(),Q(H)},className:"flex-1 flex items-center mx-1",children:/* @__PURE__ */f("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:[
10
+ /* @__PURE__ */k("form",{onSubmit:e=>{e.preventDefault(),Q(S)},className:"flex-1 flex items-center mx-1",children:/* @__PURE__ */f("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__ */k("span",{className:"px-2 text-gray-400 text-xs",children:j.startsWith("https://")?"🔒":"⚠️"}),
12
- /* @__PURE__ */k("input",{type:"text",value:H,onChange:e=>I(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…"}),j!==H&&/* @__PURE__ */k("button",{type:"button",onClick:()=>I(j),className:"px-1.5 text-[10px] text-gray-400 hover:text-gray-700",title:"Reset to current URL",children:"×"})]})}),
13
- /* @__PURE__ */k("button",{"data-bookmark-popover":!0,onClick:()=>_?$(K.findIndex(e=>e.url===j)):void Z(L(j)),className:ee+" "+(_?"text-yellow-500":""),title:_?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */k(h,{name:"star",fallback:c,className:"h-4 w-4",fill:_?"currentColor":"none",strokeWidth:1.5})}),
14
- /* @__PURE__ */k("button",{onClick:B,className:ee,title:"Open in new tab",children:/* @__PURE__ */k(h,{name:"external",fallback:m,className:"h-4 w-4",strokeWidth:1.5})}),
15
- /* @__PURE__ */k("button",{onClick:()=>P(e=>!e),className:ee,title:"Embedding help",children:/* @__PURE__ */k(h,{name:"help",fallback:d,className:"h-4 w-4",strokeWidth:1.5})})]}),null!==J&&/* @__PURE__ */f("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:[
12
+ /* @__PURE__ */k("input",{type:"text",value:S,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…"}),j!==S&&/* @__PURE__ */k("button",{type:"button",onClick:()=>L(j),className:"px-1.5 text-[10px] text-gray-400 hover:text-gray-700",title:"Reset to current URL",children:"×"})]})}),
13
+ /* @__PURE__ */k("button",{"data-bookmark-popover":!0,onClick:()=>_?V(I.findIndex(e=>e.url===j)):void G(T(j)),className:ee+" "+(_?"text-yellow-500":""),title:_?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */k(h,{name:"star",fallback:c,className:"h-4 w-4",fill:_?"currentColor":"none",strokeWidth:1.5})}),
14
+ /* @__PURE__ */k("button",{onClick:Z,className:ee,title:"Open in new tab",children:/* @__PURE__ */k(h,{name:"external",fallback:m,className:"h-4 w-4",strokeWidth:1.5})}),
15
+ /* @__PURE__ */k("button",{onClick:()=>A(e=>!e),className:ee,title:"Embedding help",children:/* @__PURE__ */k(h,{name:"help",fallback:d,className:"h-4 w-4",strokeWidth:1.5})})]}),null!==q&&/* @__PURE__ */f("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__ */k("div",{className:"text-[11px] font-semibold uppercase tracking-wide text-gray-500 mb-1",children:"Bookmark this page"}),
17
17
  /* @__PURE__ */k("div",{className:"text-[11px] text-gray-400 truncate mb-2 font-mono",title:j,children:j}),
18
- /* @__PURE__ */k("input",{ref:q,type:"text",value:J,onChange:e=>Z(e.target.value),onKeyDown:e=>{"Enter"===e.key?G():"Escape"===e.key&&Z(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"}),
18
+ /* @__PURE__ */k("input",{ref:J,type:"text",value:q,onChange:e=>G(e.target.value),onKeyDown:e=>{"Enter"===e.key?B():"Escape"===e.key&&G(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__ */f("div",{className:"flex justify-end gap-1 mt-2",children:[
20
- /* @__PURE__ */k("button",{onClick:()=>Z(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
21
- /* @__PURE__ */k("button",{onClick:G,disabled:!J.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__ */f("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===K.length&&/* @__PURE__ */k("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),K.map((e,t)=>/* @__PURE__ */f("button",{onClick:()=>Q(e.url),onContextMenu:async o=>{o.preventDefault();await a({title:"Remove bookmark",message:`"${e.label}" will be removed from your bookmarks.`,confirmLabel:"Remove",variant:"danger"})&&$(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:[
20
+ /* @__PURE__ */k("button",{onClick:()=>G(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
21
+ /* @__PURE__ */k("button",{onClick:B,disabled:!q.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__ */f("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===I.length&&/* @__PURE__ */k("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),I.map((e,t)=>/* @__PURE__ */f("button",{onClick:()=>Q(e.url),onContextMenu:async o=>{o.preventDefault();await a({title:"Remove bookmark",message:`"${e.label}" will be removed from your bookmarks.`,confirmLabel:"Remove",variant:"danger"})&&V(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__ */k("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__ */k("div",{className:"flex-1"}),
25
25
  /* @__PURE__ */k("button",{onClick:()=>{N(j);try{localStorage.setItem(y,j)}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__ */k("div",{className:"flex-1 relative min-h-0 bg-gray-50",children:/* @__PURE__ */k(R,{url:j,iframeKey:E,iframeRef:A,openExternal:B,showHelp:X,dismissHelp:()=>P(!1)})})]})}function L(e){try{return new URL(e).hostname.replace(/^www\./,"")}catch{return e}}function R({url:e,iframeKey:t,iframeRef:o,openExternal:r,showHelp:a,dismissHelp:n}){const[l,s]=g(!1);b(()=>{s(!1)},[e]);return function(e){try{const t=new URL(e).hostname.toLowerCase();return z.some(e=>t===e||t.endsWith("."+e))}catch{return!1}}(e)&&!l?/* @__PURE__ */k("div",{className:"absolute inset-0 flex items-center justify-center bg-white p-8",children:/* @__PURE__ */f("div",{className:"max-w-md text-center",children:[
26
+ /* @__PURE__ */k("div",{className:"flex-1 relative min-h-0 bg-gray-50",children:/* @__PURE__ */k(H,{url:j,iframeKey:P,iframeRef:K,openExternal:Z,showHelp:X,dismissHelp:()=>A(!1)})})]})}function T(e){try{return new URL(e).hostname.replace(/^www\./,"")}catch{return e}}function H({url:e,iframeKey:t,iframeRef:o,openExternal:r,showHelp:a,dismissHelp:n}){const[l,s]=g(!1);b(()=>{s(!1)},[e]);return function(e){try{const t=new URL(e).hostname.toLowerCase();return z.some(e=>t===e||t.endsWith("."+e))}catch{return!1}}(e)&&!l?/* @__PURE__ */k("div",{className:"absolute inset-0 flex items-center justify-center bg-white p-8",children:/* @__PURE__ */f("div",{className:"max-w-md text-center",children:[
27
27
  /* @__PURE__ */k(h,{name:"no-symbol",fallback:u,className:"h-14 w-14 mx-auto text-gray-300 mb-3",strokeWidth:1.2}),
28
- /* @__PURE__ */f("h3",{className:"text-base font-semibold text-gray-800 mb-1",children:[L(e)," can't be embedded"]}),
28
+ /* @__PURE__ */f("h3",{className:"text-base font-semibold text-gray-800 mb-1",children:[T(e)," can't be embedded"]}),
29
29
  /* @__PURE__ */f("p",{className:"text-sm text-gray-500 mb-4",children:["This site sends an ",
30
30
  /* @__PURE__ */k("span",{className:"font-mono text-xs",children:"X-Frame-Options"})," ","or ",
31
31
  /* @__PURE__ */k("span",{className:"font-mono text-xs",children:"Content-Security-Policy"})," header that refuses iframe embedding. The browser blocks the load before our app can do anything about it."]}),
@@ -40,4 +40,4 @@ return f("div",{className:"relative flex flex-col h-full bg-white",children:[
40
40
  /* @__PURE__ */f("p",{children:["Hit the ",
41
41
  /* @__PURE__ */k("span",{className:"font-medium",children:"↗"})," button to open the page in a real new tab. Sites that ",
42
42
  /* @__PURE__ */k("em",{children:"do"})," allow embedding (Wikipedia, DevDocs, your own apps) work fine in here."]}),
43
- /* @__PURE__ */k("button",{onClick:n,className:"mt-2 text-[11px] text-blue-600 hover:underline",children:"Got it"})]})]})}export{H as default};
43
+ /* @__PURE__ */k("button",{onClick:n,className:"mt-2 text-[11px] text-blue-600 hover:underline",children:"Got it"})]})]})}export{S as default};
@@ -0,0 +1,29 @@
1
+ import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-LY2G6LHW.js";import{useWidgetSettings as n}from"./chunk-TMZVVRTA.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TCHOT4R.js";import"./chunk-2TP4GK53.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import"./chunk-VOY6TCS2.js";import"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import{CheckFallbackB as l,CopyFallbackB as r}from"./chunk-XUF7VW4U.js";import"./chunk-3JK2LSFU.js";import"./chunk-C3QOHMR6.js";import{Icon as c}from"./chunk-YKZHOXI4.js";import{useState as a,useRef as i,useCallback as o,useEffect as s}from"react";import{jsxs as u,Fragment as d,jsx as h}from"react/jsx-runtime";var m="calc_appearance";function b(){const[b,k]=a("0"),[p,g]=a(null),[f,y]=a(null),[x,N]=a(!1),[v,C]=a(""),[$,j]=a([]),[T,w]=a(!1),[S,F]=a(()=>e(m)),[W,E]=a(!1),[O,A]=a(S),H=i(null);n(o(()=>{A({...S}),E(!0)},[S]));const L=o(e=>{C(""),k(t=>x||"0"===t?(N(!1),"."===e?"0.":e):"."===e&&t.includes(".")?t:t+e)},[x]),X=o(()=>{k("0"),g(null),y(null),N(!1)},[]),D=o((e,t,n)=>{switch(n){case"+":return e+t;case"-":return e-t;case"×":return e*t;case"÷":return 0===t?0:e/t;default:return t}},[]),R=o(e=>{const t=parseFloat(b);if(null!==p&&f&&!x){const e=D(p,t,f);k(String(e)),g(e),j(n=>[`${p} ${f} ${t} = ${e}`,...n].slice(0,20))}else g(t);y(e),N(!0)},[b,p,f,x,D]),Z=o(()=>{if(null===p||!f)return;const e=parseFloat(b),t=D(p,e,f),n=`${p} ${f} ${e} =`;j(e=>[`${n} ${t}`,...e].slice(0,20)),C(n),k(String(t)),g(null),y(null),N(!0)},[b,p,f,D]),J=o(()=>{k(e=>String(parseFloat(e)/100))},[]),U=o(()=>{k(e=>e.startsWith("-")?e.slice(1):"0"===e?e:"-"+e)},[]);s(()=>{const e=e=>{const t=e.target.tagName;"INPUT"!==t&&"TEXTAREA"!==t&&"SELECT"!==t&&(e.key>="0"&&e.key<="9"?L(e.key):"."===e.key?L("."):"+"===e.key?R("+"):"-"===e.key?R("-"):"*"===e.key?R("×"):"/"===e.key?(e.preventDefault(),R("÷")):"Enter"===e.key||"="===e.key?("Enter"===e.key&&e.target instanceof Node&&H.current?.contains(e.target)&&e.preventDefault(),Z()):"Escape"===e.key?X():"Backspace"===e.key?k(e=>e.length>1?e.slice(0,-1):"0"):"%"===e.key&&J())};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[L,R,Z,X,J]);const V="flex items-center justify-center rounded-lg text-sm font-medium transition-colors",M=`${V} bg-white border border-gray-200 text-gray-900 hover:bg-gray-50 active:bg-gray-100`,P=`${V} bg-blue-50 border border-blue-200 text-blue-700 hover:bg-blue-100 active:bg-blue-200`,Q=`${V} bg-gray-100 border border-gray-200 text-gray-600 hover:bg-gray-200 active:bg-gray-300`,Y=`${V} bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800`;/* @__PURE__ */
2
+ return u(d,{children:[
3
+ /* @__PURE__ */u("div",{ref:H,className:"flex flex-col h-full",style:{opacity:S.activeOpacity/100,backdropFilter:S.activeBlur>0?`blur(${S.activeBlur}px)`:void 0},children:[
4
+ /* @__PURE__ */u("div",{className:"bg-slate-200 rounded-t-lg px-4 py-2 group/display relative flex flex-col justify-end min-h-[120px] border-b border-slate-300",children:[
5
+ /* @__PURE__ */h("div",{className:"flex flex-col justify-end flex-1 overflow-hidden",children:$.slice(0,3).reverse().map((e,t)=>/* @__PURE__ */h("div",{className:"text-[11px] text-slate-400 text-right font-mono truncate",children:e},t))}),(v||f&&null!==p)&&/* @__PURE__ */h("div",{className:"text-base text-slate-600 text-right font-mono",children:v||`${p} ${f} ${x?"":b}`}),
6
+ /* @__PURE__ */u("div",{className:"flex items-center gap-2",children:[
7
+ /* @__PURE__ */h("button",{onClick:()=>{navigator.clipboard.writeText(b),w(!0),setTimeout(()=>w(!1),1200)},title:"Copy result",className:"shrink-0 text-slate-300 hover:text-slate-500 transition-colors opacity-0 group-hover/display:opacity-100",children:/* @__PURE__ */h(c,T?{name:"check",fallback:l,className:"h-5 w-5 text-green-500",strokeWidth:2}:{name:"copy",fallback:r,className:"h-5 w-5",strokeWidth:2})}),
8
+ /* @__PURE__ */h("div",{className:"text-right text-4xl font-mono font-bold text-slate-800 truncate flex-1",children:(e=>{const t=parseFloat(e);return isNaN(t)||e.endsWith(".")||e.endsWith(".0")||Math.abs(t)<1e15&&e.length<16?e:t.toExponential(6)})(b)})]})]}),
9
+ /* @__PURE__ */u("div",{className:"grid grid-cols-4 grid-rows-5 gap-1.5 flex-1 min-h-0 p-2",children:[
10
+ /* @__PURE__ */h("button",{onClick:X,className:Q,children:"AC"}),
11
+ /* @__PURE__ */h("button",{onClick:U,className:Q,children:"+/−"}),
12
+ /* @__PURE__ */h("button",{onClick:J,className:Q,children:"%"}),
13
+ /* @__PURE__ */h("button",{onClick:()=>R("÷"),className:`${P} ${"÷"===f?"ring-2 ring-blue-400":""}`,children:"÷"}),
14
+ /* @__PURE__ */h("button",{onClick:()=>L("7"),className:M,children:"7"}),
15
+ /* @__PURE__ */h("button",{onClick:()=>L("8"),className:M,children:"8"}),
16
+ /* @__PURE__ */h("button",{onClick:()=>L("9"),className:M,children:"9"}),
17
+ /* @__PURE__ */h("button",{onClick:()=>R("×"),className:`${P} ${"×"===f?"ring-2 ring-blue-400":""}`,children:"×"}),
18
+ /* @__PURE__ */h("button",{onClick:()=>L("4"),className:M,children:"4"}),
19
+ /* @__PURE__ */h("button",{onClick:()=>L("5"),className:M,children:"5"}),
20
+ /* @__PURE__ */h("button",{onClick:()=>L("6"),className:M,children:"6"}),
21
+ /* @__PURE__ */h("button",{onClick:()=>R("-"),className:`${P} ${"-"===f?"ring-2 ring-blue-400":""}`,children:"−"}),
22
+ /* @__PURE__ */h("button",{onClick:()=>L("1"),className:M,children:"1"}),
23
+ /* @__PURE__ */h("button",{onClick:()=>L("2"),className:M,children:"2"}),
24
+ /* @__PURE__ */h("button",{onClick:()=>L("3"),className:M,children:"3"}),
25
+ /* @__PURE__ */h("button",{onClick:()=>R("+"),className:`${P} ${"+"===f?"ring-2 ring-blue-400":""}`,children:"+"}),
26
+ /* @__PURE__ */h("button",{onClick:()=>L("0"),className:`${M} col-span-2`,children:"0"}),
27
+ /* @__PURE__ */h("button",{onClick:()=>L("."),className:M,children:"."}),
28
+ /* @__PURE__ */h("button",{onClick:Z,className:Y,children:"="})]})]}),
29
+ /* @__PURE__ */h(t,{open:W,onClose:()=>E(!1),title:"Calculator Settings",appearance:O,onAppearanceChange:A,onSave:()=>{F(O),localStorage.setItem(m,JSON.stringify(O)),E(!1)}})]})}export{b as default};
@@ -0,0 +1 @@
1
+ export{ConfirmProvider,confirm,confirmDestructive,prompt,useConfirm}from"./chunk-VOY6TCS2.js";import"./chunk-AFYQTVZO.js";import"./chunk-X4FJ54PP.js";import"./chunk-XUF7VW4U.js";import"./chunk-3JK2LSFU.js";import"./chunk-C3QOHMR6.js";import"./chunk-YKZHOXI4.js";
@@ -1,6 +1,6 @@
1
- import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-MIAJQGWE.js";import{useWidgetSettings as r}from"./chunk-SBDIHKLU.js";import"./chunk-DSSLOQDW.js";import"./chunk-GMMTTWHB.js";import"./chunk-PQLVWCUI.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import"./chunk-ZZKIKW62.js";import"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import"./chunk-XUF7VW4U.js";import"./chunk-J3LIIIHJ.js";import"./chunk-C3QOHMR6.js";import"./chunk-YKZHOXI4.js";import{useState as n,useCallback as a,useEffect as s}from"react";import{jsxs as c,Fragment as o,jsx as i}from"react/jsx-runtime";var l=["USD","CNY","AUD","GBP","EUR","JPY","CAD","THB","NZD","SGD","HKD","CHF","KRW","INR","MXN","BRL"],m=[["USD","CNY"],["USD","AUD"],["GBP","USD"],["USD","JPY"]],u="currency_pairs",p="currency_appearance",d="currency_rates_cache";function h(e){try{const t=JSON.parse(localStorage.getItem(d)||"{}")[e];if(t&&Date.now()-t.timestamp<36e5)return t.rates}catch{}return null}function g(e,t){try{const r=JSON.parse(localStorage.getItem(d)||"{}");r[e]={rates:t,timestamp:Date.now()},localStorage.setItem(d,JSON.stringify(r))}catch{}}function x(){try{const e=JSON.parse(localStorage.getItem(u)||"");if(Array.isArray(e)&&e.length)return e.slice(0,4)}catch{}return m}var y={USD:"🇺🇸",CNY:"🇨🇳",AUD:"🇦🇺",GBP:"🇬🇧",JPY:"🇯🇵",CAD:"🇨🇦",EUR:"🇪🇺",THB:"🇹🇭",NZD:"🇳🇿",SGD:"🇸🇬",HKD:"🇭🇰",CHF:"🇨🇭",KRW:"🇰🇷",INR:"🇮🇳",MXN:"🇲🇽",BRL:"🇧🇷"};function f(){const[m,d]=n(x),[f,N]=n(()=>e(p)),[b,v]=n({}),[S,k]=n(!1),[D,j]=n(!1),[C,I]=n([]),[U,J]=n(f),[H,A]=n("USD"),[O,B]=n("CNY");r(a(()=>{I([...m]),J({...f}),j(!0)},[m,f])),s(()=>{const e=[...new Set(m.map(([e])=>e))];let t=!0;return async function(){k(!0);const r={};for(const t of e){const e=h(t);if(e)r[t]=e;else try{const e=await fetch(`https://open.er-api.com/v6/latest/${t}`),n=await e.json();n.rates&&(r[t]=n.rates,g(t,n.rates))}catch{}}t&&(v(r),k(!1))}(),()=>{t=!1}},[m]);/* @__PURE__ */
1
+ import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-LY2G6LHW.js";import{useWidgetSettings as r}from"./chunk-TMZVVRTA.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TCHOT4R.js";import"./chunk-2TP4GK53.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import"./chunk-VOY6TCS2.js";import"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import"./chunk-XUF7VW4U.js";import"./chunk-3JK2LSFU.js";import"./chunk-C3QOHMR6.js";import"./chunk-YKZHOXI4.js";import{useState as n,useCallback as a,useEffect as s}from"react";import{jsxs as c,Fragment as o,jsx as i}from"react/jsx-runtime";var l=["USD","CNY","AUD","GBP","EUR","JPY","CAD","THB","NZD","SGD","HKD","CHF","KRW","INR","MXN","BRL"],m=[["USD","CNY"],["USD","AUD"],["GBP","USD"],["USD","JPY"]],u="currency_pairs",p="currency_appearance",d="currency_rates_cache";function h(e){try{const t=JSON.parse(localStorage.getItem(d)||"{}")[e];if(t&&Date.now()-t.timestamp<36e5)return t.rates}catch{}return null}function g(e,t){try{const r=JSON.parse(localStorage.getItem(d)||"{}");r[e]={rates:t,timestamp:Date.now()},localStorage.setItem(d,JSON.stringify(r))}catch{}}function x(){try{const e=JSON.parse(localStorage.getItem(u)||"");if(Array.isArray(e)&&e.length)return e.slice(0,4)}catch{}return m}var y={USD:"🇺🇸",CNY:"🇨🇳",AUD:"🇦🇺",GBP:"🇬🇧",JPY:"🇯🇵",CAD:"🇨🇦",EUR:"🇪🇺",THB:"🇹🇭",NZD:"🇳🇿",SGD:"🇸🇬",HKD:"🇭🇰",CHF:"🇨🇭",KRW:"🇰🇷",INR:"🇮🇳",MXN:"🇲🇽",BRL:"🇧🇷"};function f(){const[m,d]=n(x),[f,N]=n(()=>e(p)),[b,S]=n({}),[v,k]=n(!1),[D,j]=n(!1),[C,U]=n([]),[O,H]=n(f),[J,A]=n("USD"),[R,F]=n("CNY");r(a(()=>{U([...m]),H({...f}),j(!0)},[m,f])),s(()=>{const e=[...new Set(m.map(([e])=>e))];let t=!0;return async function(){k(!0);const r={};for(const t of e){const e=h(t);if(e)r[t]=e;else try{const e=await fetch(`https://open.er-api.com/v6/latest/${t}`),n=await e.json();n.rates&&(r[t]=n.rates,g(t,n.rates))}catch{}}t&&(S(r),k(!1))}(),()=>{t=!1}},[m]);/* @__PURE__ */
2
2
  return c(o,{children:[
3
- /* @__PURE__ */i("div",{style:{backgroundColor:`rgb(var(--taskbar-bg-rgb, 243 244 246) / ${f.activeOpacity/100})`,backdropFilter:f.activeBlur>0?`blur(${f.activeBlur}px)`:void 0},children:/* @__PURE__ */c("div",{className:"px-4 py-3 space-y-1",children:[S&&/* @__PURE__ */i("div",{className:"text-xs text-gray-400 text-center py-4",children:"Loading rates..."}),m.map(([e,t],r)=>{const n=b[e]?.[t];/* @__PURE__ */
3
+ /* @__PURE__ */i("div",{style:{backgroundColor:`rgb(var(--taskbar-bg-rgb, 243 244 246) / ${f.activeOpacity/100})`,backdropFilter:f.activeBlur>0?`blur(${f.activeBlur}px)`:void 0},children:/* @__PURE__ */c("div",{className:"px-4 py-3 space-y-1",children:[v&&/* @__PURE__ */i("div",{className:"text-xs text-gray-400 text-center py-4",children:"Loading rates..."}),m.map(([e,t],r)=>{const n=b[e]?.[t];/* @__PURE__ */
4
4
  return c("button",{onClick:()=>{const e=m.map((e,t)=>t===r?[e[1],e[0]]:e);d(e),localStorage.setItem(u,JSON.stringify(e))},className:"flex items-center justify-between px-2 py-2 border-b border-gray-200 last:border-0 w-full hover:bg-gray-100 rounded transition-colors cursor-pointer",children:[
5
5
  /* @__PURE__ */c("div",{className:"flex items-center gap-1.5",children:[
6
6
  /* @__PURE__ */i("span",{className:"text-base",children:y[e]||""}),
@@ -9,13 +9,13 @@ return c("button",{onClick:()=>{const e=m.map((e,t)=>t===r?[e[1],e[0]]:e);d(e),l
9
9
  /* @__PURE__ */i("span",{className:"text-base",children:y[t]||""}),
10
10
  /* @__PURE__ */i("span",{className:"text-sm font-semibold text-gray-700",children:t})]}),
11
11
  /* @__PURE__ */i("div",{className:"text-base font-mono font-semibold text-gray-900 tabular-nums",children:null!=n?n.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:4}):"—"})]},`${e}-${t}-${r}`)})]})}),
12
- /* @__PURE__ */i(t,{open:D,onClose:()=>j(!1),title:"Currency Settings",appearance:U,onAppearanceChange:J,onSave:()=>{if(0===C.length)return;const e=C.slice(0,4);d(e),N(U),localStorage.setItem(u,JSON.stringify(e)),localStorage.setItem(p,JSON.stringify(U)),j(!1)},children:/* @__PURE__ */c("div",{children:[
12
+ /* @__PURE__ */i(t,{open:D,onClose:()=>j(!1),title:"Currency Settings",appearance:O,onAppearanceChange:H,onSave:()=>{if(0===C.length)return;const e=C.slice(0,4);d(e),N(O),localStorage.setItem(u,JSON.stringify(e)),localStorage.setItem(p,JSON.stringify(O)),j(!1)},children:/* @__PURE__ */c("div",{children:[
13
13
  /* @__PURE__ */c("h3",{className:"text-sm font-semibold text-gray-700 mb-2",children:["Currency Pairs ",
14
14
  /* @__PURE__ */c("span",{className:"text-[11px] font-normal text-gray-400",children:["(",C.length,"/",4,")"]})]}),
15
15
  /* @__PURE__ */i("div",{className:"space-y-1 mb-2",children:C.map(([e,t],r)=>/* @__PURE__ */c("div",{className:"flex items-center justify-between py-1 px-2 bg-gray-50 rounded",children:[
16
16
  /* @__PURE__ */c("span",{className:"text-sm",children:[y[e]||""," ",e," → ",y[t]||""," ",t]}),
17
- /* @__PURE__ */i("button",{onClick:()=>I(e=>e.filter((e,t)=>t!==r)),className:"text-red-400 hover:text-red-600",children:"×"})]},r))}),C.length<4?/* @__PURE__ */c("div",{className:"flex items-center gap-2",children:[
18
- /* @__PURE__ */i("select",{value:H,onChange:e=>A(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
17
+ /* @__PURE__ */i("button",{onClick:()=>U(e=>e.filter((e,t)=>t!==r)),className:"text-red-400 hover:text-red-600",children:"×"})]},r))}),C.length<4?/* @__PURE__ */c("div",{className:"flex items-center gap-2",children:[
18
+ /* @__PURE__ */i("select",{value:J,onChange:e=>A(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
19
19
  /* @__PURE__ */i("span",{className:"text-gray-400",children:"→"}),
20
- /* @__PURE__ */i("select",{value:O,onChange:e=>B(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
21
- /* @__PURE__ */i("button",{onClick:()=>{H!==O&&C.length<4&&I(e=>[...e,[H,O]])},className:"text-sm font-medium text-blue-600 hover:text-blue-800 px-2",children:"+ Add"})]}):/* @__PURE__ */c("p",{className:"text-[11px] text-gray-400 italic",children:["Max ",4," pairs — remove one to add another."]})]})})]})}export{f as default};
20
+ /* @__PURE__ */i("select",{value:R,onChange:e=>F(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
21
+ /* @__PURE__ */i("button",{onClick:()=>{J!==R&&C.length<4&&U(e=>[...e,[J,R]])},className:"text-sm font-medium text-blue-600 hover:text-blue-800 px-2",children:"+ Add"})]}):/* @__PURE__ */c("p",{className:"text-[11px] text-gray-400 italic",children:["Max ",4," pairs — remove one to add another."]})]})})]})}export{f as default};
@@ -1,25 +1,25 @@
1
- import{escapeHtml as e}from"./chunk-BR22X7E5.js";import{AboutApp as t}from"./chunk-MSIN5HKX.js";import{WindowTitle as n}from"./chunk-SBDIHKLU.js";import"./chunk-DSSLOQDW.js";import"./chunk-GMMTTWHB.js";import"./chunk-OD5KLVU5.js";import{toast_default as r}from"./chunk-3DGTIWZP.js";import"./chunk-PQLVWCUI.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import"./chunk-ZZKIKW62.js";import{PopupMenu as o,PopupMenuLabel as l,PopupMenuItem as a,PopupMenuDivider as i}from"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import{FolderOpenFallback as s,ImageFallback as c}from"./chunk-XUF7VW4U.js";import"./chunk-J3LIIIHJ.js";import"./chunk-C3QOHMR6.js";import{Icon as d}from"./chunk-YKZHOXI4.js";import{useState as m,useRef as h}from"react";import{jsxs as u,jsx as p}from"react/jsx-runtime";var g=/* @__PURE__ */new Set(["txt","md","markdown","csv","tsv","log","json","xml","html","htm","css","js","jsx","ts","tsx","py","rb","go","rs","java","c","h","cpp","hpp","sh","bash","yml","yaml","toml","ini","conf","env","sql"]),x=/* @__PURE__ */new Set(["docx"]),f={filename:"Untitled",kind:"docx",content:"",ext:"docx"};function y(){const[y,v]=m(f),[b,k]=m(!1),[w,N]=m(!1),[j,D]=m(!1),[C,L]=m(null),M=h(null),U=h(null),W=h(null),R=(e,t)=>{W.current?.focus(),document.execCommand(e,!1,t),D(!0)},I=e=>{for(const t of e){if(!t.type.startsWith("image/"))continue;const e=new FileReader;e.onload=()=>{W.current?.focus(),document.execCommand("insertImage",!1,String(e.result)),D(!0)},e.readAsDataURL(t)}},O=e=>{C&&(C.el.removeAttribute("width"),C.el.removeAttribute("height"),C.el.style.width=e??"",C.el.style.height=e?"auto":"",D(!0),L(null))},T=async t=>{const n=(t.name.split(".").pop()||"").toLowerCase();k(!0);try{if(g.has(n)||!x.has(n)&&t.type.startsWith("text/")){const r=await t.text(),o=e(r).replace(/\n/g,"<br>");v({filename:t.name,kind:"text",content:o,ext:n}),D(!1)}else if(x.has(n))try{const e=await(import(
1
+ import{escapeHtml as e}from"./chunk-BR22X7E5.js";import{AboutApp as t}from"./chunk-GPNNQIRY.js";import{WindowTitle as n}from"./chunk-TMZVVRTA.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TCHOT4R.js";import"./chunk-OBMR6BEP.js";import{toast_default as r}from"./chunk-DBB4DB7J.js";import"./chunk-2TP4GK53.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import"./chunk-VOY6TCS2.js";import{PopupMenu as o,PopupMenuLabel as l,PopupMenuItem as a,PopupMenuDivider as i}from"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import{FolderOpenFallback as s,ImageFallback as c}from"./chunk-XUF7VW4U.js";import"./chunk-3JK2LSFU.js";import"./chunk-C3QOHMR6.js";import{Icon as d}from"./chunk-YKZHOXI4.js";import{useState as m,useRef as h}from"react";import{jsxs as u,jsx as p}from"react/jsx-runtime";var g=/* @__PURE__ */new Set(["txt","md","markdown","csv","tsv","log","json","xml","html","htm","css","js","jsx","ts","tsx","py","rb","go","rs","java","c","h","cpp","hpp","sh","bash","yml","yaml","toml","ini","conf","env","sql"]),x=/* @__PURE__ */new Set(["docx"]),f={filename:"Untitled",kind:"docx",content:"",ext:"docx"};function y(){const[y,v]=m(f),[b,k]=m(!1),[w,N]=m(!1),[j,C]=m(!1),[D,L]=m(null),M=h(null),R=h(null),U=h(null),T=(e,t)=>{U.current?.focus(),document.execCommand(e,!1,t),C(!0)},O=e=>{for(const t of e){if(!t.type.startsWith("image/"))continue;const e=new FileReader;e.onload=()=>{U.current?.focus(),document.execCommand("insertImage",!1,String(e.result)),C(!0)},e.readAsDataURL(t)}},B=e=>{D&&(D.el.removeAttribute("width"),D.el.removeAttribute("height"),D.el.style.width=e??"",D.el.style.height=e?"auto":"",C(!0),L(null))},W=async t=>{const n=(t.name.split(".").pop()||"").toLowerCase();k(!0);try{if(g.has(n)||!x.has(n)&&t.type.startsWith("text/")){const r=await t.text(),o=e(r).replace(/\n/g,"<br>");v({filename:t.name,kind:"text",content:o,ext:n}),C(!1)}else if(x.has(n))try{const e=await(import(
2
2
  /* @vite-ignore */
3
- "mammoth")),r=await t.arrayBuffer(),o=await e.convertToHtml({arrayBuffer:r});v({filename:t.name,kind:"docx",content:o.value||"<p><em>(empty document)</em></p>",ext:n}),D(!1)}catch(e){r.error('Install the optional "mammoth" peer dep to read .docx files.')}else"doc"===n?r.error(".doc is the legacy Word binary format — convert to .docx first."):r.error(`Unsupported file type: .${n||"unknown"}`)}finally{k(!1)}},B=y?.filename?(S=y.filename).length>24?`${S.slice(0,23)}…`:S:"Untitled";var S;/* @__PURE__ */
4
- return u("div",{className:"relative flex flex-col h-full",onDragOver:e=>{e.preventDefault(),w||N(!0)},onDragLeave:e=>{e.currentTarget===e.target&&N(!1)},onDrop:e=>{e.preventDefault(),N(!1);const t=Array.from(e.dataTransfer.files??[]);0!==t.length&&(t.every(e=>e.type.startsWith("image/"))?I(t):T(t[0]))},children:[
5
- /* @__PURE__ */p(n,{title:`${B}${j?" •":""} - Documents`}),
3
+ "mammoth")),r=await t.arrayBuffer(),o=await e.convertToHtml({arrayBuffer:r});v({filename:t.name,kind:"docx",content:o.value||"<p><em>(empty document)</em></p>",ext:n}),C(!1)}catch(e){r.error('Install the optional "mammoth" peer dep to read .docx files.')}else"doc"===n?r.error(".doc is the legacy Word binary format — convert to .docx first."):r.error(`Unsupported file type: .${n||"unknown"}`)}finally{k(!1)}},S=y?.filename?(A=y.filename).length>24?`${A.slice(0,23)}…`:A:"Untitled";var A;/* @__PURE__ */
4
+ return u("div",{className:"relative flex flex-col h-full",onDragOver:e=>{e.preventDefault(),w||N(!0)},onDragLeave:e=>{e.currentTarget===e.target&&N(!1)},onDrop:e=>{e.preventDefault(),N(!1);const t=Array.from(e.dataTransfer.files??[]);0!==t.length&&(t.every(e=>e.type.startsWith("image/"))?O(t):W(t[0]))},children:[
5
+ /* @__PURE__ */p(n,{title:`${S}${j?" •":""} - Documents`}),
6
6
  /* @__PURE__ */p(t,{app:"documents"}),
7
7
  /* @__PURE__ */u("div",{className:"flex items-center flex-wrap gap-x-2 gap-y-1 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0",children:[
8
- /* @__PURE__ */p("input",{ref:M,type:"file",accept:".txt,.md,.csv,.tsv,.log,.json,.xml,.html,.htm,.css,.js,.jsx,.ts,.tsx,.py,.rb,.go,.rs,.java,.c,.h,.cpp,.hpp,.sh,.bash,.yml,.yaml,.toml,.ini,.conf,.env,.sql,.docx,text/*",onChange:e=>{const t=e.target.files?.[0];t&&T(t),M.current&&(M.current.value="")},className:"hidden"}),
8
+ /* @__PURE__ */p("input",{ref:M,type:"file",accept:".txt,.md,.csv,.tsv,.log,.json,.xml,.html,.htm,.css,.js,.jsx,.ts,.tsx,.py,.rb,.go,.rs,.java,.c,.h,.cpp,.hpp,.sh,.bash,.yml,.yaml,.toml,.ini,.conf,.env,.sql,.docx,text/*",onChange:e=>{const t=e.target.files?.[0];t&&W(t),M.current&&(M.current.value="")},className:"hidden"}),
9
9
  /* @__PURE__ */u("button",{onClick:()=>M.current?.click(),className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors flex items-center gap-1",children:[
10
10
  /* @__PURE__ */p(d,{name:"folder-open",fallback:s,className:"h-3.5 w-3.5",strokeWidth:1.5}),"Open"]}),
11
- /* @__PURE__ */p("button",{onClick:()=>{if(!y)return;const t=W.current?.innerHTML??"";if("text"===y.kind){if(/<(b|strong|i|em|u|font|span style|ul|ol|img)|style="[^"]*text-align/i.test(t)){const n=new Blob([`<!doctype html><meta charset="utf-8"><title>${e(y.filename)}</title><body style="font-family:ui-monospace,monospace;white-space:pre-wrap">${t}`],{type:"text/html"}),r=URL.createObjectURL(n),o=document.createElement("a");o.href=r,o.download=`${y.filename.replace(/\.[^.]+$/,"")}.html`,o.click(),URL.revokeObjectURL(r)}else{const e=W.current?.innerText??"",t=new Blob([e],{type:"text/plain"}),n=URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.download=y.filename,r.click(),URL.revokeObjectURL(n)}}else{const n=new Blob([`<!doctype html><meta charset="utf-8"><title>${e(y.filename)}</title>${t}`],{type:"text/html"}),r=URL.createObjectURL(n),o=document.createElement("a");o.href=r,o.download=`${y.filename.replace(/\.docx$/i,"")}.html`,o.click(),URL.revokeObjectURL(r)}},className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",children:"Save"}),
11
+ /* @__PURE__ */p("button",{onClick:()=>{if(!y)return;const t=U.current?.innerHTML??"";if("text"===y.kind){if(/<(b|strong|i|em|u|font|span style|ul|ol|img)|style="[^"]*text-align/i.test(t)){const n=new Blob([`<!doctype html><meta charset="utf-8"><title>${e(y.filename)}</title><body style="font-family:ui-monospace,monospace;white-space:pre-wrap">${t}`],{type:"text/html"}),r=URL.createObjectURL(n),o=document.createElement("a");o.href=r,o.download=`${y.filename.replace(/\.[^.]+$/,"")}.html`,o.click(),URL.revokeObjectURL(r)}else{const e=U.current?.innerText??"",t=new Blob([e],{type:"text/plain"}),n=URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.download=y.filename,r.click(),URL.revokeObjectURL(n)}}else{const n=new Blob([`<!doctype html><meta charset="utf-8"><title>${e(y.filename)}</title>${t}`],{type:"text/html"}),r=URL.createObjectURL(n),o=document.createElement("a");o.href=r,o.download=`${y.filename.replace(/\.docx$/i,"")}.html`,o.click(),URL.revokeObjectURL(r)}},className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",children:"Save"}),
12
12
  /* @__PURE__ */p("span",{className:"text-[10px] text-gray-400 ml-1",children:"TXT · DOCX · Code"}),
13
13
  /* @__PURE__ */p("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
14
14
  /* @__PURE__ */u("span",{className:"text-xs font-medium text-gray-700 truncate max-w-[200px]",title:y.filename,children:[y.filename,j?" •":""]}),
15
15
  /* @__PURE__ */p("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
16
16
  /* @__PURE__ */u("div",{className:"flex items-center gap-0.5",children:[
17
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("bold"),className:"px-2 py-1 text-xs rounded font-bold text-gray-700 hover:bg-gray-200 transition-colors",title:"Bold",children:"B"}),
18
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("italic"),className:"px-2 py-1 text-xs rounded italic text-gray-700 hover:bg-gray-200 transition-colors",title:"Italic",children:"I"}),
19
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("underline"),className:"px-2 py-1 text-xs rounded underline text-gray-700 hover:bg-gray-200 transition-colors",title:"Underline",children:"U"}),
20
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("strikeThrough"),className:"px-2 py-1 text-xs rounded line-through text-gray-700 hover:bg-gray-200 transition-colors",title:"Strikethrough",children:"S"})]}),
17
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("bold"),className:"px-2 py-1 text-xs rounded font-bold text-gray-700 hover:bg-gray-200 transition-colors",title:"Bold",children:"B"}),
18
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("italic"),className:"px-2 py-1 text-xs rounded italic text-gray-700 hover:bg-gray-200 transition-colors",title:"Italic",children:"I"}),
19
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("underline"),className:"px-2 py-1 text-xs rounded underline text-gray-700 hover:bg-gray-200 transition-colors",title:"Underline",children:"U"}),
20
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("strikeThrough"),className:"px-2 py-1 text-xs rounded line-through text-gray-700 hover:bg-gray-200 transition-colors",title:"Strikethrough",children:"S"})]}),
21
21
  /* @__PURE__ */p("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
22
- /* @__PURE__ */u("select",{onMouseDown:e=>e.preventDefault(),onChange:e=>{R("fontSize",e.target.value),e.currentTarget.value=""},defaultValue:"",className:"text-xs border border-gray-300 rounded px-1 py-0.5 bg-white",children:[
22
+ /* @__PURE__ */u("select",{onMouseDown:e=>e.preventDefault(),onChange:e=>{T("fontSize",e.target.value),e.currentTarget.value=""},defaultValue:"",className:"text-xs border border-gray-300 rounded px-1 py-0.5 bg-white",children:[
23
23
  /* @__PURE__ */p("option",{value:"",disabled:!0,children:"Size"}),
24
24
  /* @__PURE__ */p("option",{value:"1",children:"XS"}),
25
25
  /* @__PURE__ */p("option",{value:"2",children:"S"}),
@@ -27,26 +27,26 @@ return u("div",{className:"relative flex flex-col h-full",onDragOver:e=>{e.preve
27
27
  /* @__PURE__ */p("option",{value:"4",children:"L"}),
28
28
  /* @__PURE__ */p("option",{value:"5",children:"XL"}),
29
29
  /* @__PURE__ */p("option",{value:"6",children:"2XL"})]}),
30
- /* @__PURE__ */p("input",{type:"color",defaultValue:"#000000",onChange:e=>R("foreColor",e.target.value),className:"w-7 h-6 border border-gray-300 rounded cursor-pointer",title:"Text color"}),
30
+ /* @__PURE__ */p("input",{type:"color",defaultValue:"#000000",onChange:e=>T("foreColor",e.target.value),className:"w-7 h-6 border border-gray-300 rounded cursor-pointer",title:"Text color"}),
31
31
  /* @__PURE__ */p("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
32
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("insertUnorderedList"),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Bulleted list",children:"• List"}),
33
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("insertOrderedList"),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Numbered list",children:"1."}),
32
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("insertUnorderedList"),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Bulleted list",children:"• List"}),
33
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("insertOrderedList"),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Numbered list",children:"1."}),
34
34
  /* @__PURE__ */p("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
35
35
  /* @__PURE__ */u("div",{className:"flex items-center gap-0.5",children:[
36
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("justifyLeft"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align left",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M3.75 12h9.75M3.75 18h14.25"})})}),
37
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("justifyCenter"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align center",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M7.125 12h9.75M4.875 18h14.25"})})}),
38
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("justifyRight"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align right",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M10.5 12h9.75M6 18h14.25"})})}),
39
- /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>R("justifyFull"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Justify",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M3.75 12h16.5M3.75 18h16.5"})})})]}),
36
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("justifyLeft"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align left",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M3.75 12h9.75M3.75 18h14.25"})})}),
37
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("justifyCenter"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align center",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M7.125 12h9.75M4.875 18h14.25"})})}),
38
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("justifyRight"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align right",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M10.5 12h9.75M6 18h14.25"})})}),
39
+ /* @__PURE__ */p("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>T("justifyFull"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Justify",children:/* @__PURE__ */p("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */p("path",{strokeLinecap:"round",d:"M3.75 6h16.5M3.75 12h16.5M3.75 18h16.5"})})})]}),
40
40
  /* @__PURE__ */p("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
41
- /* @__PURE__ */p("input",{ref:U,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:e=>{e.target.files?.length&&I(Array.from(e.target.files)),U.current&&(U.current.value="")}}),
42
- /* @__PURE__ */u("button",{onClick:()=>U.current?.click(),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors flex items-center gap-1",title:"Insert image (or paste / drop one)",children:[
41
+ /* @__PURE__ */p("input",{ref:R,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:e=>{e.target.files?.length&&O(Array.from(e.target.files)),R.current&&(R.current.value="")}}),
42
+ /* @__PURE__ */u("button",{onClick:()=>R.current?.click(),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors flex items-center gap-1",title:"Insert image (or paste / drop one)",children:[
43
43
  /* @__PURE__ */p(d,{name:"image",fallback:c,className:"h-3.5 w-3.5",strokeWidth:1.5}),"Image"]})]}),
44
- /* @__PURE__ */p("div",{className:"flex-1 min-h-0 overflow-hidden",children:/* @__PURE__ */p("div",b?{className:"flex items-center justify-center h-full text-sm text-gray-400",children:"Loading…"}:{className:"h-full overflow-auto "+("docx"===y.kind?"bg-gray-100 px-6 py-8":"bg-white"),children:/* @__PURE__ */p("div",{ref:W,contentEditable:!0,suppressContentEditableWarning:!0,spellCheck:!1,onInput:()=>D(!0),onPaste:e=>{const t=Array.from(e.clipboardData?.items??[]).filter(e=>"file"===e.kind&&e.type.startsWith("image/")).map(e=>e.getAsFile()).filter(e=>!!e);t.length&&(e.preventDefault(),I(t))},onClick:e=>{const t=e.target;"IMG"===t.tagName&&(e.preventDefault(),L({x:e.clientX,y:e.clientY,el:t}))},dangerouslySetInnerHTML:{__html:y.content},className:"docx"===y.kind?"docs-editor mx-auto w-[8.5in] min-h-[11in] bg-white p-[1in] shadow text-[14px] leading-relaxed text-gray-800 outline-none focus:ring-2 focus:ring-blue-400/40":"docs-editor block w-full min-h-full p-4 font-mono text-[13px] leading-relaxed text-gray-800 bg-white outline-none whitespace-pre-wrap"})})}),w&&/* @__PURE__ */p("div",{className:"absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20",children:/* @__PURE__ */p("div",{className:"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg",children:"Drop a document to open · images insert into the page"})}),C&&/* @__PURE__ */u(o,{minWidth:150,style:{left:C.x,top:C.y},onClose:()=>L(null),children:[
44
+ /* @__PURE__ */p("div",{className:"flex-1 min-h-0 overflow-hidden",children:/* @__PURE__ */p("div",b?{className:"flex items-center justify-center h-full text-sm text-gray-400",children:"Loading…"}:{className:"h-full overflow-auto "+("docx"===y.kind?"bg-gray-100 px-6 py-8":"bg-white"),children:/* @__PURE__ */p("div",{ref:U,contentEditable:!0,suppressContentEditableWarning:!0,spellCheck:!1,onInput:()=>C(!0),onPaste:e=>{const t=Array.from(e.clipboardData?.items??[]).filter(e=>"file"===e.kind&&e.type.startsWith("image/")).map(e=>e.getAsFile()).filter(e=>!!e);t.length&&(e.preventDefault(),O(t))},onClick:e=>{const t=e.target;"IMG"===t.tagName&&(e.preventDefault(),L({x:e.clientX,y:e.clientY,el:t}))},dangerouslySetInnerHTML:{__html:y.content},className:"docx"===y.kind?"docs-editor mx-auto w-[8.5in] min-h-[11in] bg-white p-[1in] shadow text-[14px] leading-relaxed text-gray-800 outline-none focus:ring-2 focus:ring-blue-400/40":"docs-editor block w-full min-h-full p-4 font-mono text-[13px] leading-relaxed text-gray-800 bg-white outline-none whitespace-pre-wrap"})})}),w&&/* @__PURE__ */p("div",{className:"absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20",children:/* @__PURE__ */p("div",{className:"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg",children:"Drop a document to open · images insert into the page"})}),D&&/* @__PURE__ */u(o,{minWidth:150,style:{left:D.x,top:D.y},onClose:()=>L(null),children:[
45
45
  /* @__PURE__ */p(l,{children:"Image width"}),
46
- /* @__PURE__ */p(a,{onClick:()=>O("25%"),children:"25%"}),
47
- /* @__PURE__ */p(a,{onClick:()=>O("50%"),children:"50%"}),
48
- /* @__PURE__ */p(a,{onClick:()=>O("75%"),children:"75%"}),
49
- /* @__PURE__ */p(a,{onClick:()=>O("100%"),children:"100%"}),
50
- /* @__PURE__ */p(a,{onClick:()=>O(null),children:"Original size"}),
46
+ /* @__PURE__ */p(a,{onClick:()=>B("25%"),children:"25%"}),
47
+ /* @__PURE__ */p(a,{onClick:()=>B("50%"),children:"50%"}),
48
+ /* @__PURE__ */p(a,{onClick:()=>B("75%"),children:"75%"}),
49
+ /* @__PURE__ */p(a,{onClick:()=>B("100%"),children:"100%"}),
50
+ /* @__PURE__ */p(a,{onClick:()=>B(null),children:"Original size"}),
51
51
  /* @__PURE__ */p(i,{}),
52
- /* @__PURE__ */p(a,{danger:!0,onClick:()=>{C?.el.remove(),D(!0),L(null)},children:"Remove image"})]})]})}export{y as default};
52
+ /* @__PURE__ */p(a,{danger:!0,onClick:()=>{D?.el.remove(),C(!0),L(null)},children:"Remove image"})]})]})}export{y as default};
@@ -0,0 +1,87 @@
1
+ import{openPreviewFile as e}from"./chunk-JK2KQ4PF.js";import{Breadcrumbs as t}from"./chunk-XNHYCOPO.js";import{SidebarLayout as a}from"./chunk-MDI265RK.js";import{getFilesDemoTree as r}from"./chunk-P4SGJTK3.js";import{AboutApp as n}from"./chunk-GPNNQIRY.js";import{peekFilesViewRequest as i,consumeFilesViewRequest as s,FILES_SHOW_TRASH_EVENT as l,FILES_OPEN_DESKTOP_FOLDER_EVENT as o,subscribeDesktopFolders as c,getDesktopFoldersSnapshot as d,FolderGlyph as m,DesktopItemMiniIcon as h,desktopItemTypeLabel as p}from"./chunk-LMIQV3NB.js";import"./chunk-ESL5TDNA.js";import"./chunk-VTVSMRFX.js";import{useWindowManager as f,WindowTitle as u}from"./chunk-TMZVVRTA.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TCHOT4R.js";import"./chunk-OBMR6BEP.js";import{toast_default as x}from"./chunk-DBB4DB7J.js";import"./chunk-2TP4GK53.js";import"./chunk-5A7MWTXX.js";import"./chunk-OGXU7QFW.js";import{confirm as g,prompt as y}from"./chunk-VOY6TCS2.js";import"./chunk-ZDF5AR5W.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-D65T4H6J.js";import{CloseCircleFallback as b,ArrowLeftFallback as k,RefreshFallback as N,TrashFallback as w,FolderPlusFallback as v,UploadFallbackB as j,FolderFallback as $,PageFallback as C}from"./chunk-XUF7VW4U.js";import"./chunk-3JK2LSFU.js";import"./chunk-C3QOHMR6.js";import{formatBytes as D}from"./chunk-W32SKK3D.js";import{Icon as T}from"./chunk-YKZHOXI4.js";import{useState as L,useEffect as E,useSyncExternalStore as R,useRef as P,useCallback as S}from"react";import{jsxs as F,jsx as O,Fragment as W}from"react/jsx-runtime";var I="undefined"!=typeof window&&window.__REACT_OS_SHELL_FILE_SERVER__||"http://localhost:4000",M={pdf:"pdf",dxf:"dxf",csv:"csv",jpg:"image",jpeg:"image",png:"image",gif:"image",webp:"image",svg:"image",avif:"image",bmp:"image",stp:"3d",step:"3d",stl:"3d",obj:"3d",gltf:"3d",glb:"3d","3mf":"3d",iges:"3d",igs:"3d",ply:"3d",fbx:"3d"};function A(e,t){return"/"===e||""===e?"/"+t:e.replace(/\/$/,"")+"/"+t}function U(e){if(!e)return"—";try{const t=new Date(e);return isNaN(t.getTime())?"—":t.toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"})}catch{return e}}function V(){const V=I.replace(/\/$/,""),B=null!==r(),[H,K]=L("/"),[J,z]=L([]),[Q,X]=L([]),[_,Z]=L(null),[Y,G]=L(!0),[q,ee]=L(!1),[te,ae]=L(null),[re,ne]=L(!1),[ie,se]=L(()=>{const e=i();return"trash"===e?"trash":e?{desktopFolderId:e.folderId}:"files"});E(()=>{s()},[]);const[le,oe]=L([]);E(()=>{const e=()=>{s(),se("trash")},t=e=>{s();const t=e.detail?.folderId;t&&se({desktopFolderId:t})};return window.addEventListener(l,e),window.addEventListener(o,t),()=>{window.removeEventListener(l,e),window.removeEventListener(o,t)}},[]);const ce=R(c,d,d),de="object"==typeof ie?ie.desktopFolderId:null,me=de?ce?.folders.find(e=>e.id===de)??null:null,he=de&&ce?.itemsByFolder[de]||[];E(()=>{de&&ce&&!ce.folders.some(e=>e.id===de)&&se("files")},[de,ce]);const pe=P(0),fe=P(null),{openPage:ue,openEntity:xe}=f(),ge=t=>{"preview-file"===t.entityType&&t.filePath&&t.fileKind?e({filePath:t.filePath,filename:t.label,kind:t.fileKind,onStaged:e=>ue(e)}):"page"===t.entityType?ue(t.entityId):xe(t.entityType,t.entityId,null,t.label)},ye=S((e,t={})=>fetch(e,{...t,credentials:"include"}),[]),be=S(async()=>{try{const e=await ye(`${V}/api/quota`);if(e.ok){const t=await e.json();ae({used:t.used,limit:t.limit})}}catch{}},[ye,V]),ke=S(async e=>{if(Z(null),B){const t=function(e){const t=r();if(!t)return[];let a=t;for(const t of e.split("/").filter(Boolean)){const e=a.find(e=>"folder"===e.kind&&e.name===t);if(!e||!e.children)return[];a=e.children}return a.map(e=>({name:e.name,kind:e.kind,size:e.size??0,modifiedAt:e.modifiedAt??""}))}(e);return z(t),K(e),ee(!1),G(!1),void("/"===e&&X(t.filter(e=>"folder"===e.kind).map(e=>e.name)))}G(!0);try{const t=await ye(`${V}/api/files?path=${encodeURIComponent(e)}`);if(!t.ok){const e=await t.json().catch(()=>({}));return void x.error(e.error||`Failed to list (${t.status})`)}const a=await t.json(),r=a.entries||[];z(r),K(a.path||e),ee(!1),"/"===(a.path||e)&&X(r.filter(e=>"folder"===e.kind).map(e=>e.name))}catch(e){ee(!0)}finally{G(!1)}be()},[ye,V,be,B]),Ne=S(async()=>{if(B)return oe([]),void G(!1);G(!0),Z(null);try{const e=await ye(`${V}/api/trash`);if(!e.ok){const t=await e.json().catch(()=>({}));return void x.error(t.error||`Failed to list trash (${e.status})`)}const t=await e.json();oe(t.entries||[]),ee(!1)}catch{ee(!0)}finally{G(!1)}be()},[ye,V,be,B]);E(()=>{"files"===ie?ke(H):"trash"===ie&&Ne()},[H,ie]);const we=async e=>{const t=A(H,e.name);try{const a=await ye(`${V}/api/file?path=${encodeURIComponent(t)}`);if(!a.ok)return void x.error(`Download failed (${a.status})`);const r=await a.blob(),n=URL.createObjectURL(r),i=document.createElement("a");i.href=n,i.download=e.name,i.click(),setTimeout(()=>URL.revokeObjectURL(n),1e3)}catch(e){x.error(e?.message||"Download failed")}},ve=async e=>{const t=Array.from(e);for(const e of t){const t=new FormData;t.append("file",e);try{const a=await ye(`${V}/api/upload?path=${encodeURIComponent(H)}`,{method:"POST",body:t});if(!a.ok){const t=await a.json().catch(()=>({}));if(413===a.status){const a=Math.max(0,(t.limit||0)-(t.used||0));x.error(`Quota exceeded — ${D(a)} free, ${e.name} is ${D(t.attempted||e.size)}`)}else x.error(`Upload ${e.name}: ${t.error||a.status}`)}}catch(t){x.error(`Upload ${e.name}: ${t?.message||"failed"}`)}}ke(H)},je=()=>{pe.current=0,ne(!1)};if(E(()=>{const e=()=>je();return window.addEventListener("dragend",e),window.addEventListener("drop",e),()=>{window.removeEventListener("dragend",e),window.removeEventListener("drop",e)}},[]),q)/* @__PURE__ */
2
+ return F("div",{className:"flex flex-col h-full bg-white",children:[
3
+ /* @__PURE__ */O(u,{title:"Files - offline"}),
4
+ /* @__PURE__ */O("div",{className:"flex-1 flex items-center justify-center p-8 text-center",children:/* @__PURE__ */F("div",{className:"max-w-md",children:[
5
+ /* @__PURE__ */O(T,{name:"close-circle",fallback:b,className:"h-12 w-12 mx-auto text-gray-300 mb-3",strokeWidth:1.4}),
6
+ /* @__PURE__ */O("h3",{className:"text-base font-semibold text-gray-800 mb-1",children:"Can't reach the file server"}),
7
+ /* @__PURE__ */F("p",{className:"text-sm text-gray-500 mb-3",children:["No response from ",
8
+ /* @__PURE__ */O("span",{className:"font-mono",children:V}),". Make sure the server is running — see ",
9
+ /* @__PURE__ */O("span",{className:"font-mono",children:"examples/file-server/README.md"}),"."]}),
10
+ /* @__PURE__ */O("button",{onClick:()=>ke(H),className:"px-3 py-1.5 text-sm bg-blue-500 text-white rounded hover:bg-blue-600",children:"Retry"})]})})]});const $e="/"===H?[]:H.split("/").filter(Boolean),Ce=te&&te.limit>0?Math.min(100,Math.round(te.used/te.limit*100)):0,De=Ce>90?"bg-red-500":Ce>75?"bg-amber-500":"bg-blue-500",Te=[{label:"My files",onClick:()=>K("/")},...$e.map((e,t)=>({label:e,onClick:()=>K("/"+$e.slice(0,t+1).join("/"))}))],Le=e=>"flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors "+(e?"bg-blue-50 font-medium text-blue-700":"text-gray-700 hover:bg-gray-100"),Ee=e=>/* @__PURE__ */O("svg",{className:"h-4 w-4 shrink-0 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.6,children:/* @__PURE__ */O("path",{strokeLinecap:"round",strokeLinejoin:"round",d:e})}),Re=/* @__PURE__ */F("nav",{className:"p-2",children:[
11
+ /* @__PURE__ */O("div",{className:"px-2 py-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-400",children:"Locations"}),
12
+ /* @__PURE__ */F("button",{onClick:()=>{se("files"),K("/")},className:Le("files"===ie&&"/"===H),children:[Ee("M2.25 12l8.954-8.955a1.5 1.5 0 012.122 0L21.75 12M4.5 9.75v9.75a.75.75 0 00.75.75H9V15a.75.75 0 01.75-.75h4.5A.75.75 0 0115 15v5.25h3.75a.75.75 0 00.75-.75V9.75"),"My files"]}),Q.map(e=>/* @__PURE__ */F("button",{onClick:()=>{se("files"),K("/"+e)},className:Le("files"===ie&&H==="/"+e),children:[Ee("M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z"),
13
+ /* @__PURE__ */O("span",{className:"truncate",children:e})]},e)),!B&&/* @__PURE__ */F("button",{onClick:()=>se("trash"),className:Le("trash"===ie),children:[Ee("M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79"),"Trash"]}),ce&&ce.folders.length>0&&/* @__PURE__ */F(W,{children:[
14
+ /* @__PURE__ */O("div",{className:"px-2 pt-3 pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-400",children:"Desktop"}),ce.folders.map(e=>/* @__PURE__ */F("button",{onClick:()=>se({desktopFolderId:e.id}),className:Le(de===e.id),children:[
15
+ /* @__PURE__ */O(m,{className:"h-4 w-4 shrink-0 text-amber-500"}),
16
+ /* @__PURE__ */O("span",{className:"truncate",children:e.name}),e.itemCount>0&&/* @__PURE__ */O("span",{className:"ml-auto text-[10px] text-gray-400 tabular-nums",children:e.itemCount})]},e.id))]})]});/* @__PURE__ */
17
+ return F("div",{className:"relative flex flex-col h-full bg-white",onDragEnter:e=>{B||"object"==typeof ie||e.dataTransfer?.types?.includes?.("Files")&&(e.preventDefault(),pe.current++,re||ne(!0))},onDragOver:e=>{e.dataTransfer?.types?.includes?.("Files")&&e.preventDefault()},onDragLeave:()=>{pe.current>0&&pe.current--,0===pe.current&&ne(!1)},onDrop:e=>{e.preventDefault(),je(),B||"object"==typeof ie||e.dataTransfer.files?.length&&ve(e.dataTransfer.files)},children:[
18
+ /* @__PURE__ */O(u,{title:me?`Files - ${me.name}`:"Files"+("/"===H?"":" - "+H)}),
19
+ /* @__PURE__ */O(n,{app:"files"}),
20
+ /* @__PURE__ */O("input",{ref:fe,type:"file",multiple:!0,className:"hidden",onChange:e=>{e.target.files?.length&&ve(e.target.files),fe.current&&(fe.current.value="")}}),
21
+ /* @__PURE__ */O("div",{className:"flex-1 min-h-0",children:/* @__PURE__ */O(a,{sidebar:Re,storageKey:"files.sidebarWidth",defaultWidth:200,minWidth:150,maxWidth:320,children:/* @__PURE__ */F("div",{className:"flex h-full min-w-0 flex-col",children:[
22
+ /* @__PURE__ */F("div",{className:"flex items-center gap-1 px-2 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0 text-xs",children:[/* @__PURE__ */F(W,"files"===ie?{children:[
23
+ /* @__PURE__ */O("button",{onClick:()=>K(function(e){if("/"===e||""===e)return"/";const t=e.replace(/\/$/,""),a=t.lastIndexOf("/");return a<=0?"/":t.slice(0,a)}(H)),disabled:"/"===H,className:"px-2 py-1 rounded hover:bg-gray-200 disabled:opacity-30 text-gray-600",title:"Parent folder",children:/* @__PURE__ */O(T,{name:"arrow-left",fallback:k,className:"h-3.5 w-3.5",strokeWidth:2})}),
24
+ /* @__PURE__ */O("div",{className:"flex-1 min-w-0",children:/* @__PURE__ */O(t,{items:Te,maxItems:5})}),
25
+ /* @__PURE__ */O("button",{onClick:()=>ke(H),className:"px-2 py-1 rounded hover:bg-gray-200 text-gray-600",title:"Refresh",children:/* @__PURE__ */O(T,{name:"refresh",fallback:N,className:"h-3.5 w-3.5",strokeWidth:1.8})}),!B&&/* @__PURE__ */F(W,{children:[
26
+ /* @__PURE__ */F("button",{onClick:()=>se("trash"),className:"px-2 py-1 rounded hover:bg-gray-200 text-gray-600 flex items-center gap-1",title:"Open trash",children:[
27
+ /* @__PURE__ */O(T,{name:"trash",fallback:w,className:"h-3.5 w-3.5",strokeWidth:1.5}),"Trash"]}),
28
+ /* @__PURE__ */F("button",{onClick:async()=>{const e=await y({title:"New folder",placeholder:"Folder name",confirmLabel:"Create"});if(!e)return;if(/[\\/]/.test(e))return void x.error("Folder names cannot contain slashes");const t=A(H,e),a=await ye(`${V}/api/folder`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!a.ok){const e=await a.json().catch(()=>({}));return void x.error(e.error||`Create folder failed (${a.status})`)}ke(H)},className:"px-2 py-1 rounded hover:bg-gray-200 text-gray-600 flex items-center gap-1",children:[
29
+ /* @__PURE__ */O(T,{name:"folder-plus",fallback:v,className:"h-3.5 w-3.5",strokeWidth:1.5}),"New Folder"]}),
30
+ /* @__PURE__ */F("button",{onClick:()=>fe.current?.click(),className:"px-2 py-1 rounded bg-blue-500 text-white hover:bg-blue-600 flex items-center gap-1",children:[
31
+ /* @__PURE__ */O(T,{name:"upload",fallback:j,className:"h-3.5 w-3.5",strokeWidth:1.8}),"Upload"]})]})]}:"trash"===ie?{children:[
32
+ /* @__PURE__ */O("button",{onClick:()=>se("files"),className:"px-2 py-1 rounded hover:bg-gray-200 text-gray-600",title:"Back to files",children:/* @__PURE__ */O(T,{name:"arrow-left",fallback:k,className:"h-3.5 w-3.5",strokeWidth:2})}),
33
+ /* @__PURE__ */O("span",{className:"flex-1 text-gray-700 font-medium px-2",children:"Trash"}),
34
+ /* @__PURE__ */O("button",{onClick:Ne,className:"px-2 py-1 rounded hover:bg-gray-200 text-gray-600",title:"Refresh",children:/* @__PURE__ */O(T,{name:"refresh",fallback:N,className:"h-3.5 w-3.5",strokeWidth:1.8})}),
35
+ /* @__PURE__ */O("button",{onClick:async()=>{if(0===le.length)return;if(!await g({title:"Empty trash",message:`${le.length} item${1===le.length?"":"s"} will be permanently removed. This can't be undone.`,confirmLabel:"Empty trash",variant:"danger"}))return;const e=await ye(`${V}/api/trash`,{method:"DELETE"});if(!e.ok){const t=await e.json().catch(()=>({}));return void x.error(t.error||`Empty trash failed (${e.status})`)}Ne()},disabled:0===le.length,className:"px-2 py-1 rounded text-red-600 hover:bg-red-50 disabled:opacity-30 flex items-center gap-1",children:"Empty trash"})]}:{children:[
36
+ /* @__PURE__ */O("button",{onClick:()=>se("files"),className:"px-2 py-1 rounded hover:bg-gray-200 text-gray-600",title:"Back to files",children:/* @__PURE__ */O(T,{name:"arrow-left",fallback:k,className:"h-3.5 w-3.5",strokeWidth:2})}),
37
+ /* @__PURE__ */O(m,{className:"h-4 w-4 text-amber-500 ml-1"}),
38
+ /* @__PURE__ */O("span",{className:"flex-1 text-gray-700 font-medium px-1 truncate",children:me?.name??"Desktop folder"}),
39
+ /* @__PURE__ */O("span",{className:"text-[10px] text-gray-400 px-2",children:"Desktop shortcuts"})]}),te&&/* @__PURE__ */F(W,{children:[
40
+ /* @__PURE__ */O("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
41
+ /* @__PURE__ */F("div",{className:"flex items-center gap-1.5",title:`${D(te.used)} of ${D(te.limit)} used`,children:[
42
+ /* @__PURE__ */O("div",{className:"w-20 h-1.5 rounded-full bg-gray-200 overflow-hidden",children:/* @__PURE__ */O("div",{className:`h-full ${De} transition-all`,style:{width:`${Ce}%`}})}),
43
+ /* @__PURE__ */F("span",{className:"text-[10px] text-gray-500 tabular-nums whitespace-nowrap",children:[D(te.used)," / ",D(te.limit)]})]})]})]}),
44
+ /* @__PURE__ */O("div",{className:"flex-1 overflow-auto",children:"object"==typeof ie?0===he.length?/* @__PURE__ */O("div",{className:"p-10 text-center text-sm text-gray-400",children:"This desktop folder is empty. Drag shortcuts onto it on the desktop."}):/* @__PURE__ */F("table",{className:"w-full text-sm",children:[
45
+ /* @__PURE__ */O("thead",{className:"bg-gray-50 text-[11px] uppercase tracking-wide text-gray-500",children:/* @__PURE__ */F("tr",{children:[
46
+ /* @__PURE__ */O("th",{className:"text-left font-medium px-3 py-1.5",children:"Name"}),
47
+ /* @__PURE__ */O("th",{className:"text-left font-medium px-3 py-1.5 w-28",children:"Type"}),
48
+ /* @__PURE__ */O("th",{className:"w-56"})]})}),
49
+ /* @__PURE__ */O("tbody",{children:he.map(e=>{const t=`${e.entityType}|${e.entityId}`;/* @__PURE__ */
50
+ return F("tr",{onClick:()=>Z(t),onDoubleClick:()=>ge(e),className:"cursor-default border-b border-gray-100 "+(_===t?"bg-blue-50":"hover:bg-gray-50"),children:[
51
+ /* @__PURE__ */F("td",{className:"px-3 py-1.5 flex items-center gap-2",children:[
52
+ /* @__PURE__ */O(h,{item:e}),
53
+ /* @__PURE__ */O("span",{className:"truncate",title:e.label,children:e.label})]}),
54
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-xs text-gray-500",children:p(e)}),
55
+ /* @__PURE__ */F("td",{className:"px-3 py-1.5 text-right whitespace-nowrap",children:[
56
+ /* @__PURE__ */O("button",{onClick:t=>{t.stopPropagation(),ge(e)},className:"px-1.5 py-0.5 rounded hover:bg-gray-200 text-blue-600 text-[11px]",children:"Open"}),
57
+ /* @__PURE__ */O("button",{onClick:t=>{t.stopPropagation(),ce?.moveToDesktop([e])},className:"px-1.5 py-0.5 rounded hover:bg-gray-200 text-gray-500 text-[11px] ml-1",children:"Move to desktop"}),
58
+ /* @__PURE__ */O("button",{onClick:t=>{t.stopPropagation(),ce?.removeShortcuts([e])},className:"px-1.5 py-0.5 rounded hover:bg-red-100 text-red-600 text-[11px] ml-1",children:"Remove"})]})]},t)})})]}):"trash"===ie?Y&&0===le.length?/* @__PURE__ */O("div",{className:"p-6 text-center text-sm text-gray-400",children:"Loading…"}):0===le.length?/* @__PURE__ */O("div",{className:"p-10 text-center text-sm text-gray-400",children:"Trash is empty."}):/* @__PURE__ */F("table",{className:"w-full text-sm",children:[
59
+ /* @__PURE__ */O("thead",{className:"bg-gray-50 text-[11px] uppercase tracking-wide text-gray-500",children:/* @__PURE__ */F("tr",{children:[
60
+ /* @__PURE__ */O("th",{className:"text-left font-medium px-3 py-1.5",children:"Name"}),
61
+ /* @__PURE__ */O("th",{className:"text-left font-medium px-3 py-1.5",children:"Original location"}),
62
+ /* @__PURE__ */O("th",{className:"text-right font-medium px-3 py-1.5 w-24",children:"Size"}),
63
+ /* @__PURE__ */O("th",{className:"text-right font-medium px-3 py-1.5 w-40",children:"Deleted"}),
64
+ /* @__PURE__ */O("th",{className:"w-44"})]})}),
65
+ /* @__PURE__ */O("tbody",{children:le.map(e=>/* @__PURE__ */F("tr",{className:"border-b border-gray-100 hover:bg-gray-50",children:[
66
+ /* @__PURE__ */F("td",{className:"px-3 py-1.5 flex items-center gap-2",children:["folder"===e.kind?/* @__PURE__ */O(T,{name:"folder",fallback:$,className:"h-4 w-4 text-amber-500 shrink-0"}):/* @__PURE__ */O(T,{name:"page",fallback:C,className:"h-4 w-4 text-gray-400 shrink-0",strokeWidth:1.5}),
67
+ /* @__PURE__ */O("span",{className:"truncate",title:e.name,children:e.name})]}),
68
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-gray-500 text-xs font-mono truncate",title:e.originalPath,children:e.originalPath||"—"}),
69
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-right tabular-nums text-gray-500",children:D(e.size)}),
70
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-right text-gray-500 tabular-nums",children:e.deletedAt?U(e.deletedAt):"—"}),
71
+ /* @__PURE__ */F("td",{className:"px-3 py-1.5 text-right whitespace-nowrap",children:[
72
+ /* @__PURE__ */O("button",{onClick:()=>(async e=>{const t=await ye(`${V}/api/trash/restore`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e.id})});if(!t.ok){const e=await t.json().catch(()=>({}));return void x.error(e.error||`Restore failed (${t.status})`)}x.success(`Restored "${e.name}"`),Ne()})(e),className:"px-1.5 py-0.5 rounded hover:bg-gray-200 text-blue-600 text-[11px]",children:"Restore"}),
73
+ /* @__PURE__ */O("button",{onClick:()=>(async e=>{if(!await g({title:"Permanently delete",message:`"${e.name}" will be permanently removed. This can't be undone.`,confirmLabel:"Delete forever",variant:"danger"}))return;const t=await ye(`${V}/api/trash/${encodeURIComponent(e.id)}`,{method:"DELETE"});if(!t.ok){const e=await t.json().catch(()=>({}));return void x.error(e.error||`Delete failed (${t.status})`)}Ne()})(e),className:"px-1.5 py-0.5 rounded hover:bg-red-100 text-red-600 text-[11px] ml-1",children:"Delete forever"})]})]},e.id))})]}):Y&&0===J.length?/* @__PURE__ */O("div",{className:"p-6 text-center text-sm text-gray-400",children:"Loading…"}):0===J.length?/* @__PURE__ */F("div",{className:"p-10 text-center text-sm text-gray-400",children:["Empty folder.",!B&&/* @__PURE__ */F(W,{children:[" Drop files here or click ",
74
+ /* @__PURE__ */O("span",{className:"font-medium",children:"Upload"}),"."]})]}):/* @__PURE__ */F("table",{className:"w-full text-sm",children:[
75
+ /* @__PURE__ */O("thead",{className:"bg-gray-50 text-[11px] uppercase tracking-wide text-gray-500",children:/* @__PURE__ */F("tr",{children:[
76
+ /* @__PURE__ */O("th",{className:"text-left font-medium px-3 py-1.5",children:"Name"}),
77
+ /* @__PURE__ */O("th",{className:"text-right font-medium px-3 py-1.5 w-24",children:"Size"}),
78
+ /* @__PURE__ */O("th",{className:"text-right font-medium px-3 py-1.5 w-40",children:"Modified"}),
79
+ /* @__PURE__ */O("th",{className:"w-32"})]})}),
80
+ /* @__PURE__ */O("tbody",{children:J.map(t=>/* @__PURE__ */F("tr",{onClick:()=>Z(t.name),onDoubleClick:()=>{"folder"===t.kind?K(A(H,t.name)):(async t=>{if(B)return;const a=A(H,t.name),r=(t.name.split(".").pop()||"").toLowerCase(),n=M[r];n?await e({filePath:a,filename:t.name,kind:n,onStaged:e=>ue(e)}):we(t)})(t)},className:"cursor-default border-b border-gray-100 "+(_===t.name?"bg-blue-50":"hover:bg-gray-50"),children:[
81
+ /* @__PURE__ */F("td",{className:"px-3 py-1.5 flex items-center gap-2",children:["folder"===t.kind?/* @__PURE__ */O(T,{name:"folder",fallback:$,className:"h-4 w-4 text-amber-500 shrink-0"}):/* @__PURE__ */O(T,{name:"page",fallback:C,className:"h-4 w-4 text-gray-400 shrink-0",strokeWidth:1.5}),
82
+ /* @__PURE__ */O("span",{className:"truncate",title:t.name,children:t.name})]}),
83
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-right tabular-nums text-gray-500",children:"folder"===t.kind?"—":D(t.size)}),
84
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-right text-gray-500 tabular-nums",children:U(t.modifiedAt)}),
85
+ /* @__PURE__ */O("td",{className:"px-3 py-1.5 text-right whitespace-nowrap",children:!B&&/* @__PURE__ */F(W,{children:["file"===t.kind&&/* @__PURE__ */O("button",{onClick:e=>{e.stopPropagation(),we(t)},className:"px-1.5 py-0.5 rounded hover:bg-gray-200 text-gray-500 text-[11px]",children:"Download"}),
86
+ /* @__PURE__ */O("button",{onClick:e=>{e.stopPropagation(),(async e=>{const t=await y({title:`Rename ${e.kind}`,defaultValue:e.name,confirmLabel:"Rename"});if(!t||t===e.name)return;if(/[\\/]/.test(t))return void x.error("Names cannot contain slashes");const a=A(H,e.name),r=A(H,t),n=await ye(`${V}/api/rename`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:a,to:r})});if(!n.ok){const e=await n.json().catch(()=>({}));return void x.error(e.error||`Rename failed (${n.status})`)}ke(H)})(t)},className:"px-1.5 py-0.5 rounded hover:bg-gray-200 text-gray-500 text-[11px] ml-1",children:"Rename"}),
87
+ /* @__PURE__ */O("button",{onClick:e=>{e.stopPropagation(),(async e=>{if(!await g({title:`Delete ${e.kind}`,message:`"${e.name}"${"folder"===e.kind?" and everything inside":""} will be permanently removed.`,confirmLabel:"Delete",variant:"danger"}))return;const t=A(H,e.name),a=await ye(`${V}/api/files?path=${encodeURIComponent(t)}`,{method:"DELETE"});if(!a.ok){const e=await a.json().catch(()=>({}));return void x.error(e.error||`Delete failed (${a.status})`)}ke(H)})(t)},className:"px-1.5 py-0.5 rounded hover:bg-red-100 text-red-600 text-[11px] ml-1",children:"Delete"})]})})]},t.name))})]})})]})})}),re&&/* @__PURE__ */O("div",{className:"absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20",children:/* @__PURE__ */O("div",{className:"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg",children:"Drop to upload"})})]})}export{V as default};