@hanzo/ui 8.27.8 → 8.27.10

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 (253) hide show
  1. package/README.md +2 -1
  2. package/dist/backends/gui/3d-card.cjs +100 -0
  3. package/dist/backends/gui/3d-card.d.ts +59 -0
  4. package/dist/backends/gui/3d-card.d.ts.map +1 -0
  5. package/dist/backends/gui/3d-card.js +92 -0
  6. package/dist/backends/gui/3d-card.js.map +1 -0
  7. package/dist/backends/gui/3d-marquee.cjs +225 -0
  8. package/dist/backends/gui/3d-marquee.d.ts +114 -0
  9. package/dist/backends/gui/3d-marquee.d.ts.map +1 -0
  10. package/dist/backends/gui/3d-marquee.js +190 -0
  11. package/dist/backends/gui/3d-marquee.js.map +1 -0
  12. package/dist/backends/gui/3d-pin.cjs +51 -0
  13. package/dist/backends/gui/3d-pin.d.ts +14 -0
  14. package/dist/backends/gui/3d-pin.d.ts.map +1 -0
  15. package/dist/backends/gui/3d-pin.js +49 -0
  16. package/dist/backends/gui/3d-pin.js.map +1 -0
  17. package/dist/backends/gui/advanced-chart.cjs +61 -0
  18. package/dist/backends/gui/advanced-chart.d.ts +38 -0
  19. package/dist/backends/gui/advanced-chart.d.ts.map +1 -0
  20. package/dist/backends/gui/advanced-chart.js +59 -0
  21. package/dist/backends/gui/advanced-chart.js.map +1 -0
  22. package/dist/backends/gui/alert.cjs +106 -0
  23. package/dist/backends/gui/alert.d.ts +39 -0
  24. package/dist/backends/gui/alert.d.ts.map +1 -0
  25. package/dist/backends/gui/alert.js +102 -0
  26. package/dist/backends/gui/alert.js.map +1 -0
  27. package/dist/backends/gui/android.cjs +52 -0
  28. package/dist/backends/gui/android.d.ts +16 -0
  29. package/dist/backends/gui/android.d.ts.map +1 -0
  30. package/dist/backends/gui/android.js +50 -0
  31. package/dist/backends/gui/android.js.map +1 -0
  32. package/dist/backends/gui/animated-beam.cjs +97 -0
  33. package/dist/backends/gui/animated-beam.d.ts +57 -0
  34. package/dist/backends/gui/animated-beam.d.ts.map +1 -0
  35. package/dist/backends/gui/animated-beam.js +62 -0
  36. package/dist/backends/gui/animated-beam.js.map +1 -0
  37. package/dist/backends/gui/animated-cursor.cjs +183 -0
  38. package/dist/backends/gui/animated-cursor.d.ts +42 -0
  39. package/dist/backends/gui/animated-cursor.d.ts.map +1 -0
  40. package/dist/backends/gui/animated-cursor.js +181 -0
  41. package/dist/backends/gui/animated-cursor.js.map +1 -0
  42. package/dist/backends/gui/animated-testimonials.cjs +109 -0
  43. package/dist/backends/gui/animated-testimonials.d.ts +23 -0
  44. package/dist/backends/gui/animated-testimonials.d.ts.map +1 -0
  45. package/dist/backends/gui/animated-testimonials.js +74 -0
  46. package/dist/backends/gui/animated-testimonials.js.map +1 -0
  47. package/dist/backends/gui/animated-tooltip.cjs +100 -0
  48. package/dist/backends/gui/animated-tooltip.d.ts +15 -0
  49. package/dist/backends/gui/animated-tooltip.d.ts.map +1 -0
  50. package/dist/backends/gui/animated-tooltip.js +65 -0
  51. package/dist/backends/gui/animated-tooltip.js.map +1 -0
  52. package/dist/backends/gui/announcement.cjs +54 -0
  53. package/dist/backends/gui/announcement.d.ts +26 -0
  54. package/dist/backends/gui/announcement.d.ts.map +1 -0
  55. package/dist/backends/gui/announcement.js +52 -0
  56. package/dist/backends/gui/announcement.js.map +1 -0
  57. package/dist/backends/gui/apple-cards-carousel.cjs +198 -0
  58. package/dist/backends/gui/apple-cards-carousel.d.ts +45 -0
  59. package/dist/backends/gui/apple-cards-carousel.d.ts.map +1 -0
  60. package/dist/backends/gui/apple-cards-carousel.js +162 -0
  61. package/dist/backends/gui/apple-cards-carousel.js.map +1 -0
  62. package/dist/backends/gui/apple-hello-effect.cjs +38 -0
  63. package/dist/backends/gui/apple-hello-effect.d.ts +9 -0
  64. package/dist/backends/gui/apple-hello-effect.d.ts.map +1 -0
  65. package/dist/backends/gui/apple-hello-effect.js +36 -0
  66. package/dist/backends/gui/apple-hello-effect.js.map +1 -0
  67. package/dist/backends/gui/banner.cjs +108 -0
  68. package/dist/backends/gui/banner.d.ts +33 -0
  69. package/dist/backends/gui/banner.d.ts.map +1 -0
  70. package/dist/backends/gui/banner.js +106 -0
  71. package/dist/backends/gui/banner.js.map +1 -0
  72. package/dist/backends/gui/calendar.cjs +152 -0
  73. package/dist/backends/gui/calendar.d.ts +41 -0
  74. package/dist/backends/gui/calendar.d.ts.map +1 -0
  75. package/dist/backends/gui/calendar.js +151 -0
  76. package/dist/backends/gui/calendar.js.map +1 -0
  77. package/dist/backends/gui/choicebox.cjs +50 -0
  78. package/dist/backends/gui/choicebox.d.ts +33 -0
  79. package/dist/backends/gui/choicebox.d.ts.map +1 -0
  80. package/dist/backends/gui/choicebox.js +49 -0
  81. package/dist/backends/gui/choicebox.js.map +1 -0
  82. package/dist/backends/gui/code-block.cjs +120 -0
  83. package/dist/backends/gui/code-block.d.ts +22 -0
  84. package/dist/backends/gui/code-block.d.ts.map +1 -0
  85. package/dist/backends/gui/code-block.js +118 -0
  86. package/dist/backends/gui/code-block.js.map +1 -0
  87. package/dist/backends/gui/code-editor.cjs +195 -0
  88. package/dist/backends/gui/code-editor.d.ts +43 -0
  89. package/dist/backends/gui/code-editor.d.ts.map +1 -0
  90. package/dist/backends/gui/code-editor.js +160 -0
  91. package/dist/backends/gui/code-editor.js.map +1 -0
  92. package/dist/backends/gui/code-tabs.cjs +21 -0
  93. package/dist/backends/gui/code-tabs.d.ts +22 -0
  94. package/dist/backends/gui/code-tabs.d.ts.map +1 -0
  95. package/dist/backends/gui/code-tabs.js +19 -0
  96. package/dist/backends/gui/code-tabs.js.map +1 -0
  97. package/dist/backends/gui/color-picker.cjs +141 -0
  98. package/dist/backends/gui/color-picker.d.ts +25 -0
  99. package/dist/backends/gui/color-picker.d.ts.map +1 -0
  100. package/dist/backends/gui/color-picker.js +139 -0
  101. package/dist/backends/gui/color-picker.js.map +1 -0
  102. package/dist/backends/gui/combobox.cjs +42 -0
  103. package/dist/backends/gui/combobox.d.ts +39 -0
  104. package/dist/backends/gui/combobox.d.ts.map +1 -0
  105. package/dist/backends/gui/combobox.js +40 -0
  106. package/dist/backends/gui/combobox.js.map +1 -0
  107. package/dist/backends/gui/company-profile.cjs +43 -0
  108. package/dist/backends/gui/company-profile.d.ts +33 -0
  109. package/dist/backends/gui/company-profile.d.ts.map +1 -0
  110. package/dist/backends/gui/company-profile.js +41 -0
  111. package/dist/backends/gui/company-profile.js.map +1 -0
  112. package/dist/backends/gui/comparison.cjs +43 -0
  113. package/dist/backends/gui/comparison.d.ts +16 -0
  114. package/dist/backends/gui/comparison.d.ts.map +1 -0
  115. package/dist/backends/gui/comparison.js +41 -0
  116. package/dist/backends/gui/comparison.js.map +1 -0
  117. package/dist/backends/gui/credit-card.cjs +80 -0
  118. package/dist/backends/gui/credit-card.d.ts +21 -0
  119. package/dist/backends/gui/credit-card.d.ts.map +1 -0
  120. package/dist/backends/gui/credit-card.js +78 -0
  121. package/dist/backends/gui/credit-card.js.map +1 -0
  122. package/dist/backends/gui/crypto-market.cjs +128 -0
  123. package/dist/backends/gui/crypto-market.d.ts +52 -0
  124. package/dist/backends/gui/crypto-market.d.ts.map +1 -0
  125. package/dist/backends/gui/crypto-market.js +126 -0
  126. package/dist/backends/gui/crypto-market.js.map +1 -0
  127. package/dist/backends/gui/crypto-screener.cjs +55 -0
  128. package/dist/backends/gui/crypto-screener.d.ts +42 -0
  129. package/dist/backends/gui/crypto-screener.d.ts.map +1 -0
  130. package/dist/backends/gui/crypto-screener.js +53 -0
  131. package/dist/backends/gui/crypto-screener.js.map +1 -0
  132. package/dist/backends/gui/cursor.cjs +40 -0
  133. package/dist/backends/gui/cursor.d.ts +16 -0
  134. package/dist/backends/gui/cursor.d.ts.map +1 -0
  135. package/dist/backends/gui/cursor.js +38 -0
  136. package/dist/backends/gui/cursor.js.map +1 -0
  137. package/dist/backends/gui/data-table.cjs +211 -0
  138. package/dist/backends/gui/data-table.d.ts +71 -0
  139. package/dist/backends/gui/data-table.d.ts.map +1 -0
  140. package/dist/backends/gui/data-table.js +175 -0
  141. package/dist/backends/gui/data-table.js.map +1 -0
  142. package/dist/backends/gui/date-picker.cjs +66 -0
  143. package/dist/backends/gui/date-picker.d.ts +29 -0
  144. package/dist/backends/gui/date-picker.d.ts.map +1 -0
  145. package/dist/backends/gui/date-picker.js +64 -0
  146. package/dist/backends/gui/date-picker.js.map +1 -0
  147. package/dist/backends/gui/desktop-spotlight.cjs +46 -0
  148. package/dist/backends/gui/desktop-spotlight.d.ts +34 -0
  149. package/dist/backends/gui/desktop-spotlight.d.ts.map +1 -0
  150. package/dist/backends/gui/desktop-spotlight.js +44 -0
  151. package/dist/backends/gui/desktop-spotlight.js.map +1 -0
  152. package/dist/backends/gui/desktop-window.cjs +180 -0
  153. package/dist/backends/gui/desktop-window.d.ts +51 -0
  154. package/dist/backends/gui/desktop-window.d.ts.map +1 -0
  155. package/dist/backends/gui/desktop-window.js +177 -0
  156. package/dist/backends/gui/desktop-window.js.map +1 -0
  157. package/dist/backends/gui/desktop.cjs +238 -0
  158. package/dist/backends/gui/desktop.d.ts +109 -0
  159. package/dist/backends/gui/desktop.d.ts.map +1 -0
  160. package/dist/backends/gui/desktop.js +199 -0
  161. package/dist/backends/gui/desktop.js.map +1 -0
  162. package/dist/backends/gui/dialog-stack.cjs +26 -0
  163. package/dist/backends/gui/dialog-stack.d.ts +23 -0
  164. package/dist/backends/gui/dialog-stack.d.ts.map +1 -0
  165. package/dist/backends/gui/dialog-stack.js +24 -0
  166. package/dist/backends/gui/dialog-stack.js.map +1 -0
  167. package/dist/backends/gui/dock.cjs +99 -0
  168. package/dist/backends/gui/dock.d.ts +41 -0
  169. package/dist/backends/gui/dock.d.ts.map +1 -0
  170. package/dist/backends/gui/dock.js +96 -0
  171. package/dist/backends/gui/dock.js.map +1 -0
  172. package/dist/backends/gui/dropzone.cjs +142 -0
  173. package/dist/backends/gui/dropzone.d.ts +12 -0
  174. package/dist/backends/gui/dropzone.d.ts.map +1 -0
  175. package/dist/backends/gui/dropzone.js +140 -0
  176. package/dist/backends/gui/dropzone.js.map +1 -0
  177. package/dist/backends/gui/editor.cjs +80 -0
  178. package/dist/backends/gui/editor.d.ts +23 -0
  179. package/dist/backends/gui/editor.d.ts.map +1 -0
  180. package/dist/backends/gui/editor.js +45 -0
  181. package/dist/backends/gui/editor.js.map +1 -0
  182. package/dist/backends/gui/empty.cjs +71 -0
  183. package/dist/backends/gui/empty.d.ts +43 -0
  184. package/dist/backends/gui/empty.d.ts.map +1 -0
  185. package/dist/backends/gui/empty.js +63 -0
  186. package/dist/backends/gui/empty.js.map +1 -0
  187. package/dist/backends/gui/financials.cjs +45 -0
  188. package/dist/backends/gui/financials.d.ts +36 -0
  189. package/dist/backends/gui/financials.d.ts.map +1 -0
  190. package/dist/backends/gui/financials.js +43 -0
  191. package/dist/backends/gui/financials.js.map +1 -0
  192. package/dist/backends/gui/forex-market.cjs +21 -0
  193. package/dist/backends/gui/forex-market.d.ts +17 -0
  194. package/dist/backends/gui/forex-market.d.ts.map +1 -0
  195. package/dist/backends/gui/forex-market.js +19 -0
  196. package/dist/backends/gui/forex-market.js.map +1 -0
  197. package/dist/backends/gui/forex-screener.cjs +57 -0
  198. package/dist/backends/gui/forex-screener.d.ts +45 -0
  199. package/dist/backends/gui/forex-screener.d.ts.map +1 -0
  200. package/dist/backends/gui/forex-screener.js +55 -0
  201. package/dist/backends/gui/forex-screener.js.map +1 -0
  202. package/dist/backends/gui/gantt.cjs +50 -0
  203. package/dist/backends/gui/gantt.d.ts +19 -0
  204. package/dist/backends/gui/gantt.d.ts.map +1 -0
  205. package/dist/backends/gui/gantt.js +49 -0
  206. package/dist/backends/gui/gantt.js.map +1 -0
  207. package/dist/backends/gui/index.cjs +108 -1
  208. package/dist/backends/gui/index.d.ts +41 -0
  209. package/dist/backends/gui/index.d.ts.map +1 -1
  210. package/dist/backends/gui/index.js +41 -0
  211. package/dist/backends/gui/index.js.map +1 -1
  212. package/dist/canvas.cjs +3 -3
  213. package/dist/canvas.js +3 -3
  214. package/dist/canvas.js.map +1 -1
  215. package/dist/gallery.html +2 -2
  216. package/dist/grid.cjs +19 -43
  217. package/dist/grid.d.ts +36 -1
  218. package/dist/grid.d.ts.map +1 -1
  219. package/dist/grid.js +19 -43
  220. package/dist/grid.js.map +1 -1
  221. package/dist/index.cjs +70 -1
  222. package/dist/index.d.ts +1 -0
  223. package/dist/index.d.ts.map +1 -1
  224. package/dist/index.js +3 -0
  225. package/dist/index.js.map +1 -1
  226. package/dist/index.node.cjs +70 -1
  227. package/dist/index.node.js +3 -0
  228. package/dist/product/ChunkGuard.cjs +58 -0
  229. package/dist/product/ChunkGuard.d.ts +7 -0
  230. package/dist/product/ChunkGuard.d.ts.map +1 -0
  231. package/dist/product/ChunkGuard.js +56 -0
  232. package/dist/product/ChunkGuard.js.map +1 -0
  233. package/dist/product/DetailPane.cjs +46 -0
  234. package/dist/product/DetailPane.d.ts +47 -0
  235. package/dist/product/DetailPane.d.ts.map +1 -0
  236. package/dist/product/DetailPane.js +43 -0
  237. package/dist/product/DetailPane.js.map +1 -0
  238. package/dist/product/Fact.cjs +19 -0
  239. package/dist/product/Fact.d.ts +23 -0
  240. package/dist/product/Fact.d.ts.map +1 -0
  241. package/dist/product/Fact.js +17 -0
  242. package/dist/product/Fact.js.map +1 -0
  243. package/dist/product/chunkGuard.logic.cjs +54 -0
  244. package/dist/product/chunkGuard.logic.d.ts +26 -0
  245. package/dist/product/chunkGuard.logic.d.ts.map +1 -0
  246. package/dist/product/chunkGuard.logic.js +49 -0
  247. package/dist/product/chunkGuard.logic.js.map +1 -0
  248. package/dist/product/index.cjs +10 -0
  249. package/dist/product/index.d.ts +3 -0
  250. package/dist/product/index.d.ts.map +1 -1
  251. package/dist/product/index.js +10 -0
  252. package/dist/product/index.js.map +1 -1
  253. package/package.json +5 -3
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * DatePicker — a button that opens a calendar in a popover, for picking one
5
+ * date (`mode="single"`, the default) or a range (`mode="range"`), with an
6
+ * optional list of quick presets shown above the calendar.
7
+ *
8
+ * The trigger reads the current pick, or `placeholder` while empty. `selected`
9
+ * and `onSelect` name the value the same way `Calendar` does, since this is a
10
+ * `Calendar` with a trigger and a panel in front of it, not a different
11
+ * contract. Picking a single date closes the popover; a range stays open
12
+ * until both ends are set. `disabled` marks days that cannot be picked, same
13
+ * contract as `Calendar`.
14
+ */
15
+ import { SizableText, YStack } from '@hanzo/gui';
16
+ import { Calendar as CalendarIcon } from '@hanzogui/lucide-icons-2';
17
+ import { useState } from 'react';
18
+ import { Button } from './button.js';
19
+ import { Calendar } from './calendar.js';
20
+ import { Popover, PopoverContent, PopoverTrigger } from './popover.js';
21
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select.js';
22
+ import { slot } from './slot.js';
23
+ const MONTHS = [
24
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
25
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
26
+ ];
27
+ const format = (d) => `${MONTHS[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
28
+ const sameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
29
+ export function DatePicker(props) {
30
+ const { placeholder = 'Pick a date', disabled, className, width = 280 } = props;
31
+ const isRange = props.mode === 'range';
32
+ const [open, setOpen] = useState(false);
33
+ const [ownDate, setOwnDate] = useState(isRange ? undefined : props.selected);
34
+ const [ownRange, setOwnRange] = useState(isRange ? props.selected : undefined);
35
+ const date = !isRange ? (props.selected ?? ownDate) : undefined;
36
+ const range = isRange ? (props.selected ?? ownRange) : undefined;
37
+ const selectDate = (d) => {
38
+ setOwnDate(d);
39
+ props.onSelect?.(d);
40
+ setOpen(false);
41
+ };
42
+ const selectRange = (r) => {
43
+ setOwnRange(r);
44
+ props.onSelect?.(r);
45
+ };
46
+ const label = isRange
47
+ ? range?.from
48
+ ? range.to && !sameDay(range.to, range.from)
49
+ ? `${format(range.from)} - ${format(range.to)}`
50
+ : format(range.from)
51
+ : placeholder
52
+ : date
53
+ ? format(date)
54
+ : placeholder;
55
+ const empty = isRange ? !range?.from : !date;
56
+ const presets = !isRange ? props.presets : undefined;
57
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { ...slot('date-picker-trigger'), "data-empty": empty || undefined, variant: "outline", width: width, justify: "flex-start", className: className, children: [_jsx(CalendarIcon, { size: 16 }), empty ? _jsx(SizableText, { color: "$quiet", children: label }) : label] }) }), _jsxs(PopoverContent, { ...slot('date-picker-content'), width: "auto", p: 0, align: "start", children: [presets && presets.length > 0 && (_jsx(YStack, { ...slot('date-picker-presets'), p: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: _jsxs(Select, { onValueChange: (value) => {
58
+ const days = Number(value);
59
+ const picked = new Date();
60
+ picked.setDate(picked.getDate() + days);
61
+ selectDate(picked);
62
+ }, children: [_jsx(SelectTrigger, { ...slot('date-picker-preset-trigger'), children: _jsx(SelectValue, { placeholder: "Select" }) }), _jsx(SelectContent, { children: presets.map((preset) => (_jsx(SelectItem, { value: String(preset.days), children: preset.label }, preset.days))) })] }) })), isRange ? (_jsx(Calendar, { mode: "range", selected: range, onSelect: selectRange, defaultMonth: range?.from, numberOfMonths: props.numberOfMonths ?? 2, disabled: disabled, initialFocus: true })) : (_jsx(Calendar, { mode: "single", selected: date, onSelect: selectDate, disabled: disabled, initialFocus: true }))] })] }));
63
+ }
64
+ //# sourceMappingURL=date-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-picker.js","sourceRoot":"","sources":["../../../src/backends/gui/date-picker.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ 'use client';"use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Spotlight = Spotlight;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ /**
7
+ * Spotlight — a macOS-style search dialog: type to filter a flat list of
8
+ * items grouped by category, arrow keys move the cursor, Enter runs the
9
+ * highlighted row, Escape or a backdrop click closes it.
10
+ *
11
+ * The search, the grouping, the cursor and the keyboard bindings are not
12
+ * reimplemented here — they are `Command`'s, the same state machine the
13
+ * command palette runs on. This file is composition: a `CommandDialog` wired
14
+ * to `isOpen`/`onClose`, with `SpotlightItem`s spread across `CommandGroup`s.
15
+ */
16
+ const react_1 = require("react");
17
+ const gui_1 = require("@hanzo/gui");
18
+ const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
19
+ const command_1 = require("./command.cjs");
20
+ const NO_CATEGORY = 'Results';
21
+ /** Items in first-seen category order — the grouping `Object.entries` alone won't give. */
22
+ function groupByCategory(items) {
23
+ const order = [];
24
+ const groups = new Map();
25
+ for (const item of items) {
26
+ const category = item.category || NO_CATEGORY;
27
+ if (!groups.has(category)) {
28
+ groups.set(category, []);
29
+ order.push(category);
30
+ }
31
+ groups.get(category).push(item);
32
+ }
33
+ return order.map((category) => [category, groups.get(category)]);
34
+ }
35
+ function Spotlight({ isOpen, onClose, items = [], onSelect, placeholder = 'Search...', }) {
36
+ const groups = (0, react_1.useMemo)(() => groupByCategory(items), [items]);
37
+ const pick = (item) => {
38
+ item.action?.();
39
+ onSelect?.(item);
40
+ onClose();
41
+ };
42
+ return ((0, jsx_runtime_1.jsxs)(command_1.CommandDialog, { "data-slot": "spotlight", open: isOpen, onOpenChange: (open) => {
43
+ if (!open)
44
+ onClose();
45
+ }, title: "Spotlight", description: placeholder, children: [(0, jsx_runtime_1.jsx)(command_1.CommandInput, { placeholder: placeholder, autoFocus: true }), (0, jsx_runtime_1.jsxs)(command_1.CommandList, { children: [(0, jsx_runtime_1.jsx)(command_1.CommandEmpty, { children: "No results found" }), groups.map(([category, categoryItems]) => ((0, jsx_runtime_1.jsx)(command_1.CommandGroup, { heading: category, children: categoryItems.map((item) => ((0, jsx_runtime_1.jsx)(command_1.CommandItem, { "data-slot": "spotlight-item", value: item.title, keywords: item.keywords, onSelect: () => pick(item), children: (0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$3", flex: 1, minW: 0, children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", justify: "center", width: 32, height: 32, rounded: "$3", bg: "$hover", children: item.icon ?? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Search, { size: 16, opacity: 0.7 }) }), (0, jsx_runtime_1.jsxs)(gui_1.YStack, { flex: 1, minW: 0, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$3", fontWeight: "500", numberOfLines: 1, children: item.title }), item.subtitle ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$quiet", numberOfLines: 1, children: item.subtitle })) : null] })] }) }, item.id))) }, category)))] })] }));
46
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Spotlight — a macOS-style search dialog: type to filter a flat list of
3
+ * items grouped by category, arrow keys move the cursor, Enter runs the
4
+ * highlighted row, Escape or a backdrop click closes it.
5
+ *
6
+ * The search, the grouping, the cursor and the keyboard bindings are not
7
+ * reimplemented here — they are `Command`'s, the same state machine the
8
+ * command palette runs on. This file is composition: a `CommandDialog` wired
9
+ * to `isOpen`/`onClose`, with `SpotlightItem`s spread across `CommandGroup`s.
10
+ */
11
+ import { type ReactNode } from 'react';
12
+ export interface SpotlightItem {
13
+ id: string;
14
+ title: string;
15
+ subtitle?: string;
16
+ icon?: ReactNode;
17
+ category?: string;
18
+ keywords?: string[];
19
+ action?: () => void;
20
+ }
21
+ export interface SpotlightProps {
22
+ /** Controls visibility of the spotlight dialog. */
23
+ isOpen: boolean;
24
+ /** Called when the spotlight is closed — Escape, backdrop, or a selection. */
25
+ onClose: () => void;
26
+ /** The searchable items, in the order groups should fall when unfiltered. */
27
+ items?: SpotlightItem[];
28
+ /** Called with the picked item, after its own `action` (if any) runs. */
29
+ onSelect?: (item: SpotlightItem) => void;
30
+ /** Search input placeholder text. */
31
+ placeholder?: string;
32
+ }
33
+ export declare function Spotlight({ isOpen, onClose, items, onSelect, placeholder, }: SpotlightProps): import("react").JSX.Element;
34
+ //# sourceMappingURL=desktop-spotlight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-spotlight.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/desktop-spotlight.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAa/C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,MAAM,EAAE,OAAO,CAAA;IACf,8EAA8E;IAC9E,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,aAAa,EAAE,CAAA;IACvB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAA;IACxC,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAmBD,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,OAAO,EACP,KAAU,EACV,QAAQ,EACR,WAAyB,GAC1B,EAAE,cAAc,+BA6DhB"}
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Spotlight — a macOS-style search dialog: type to filter a flat list of
5
+ * items grouped by category, arrow keys move the cursor, Enter runs the
6
+ * highlighted row, Escape or a backdrop click closes it.
7
+ *
8
+ * The search, the grouping, the cursor and the keyboard bindings are not
9
+ * reimplemented here — they are `Command`'s, the same state machine the
10
+ * command palette runs on. This file is composition: a `CommandDialog` wired
11
+ * to `isOpen`/`onClose`, with `SpotlightItem`s spread across `CommandGroup`s.
12
+ */
13
+ import { useMemo } from 'react';
14
+ import { SizableText, XStack, YStack } from '@hanzo/gui';
15
+ import { Search as SearchIcon } from '@hanzogui/lucide-icons-2';
16
+ import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from './command.js';
17
+ const NO_CATEGORY = 'Results';
18
+ /** Items in first-seen category order — the grouping `Object.entries` alone won't give. */
19
+ function groupByCategory(items) {
20
+ const order = [];
21
+ const groups = new Map();
22
+ for (const item of items) {
23
+ const category = item.category || NO_CATEGORY;
24
+ if (!groups.has(category)) {
25
+ groups.set(category, []);
26
+ order.push(category);
27
+ }
28
+ groups.get(category).push(item);
29
+ }
30
+ return order.map((category) => [category, groups.get(category)]);
31
+ }
32
+ export function Spotlight({ isOpen, onClose, items = [], onSelect, placeholder = 'Search...', }) {
33
+ const groups = useMemo(() => groupByCategory(items), [items]);
34
+ const pick = (item) => {
35
+ item.action?.();
36
+ onSelect?.(item);
37
+ onClose();
38
+ };
39
+ return (_jsxs(CommandDialog, { "data-slot": "spotlight", open: isOpen, onOpenChange: (open) => {
40
+ if (!open)
41
+ onClose();
42
+ }, title: "Spotlight", description: placeholder, children: [_jsx(CommandInput, { placeholder: placeholder, autoFocus: true }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: "No results found" }), groups.map(([category, categoryItems]) => (_jsx(CommandGroup, { heading: category, children: categoryItems.map((item) => (_jsx(CommandItem, { "data-slot": "spotlight-item", value: item.title, keywords: item.keywords, onSelect: () => pick(item), children: _jsxs(XStack, { items: "center", gap: "$3", flex: 1, minW: 0, children: [_jsx(XStack, { items: "center", justify: "center", width: 32, height: 32, rounded: "$3", bg: "$hover", children: item.icon ?? _jsx(SearchIcon, { size: 16, opacity: 0.7 }) }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "500", numberOfLines: 1, children: item.title }), item.subtitle ? (_jsx(SizableText, { size: "$1", color: "$quiet", numberOfLines: 1, children: item.subtitle })) : null] })] }) }, item.id))) }, category)))] })] }));
43
+ }
44
+ //# sourceMappingURL=desktop-spotlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-spotlight.js","sourceRoot":"","sources":["../../../src/backends/gui/desktop-spotlight.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE/D,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,WAAW,GACZ,MAAM,WAAW,CAAA;AAyBlB,MAAM,WAAW,GAAG,SAAS,CAAA;AAE7B,2FAA2F;AAC3F,SAAS,eAAe,CAAC,KAAsB;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAA;IACjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAA;QAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;YACxB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACtB,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAU,CAAC,CAAA;AAC5E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EACxB,MAAM,EACN,OAAO,EACP,KAAK,GAAG,EAAE,EACV,QAAQ,EACR,WAAW,GAAG,WAAW,GACV;IACf,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAE7D,MAAM,IAAI,GAAG,CAAC,IAAmB,EAAE,EAAE;QACnC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAA;QACf,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAA;QAChB,OAAO,EAAE,CAAA;IACX,CAAC,CAAA;IAED,OAAO,CACL,MAAC,aAAa,iBACF,WAAW,EACrB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;YACrB,IAAI,CAAC,IAAI;gBAAE,OAAO,EAAE,CAAA;QACtB,CAAC,EACD,KAAK,EAAC,WAAW,EACjB,WAAW,EAAE,WAAW,aAExB,KAAC,YAAY,IAAC,WAAW,EAAE,WAAW,EAAE,SAAS,SAAG,EACpD,MAAC,WAAW,eACV,KAAC,YAAY,mCAAgC,EAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CACzC,KAAC,YAAY,IAAgB,OAAO,EAAE,QAAQ,YAC3C,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAC3B,KAAC,WAAW,iBAEA,gBAAgB,EAC1B,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,YAE1B,MAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,aAC9C,KAAC,MAAM,IACL,KAAK,EAAC,QAAQ,EACd,OAAO,EAAC,QAAQ,EAChB,KAAK,EAAE,EAAE,EACT,MAAM,EAAE,EAAE,EACV,OAAO,EAAC,IAAI,EACZ,EAAE,EAAC,QAAQ,YAEV,IAAI,CAAC,IAAI,IAAI,KAAC,UAAU,IAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,GAAI,GAC7C,EACT,MAAC,MAAM,IAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,aACtB,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,aAAa,EAAE,CAAC,YACrD,IAAI,CAAC,KAAK,GACC,EACb,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CACf,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,EAAC,aAAa,EAAE,CAAC,YACnD,IAAI,CAAC,QAAQ,GACF,CACf,CAAC,CAAC,CAAC,IAAI,IACD,IACF,IA3BJ,IAAI,CAAC,EAAE,CA4BA,CACf,CAAC,IAhCe,QAAQ,CAiCZ,CAChB,CAAC,IACU,IACA,CACjB,CAAA;AACH,CAAC"}
@@ -0,0 +1,180 @@
1
+ 'use client';"use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.DesktopWindowControls = DesktopWindowControls;
5
+ exports.DesktopWindow = DesktopWindow;
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ /**
8
+ * DesktopWindow — a macOS-style floating panel: a title bar with traffic-light
9
+ * controls (close/minimize/maximize), a draggable title bar, a resizable
10
+ * bottom-right corner, and a double-click (or the maximize button) to fill the
11
+ * viewport and restore.
12
+ *
13
+ * Dragging and resizing both run on `drag()` from `./gesture` — the one
14
+ * pointer/responder contract this backend already uses for the color picker's
15
+ * saturation field and hue bar. Position and size are plain numeric state; the
16
+ * frame's box is set through `style` because it is a live pixel rectangle, not
17
+ * a themed surface — everything else on the frame (background, border, radius,
18
+ * shadow) is a token read through `variant`.
19
+ */
20
+ const gui_1 = require("@hanzo/gui");
21
+ const react_1 = require("react");
22
+ const gesture_1 = require("./gesture.cjs");
23
+ const slot_1 = require("./slot.cjs");
24
+ const TITLE_BAR_H = 40;
25
+ const CONTROL = 12;
26
+ const RESIZE_HANDLE = 16;
27
+ const box = (el) => typeof HTMLElement !== 'undefined' && el instanceof HTMLElement ? el : null;
28
+ const WindowContext = /* @__PURE__ */ (0, gui_1.createStyledContext)({
29
+ windowType: 'default',
30
+ });
31
+ const Frame = (0, gui_1.styled)(gui_1.YStack, {
32
+ name: 'DesktopWindow',
33
+ context: WindowContext,
34
+ borderWidth: 1,
35
+ rounded: '$5',
36
+ overflow: 'hidden',
37
+ shadowColor: '$shadowColor',
38
+ shadowRadius: 24,
39
+ variants: {
40
+ windowType: {
41
+ default: { bg: '$panel', borderColor: '$borderColor' },
42
+ dark: { bg: '$color1', borderColor: '$borderColor' },
43
+ light: { bg: '$color12', borderColor: '$borderColor' },
44
+ transparent: { bg: '$panel', borderColor: '$borderColor', opacity: 0.92 },
45
+ },
46
+ },
47
+ defaultVariants: { windowType: 'default' },
48
+ });
49
+ const TitleText = (0, gui_1.styled)(gui_1.SizableText, {
50
+ name: 'DesktopWindowTitleText',
51
+ context: WindowContext,
52
+ size: '$2',
53
+ fontWeight: '600',
54
+ variants: {
55
+ windowType: {
56
+ default: { color: '$ink' },
57
+ dark: { color: '$white1' },
58
+ light: { color: '$color1' },
59
+ transparent: { color: '$ink' },
60
+ },
61
+ },
62
+ });
63
+ /** One traffic-light dot: close (red), minimize (yellow) or maximize (green). */
64
+ const Light = (0, gui_1.styled)(gui_1.XStack, {
65
+ name: 'DesktopWindowLight',
66
+ width: CONTROL,
67
+ height: CONTROL,
68
+ rounded: 999,
69
+ items: 'center',
70
+ justify: 'center',
71
+ cursor: 'pointer',
72
+ variants: {
73
+ tone: {
74
+ close: { bg: '$red9', hoverStyle: { bg: '$red10' } },
75
+ minimize: { bg: '$yellow9', hoverStyle: { bg: '$yellow10' } },
76
+ maximize: { bg: '$green9', hoverStyle: { bg: '$green10' } },
77
+ },
78
+ },
79
+ });
80
+ /** The three traffic-light dots on their own, for a title bar built by hand. */
81
+ function DesktopWindowControls({ onClose, onMinimize, onMaximize, isMaximized = false, }) {
82
+ return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('desktop-window-controls'), items: "center", gap: "$2", children: [(0, jsx_runtime_1.jsx)(Light, { ...(0, slot_1.slot)('desktop-window-close'), tone: "close", onPress: onClose, ...(0, gesture_1.touch)(CONTROL, 44, 'both'), "aria-label": "Close window", role: "button" }), (0, jsx_runtime_1.jsx)(Light, { ...(0, slot_1.slot)('desktop-window-minimize'), tone: "minimize", onPress: onMinimize, ...(0, gesture_1.touch)(CONTROL, 44, 'both'), "aria-label": "Minimize window", role: "button" }), (0, jsx_runtime_1.jsx)(Light, { ...(0, slot_1.slot)('desktop-window-maximize'), tone: "maximize", onPress: onMaximize, ...(0, gesture_1.touch)(CONTROL, 44, 'both'), "aria-label": isMaximized ? 'Restore window' : 'Maximize window', role: "button" })] }));
83
+ }
84
+ /**
85
+ * A macOS-style window: `title` names it, `onClose`/`onMinimize`/`onMaximize`
86
+ * wire the traffic lights, `initialPosition`/`initialSize` place it, and
87
+ * `windowType` picks its surface. Position and size stay in this component —
88
+ * a caller that needs several windows keeps one of these per window and an
89
+ * `onFocus` that raises its `zIndex`.
90
+ */
91
+ function DesktopWindow({ title, icon, children, onClose, onMinimize, onMaximize, onFocus, initialPosition, initialSize, minWidth = 200, minHeight = 150, maxWidth, maxHeight, windowType = 'default', resizable = true, draggable = true, hideControls = false, zIndex = 1000, }) {
92
+ const frameRef = (0, react_1.useRef)(null);
93
+ const [position, setPosition] = (0, react_1.useState)(() => initialPosition ?? { x: 100, y: 100 });
94
+ const [size, setSize] = (0, react_1.useState)(() => initialSize ?? { width: 800, height: 600 });
95
+ const [isMaximized, setIsMaximized] = (0, react_1.useState)(false);
96
+ const restoreRef = (0, react_1.useRef)(null);
97
+ const lastPointer = (0, react_1.useRef)({ x: 0, y: 0 });
98
+ const startSize = (0, react_1.useRef)(size);
99
+ const titleId = `desktop-window-title-${title.replace(/\s+/g, '-').toLowerCase()}`;
100
+ const handleMaximize = (0, react_1.useCallback)(() => {
101
+ setIsMaximized((was) => {
102
+ if (was) {
103
+ if (restoreRef.current) {
104
+ setPosition(restoreRef.current.position);
105
+ setSize(restoreRef.current.size);
106
+ }
107
+ return false;
108
+ }
109
+ restoreRef.current = { position, size };
110
+ setPosition({ x: 0, y: 0 });
111
+ setSize({
112
+ width: typeof window !== 'undefined' ? window.innerWidth : size.width,
113
+ height: typeof window !== 'undefined' ? window.innerHeight : size.height,
114
+ });
115
+ return true;
116
+ });
117
+ onMaximize?.();
118
+ }, [position, size, onMaximize]);
119
+ const beginDrag = (0, react_1.useCallback)((e) => {
120
+ lastPointer.current = { x: (0, gesture_1.dragPos)(e, true), y: (0, gesture_1.dragPos)(e, false) };
121
+ }, []);
122
+ const moveDrag = (0, react_1.useCallback)((e) => {
123
+ const x = (0, gesture_1.dragPos)(e, true);
124
+ const y = (0, gesture_1.dragPos)(e, false);
125
+ const dx = x - lastPointer.current.x;
126
+ const dy = y - lastPointer.current.y;
127
+ lastPointer.current = { x, y };
128
+ setPosition((p) => ({ x: p.x + dx, y: p.y + dy }));
129
+ }, []);
130
+ const dragGesture = (0, gesture_1.drag)({
131
+ begin: beginDrag,
132
+ move: moveDrag,
133
+ end: () => { },
134
+ enabled: draggable && !isMaximized,
135
+ });
136
+ const beginResize = (0, react_1.useCallback)((e) => {
137
+ lastPointer.current = { x: (0, gesture_1.dragPos)(e, true), y: (0, gesture_1.dragPos)(e, false) };
138
+ startSize.current = size;
139
+ }, [size]);
140
+ const moveResize = (0, react_1.useCallback)((e) => {
141
+ const x = (0, gesture_1.dragPos)(e, true);
142
+ const y = (0, gesture_1.dragPos)(e, false);
143
+ const dx = x - lastPointer.current.x;
144
+ const dy = y - lastPointer.current.y;
145
+ const width = Math.max(minWidth, Math.min(startSize.current.width + dx, maxWidth ?? Infinity));
146
+ const height = Math.max(minHeight, Math.min(startSize.current.height + dy, maxHeight ?? Infinity));
147
+ setSize({ width, height });
148
+ }, [minWidth, minHeight, maxWidth, maxHeight]);
149
+ const resizeGesture = (0, gesture_1.drag)({
150
+ begin: beginResize,
151
+ move: moveResize,
152
+ end: () => { },
153
+ enabled: resizable && !isMaximized,
154
+ });
155
+ // Escape closes the window, same as a real one — only while it has focus.
156
+ (0, react_1.useEffect)(() => {
157
+ const el = box(frameRef.current);
158
+ if (!el)
159
+ return;
160
+ const onKeyDown = (e) => {
161
+ if (e.key === 'Escape')
162
+ onClose?.();
163
+ };
164
+ el.addEventListener('keydown', onKeyDown);
165
+ return () => el.removeEventListener('keydown', onKeyDown);
166
+ }, [onClose]);
167
+ return ((0, jsx_runtime_1.jsxs)(Frame, { ref: frameRef, ...(0, slot_1.slot)('desktop-window'), windowType: windowType, role: "dialog", "aria-labelledby": titleId, tabIndex: -1, onPointerDown: onFocus, style: {
168
+ position: 'fixed',
169
+ left: 0,
170
+ top: 0,
171
+ transform: `translate(${position.x}px, ${position.y}px)`,
172
+ width: size.width,
173
+ height: size.height,
174
+ minWidth,
175
+ minHeight,
176
+ maxWidth: isMaximized ? undefined : maxWidth,
177
+ maxHeight: isMaximized ? undefined : maxHeight,
178
+ zIndex,
179
+ }, children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('desktop-window-title-bar'), height: TITLE_BAR_H, px: "$3", items: "center", justify: "space-between", borderBottomWidth: 1, borderColor: "$borderColor", cursor: draggable && !isMaximized ? 'grab' : 'default', onDoubleClick: handleMaximize, ...dragGesture, children: [hideControls ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { width: CONTROL * 3 + 16 })) : ((0, jsx_runtime_1.jsx)(DesktopWindowControls, { onClose: onClose, onMinimize: onMinimize, onMaximize: handleMaximize, isMaximized: isMaximized })), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$2", flex: 1, justify: "center", children: [icon, (0, jsx_runtime_1.jsx)(TitleText, { id: titleId, windowType: windowType, numberOfLines: 1, children: title })] }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { width: CONTROL * 3 + 16 })] }), (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('desktop-window-content'), flex: 1, overflow: "scroll", children: children }), resizable && !isMaximized ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('desktop-window-resize-handle'), position: "absolute", b: 0, r: 0, width: RESIZE_HANDLE, height: RESIZE_HANDLE, cursor: "se-resize", ...resizeGesture })) : null] }));
180
+ }
@@ -0,0 +1,51 @@
1
+ import { type ReactNode } from 'react';
2
+ export type DesktopWindowType = 'default' | 'dark' | 'light' | 'transparent';
3
+ export type DesktopWindowControlsProps = {
4
+ onClose?: () => void;
5
+ onMinimize?: () => void;
6
+ onMaximize?: () => void;
7
+ isMaximized?: boolean;
8
+ };
9
+ /** The three traffic-light dots on their own, for a title bar built by hand. */
10
+ export declare function DesktopWindowControls({ onClose, onMinimize, onMaximize, isMaximized, }: DesktopWindowControlsProps): import("react").JSX.Element;
11
+ export type DesktopWindowProps = {
12
+ /** Text shown in the title bar. */
13
+ title: string;
14
+ icon?: ReactNode;
15
+ children?: ReactNode;
16
+ onClose?: () => void;
17
+ onMinimize?: () => void;
18
+ onMaximize?: () => void;
19
+ /** Fires on pointer-down anywhere on the frame — wire it to bring-to-front. */
20
+ onFocus?: () => void;
21
+ initialPosition?: {
22
+ x: number;
23
+ y: number;
24
+ };
25
+ initialSize?: {
26
+ width: number;
27
+ height: number;
28
+ };
29
+ minWidth?: number;
30
+ minHeight?: number;
31
+ maxWidth?: number;
32
+ maxHeight?: number;
33
+ /** Style variant — the frame's background, border and title-text color. */
34
+ windowType?: DesktopWindowType;
35
+ /** Drag the corner to resize. Defaults to on. */
36
+ resizable?: boolean;
37
+ /** Drag the title bar to move. Defaults to on. */
38
+ draggable?: boolean;
39
+ /** Hide the three traffic-light dots. */
40
+ hideControls?: boolean;
41
+ zIndex?: number;
42
+ };
43
+ /**
44
+ * A macOS-style window: `title` names it, `onClose`/`onMinimize`/`onMaximize`
45
+ * wire the traffic lights, `initialPosition`/`initialSize` place it, and
46
+ * `windowType` picks its surface. Position and size stay in this component —
47
+ * a caller that needs several windows keeps one of these per window and an
48
+ * `onFocus` that raises its `zIndex`.
49
+ */
50
+ export declare function DesktopWindow({ title, icon, children, onClose, onMinimize, onMaximize, onFocus, initialPosition, initialSize, minWidth, minHeight, maxWidth, maxHeight, windowType, resizable, draggable, hideControls, zIndex, }: DesktopWindowProps): import("react").JSX.Element;
51
+ //# sourceMappingURL=desktop-window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-window.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/desktop-window.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAA4C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIhF,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAA;AAqE5E,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,EACpC,OAAO,EACP,UAAU,EACV,UAAU,EACV,WAAmB,GACpB,EAAE,0BAA0B,+BA6B5B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,eAAe,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,yCAAyC;IACzC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,UAAU,EACV,UAAU,EACV,OAAO,EACP,eAAe,EACf,WAAW,EACX,QAAc,EACd,SAAe,EACf,QAAQ,EACR,SAAS,EACT,UAAsB,EACtB,SAAgB,EAChB,SAAgB,EAChB,YAAoB,EACpB,MAAa,GACd,EAAE,kBAAkB,+BA+JpB"}
@@ -0,0 +1,177 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * DesktopWindow — a macOS-style floating panel: a title bar with traffic-light
5
+ * controls (close/minimize/maximize), a draggable title bar, a resizable
6
+ * bottom-right corner, and a double-click (or the maximize button) to fill the
7
+ * viewport and restore.
8
+ *
9
+ * Dragging and resizing both run on `drag()` from `./gesture` — the one
10
+ * pointer/responder contract this backend already uses for the color picker's
11
+ * saturation field and hue bar. Position and size are plain numeric state; the
12
+ * frame's box is set through `style` because it is a live pixel rectangle, not
13
+ * a themed surface — everything else on the frame (background, border, radius,
14
+ * shadow) is a token read through `variant`.
15
+ */
16
+ import { SizableText, XStack, YStack, createStyledContext, styled } from '@hanzo/gui';
17
+ import { useCallback, useEffect, useRef, useState } from 'react';
18
+ import { drag, dragPos, touch } from './gesture.js';
19
+ import { slot } from './slot.js';
20
+ const TITLE_BAR_H = 40;
21
+ const CONTROL = 12;
22
+ const RESIZE_HANDLE = 16;
23
+ const box = (el) => typeof HTMLElement !== 'undefined' && el instanceof HTMLElement ? el : null;
24
+ const WindowContext = /* @__PURE__ */ createStyledContext({
25
+ windowType: 'default',
26
+ });
27
+ const Frame = styled(YStack, {
28
+ name: 'DesktopWindow',
29
+ context: WindowContext,
30
+ borderWidth: 1,
31
+ rounded: '$5',
32
+ overflow: 'hidden',
33
+ shadowColor: '$shadowColor',
34
+ shadowRadius: 24,
35
+ variants: {
36
+ windowType: {
37
+ default: { bg: '$panel', borderColor: '$borderColor' },
38
+ dark: { bg: '$color1', borderColor: '$borderColor' },
39
+ light: { bg: '$color12', borderColor: '$borderColor' },
40
+ transparent: { bg: '$panel', borderColor: '$borderColor', opacity: 0.92 },
41
+ },
42
+ },
43
+ defaultVariants: { windowType: 'default' },
44
+ });
45
+ const TitleText = styled(SizableText, {
46
+ name: 'DesktopWindowTitleText',
47
+ context: WindowContext,
48
+ size: '$2',
49
+ fontWeight: '600',
50
+ variants: {
51
+ windowType: {
52
+ default: { color: '$ink' },
53
+ dark: { color: '$white1' },
54
+ light: { color: '$color1' },
55
+ transparent: { color: '$ink' },
56
+ },
57
+ },
58
+ });
59
+ /** One traffic-light dot: close (red), minimize (yellow) or maximize (green). */
60
+ const Light = styled(XStack, {
61
+ name: 'DesktopWindowLight',
62
+ width: CONTROL,
63
+ height: CONTROL,
64
+ rounded: 999,
65
+ items: 'center',
66
+ justify: 'center',
67
+ cursor: 'pointer',
68
+ variants: {
69
+ tone: {
70
+ close: { bg: '$red9', hoverStyle: { bg: '$red10' } },
71
+ minimize: { bg: '$yellow9', hoverStyle: { bg: '$yellow10' } },
72
+ maximize: { bg: '$green9', hoverStyle: { bg: '$green10' } },
73
+ },
74
+ },
75
+ });
76
+ /** The three traffic-light dots on their own, for a title bar built by hand. */
77
+ export function DesktopWindowControls({ onClose, onMinimize, onMaximize, isMaximized = false, }) {
78
+ return (_jsxs(XStack, { ...slot('desktop-window-controls'), items: "center", gap: "$2", children: [_jsx(Light, { ...slot('desktop-window-close'), tone: "close", onPress: onClose, ...touch(CONTROL, 44, 'both'), "aria-label": "Close window", role: "button" }), _jsx(Light, { ...slot('desktop-window-minimize'), tone: "minimize", onPress: onMinimize, ...touch(CONTROL, 44, 'both'), "aria-label": "Minimize window", role: "button" }), _jsx(Light, { ...slot('desktop-window-maximize'), tone: "maximize", onPress: onMaximize, ...touch(CONTROL, 44, 'both'), "aria-label": isMaximized ? 'Restore window' : 'Maximize window', role: "button" })] }));
79
+ }
80
+ /**
81
+ * A macOS-style window: `title` names it, `onClose`/`onMinimize`/`onMaximize`
82
+ * wire the traffic lights, `initialPosition`/`initialSize` place it, and
83
+ * `windowType` picks its surface. Position and size stay in this component —
84
+ * a caller that needs several windows keeps one of these per window and an
85
+ * `onFocus` that raises its `zIndex`.
86
+ */
87
+ export function DesktopWindow({ title, icon, children, onClose, onMinimize, onMaximize, onFocus, initialPosition, initialSize, minWidth = 200, minHeight = 150, maxWidth, maxHeight, windowType = 'default', resizable = true, draggable = true, hideControls = false, zIndex = 1000, }) {
88
+ const frameRef = useRef(null);
89
+ const [position, setPosition] = useState(() => initialPosition ?? { x: 100, y: 100 });
90
+ const [size, setSize] = useState(() => initialSize ?? { width: 800, height: 600 });
91
+ const [isMaximized, setIsMaximized] = useState(false);
92
+ const restoreRef = useRef(null);
93
+ const lastPointer = useRef({ x: 0, y: 0 });
94
+ const startSize = useRef(size);
95
+ const titleId = `desktop-window-title-${title.replace(/\s+/g, '-').toLowerCase()}`;
96
+ const handleMaximize = useCallback(() => {
97
+ setIsMaximized((was) => {
98
+ if (was) {
99
+ if (restoreRef.current) {
100
+ setPosition(restoreRef.current.position);
101
+ setSize(restoreRef.current.size);
102
+ }
103
+ return false;
104
+ }
105
+ restoreRef.current = { position, size };
106
+ setPosition({ x: 0, y: 0 });
107
+ setSize({
108
+ width: typeof window !== 'undefined' ? window.innerWidth : size.width,
109
+ height: typeof window !== 'undefined' ? window.innerHeight : size.height,
110
+ });
111
+ return true;
112
+ });
113
+ onMaximize?.();
114
+ }, [position, size, onMaximize]);
115
+ const beginDrag = useCallback((e) => {
116
+ lastPointer.current = { x: dragPos(e, true), y: dragPos(e, false) };
117
+ }, []);
118
+ const moveDrag = useCallback((e) => {
119
+ const x = dragPos(e, true);
120
+ const y = dragPos(e, false);
121
+ const dx = x - lastPointer.current.x;
122
+ const dy = y - lastPointer.current.y;
123
+ lastPointer.current = { x, y };
124
+ setPosition((p) => ({ x: p.x + dx, y: p.y + dy }));
125
+ }, []);
126
+ const dragGesture = drag({
127
+ begin: beginDrag,
128
+ move: moveDrag,
129
+ end: () => { },
130
+ enabled: draggable && !isMaximized,
131
+ });
132
+ const beginResize = useCallback((e) => {
133
+ lastPointer.current = { x: dragPos(e, true), y: dragPos(e, false) };
134
+ startSize.current = size;
135
+ }, [size]);
136
+ const moveResize = useCallback((e) => {
137
+ const x = dragPos(e, true);
138
+ const y = dragPos(e, false);
139
+ const dx = x - lastPointer.current.x;
140
+ const dy = y - lastPointer.current.y;
141
+ const width = Math.max(minWidth, Math.min(startSize.current.width + dx, maxWidth ?? Infinity));
142
+ const height = Math.max(minHeight, Math.min(startSize.current.height + dy, maxHeight ?? Infinity));
143
+ setSize({ width, height });
144
+ }, [minWidth, minHeight, maxWidth, maxHeight]);
145
+ const resizeGesture = drag({
146
+ begin: beginResize,
147
+ move: moveResize,
148
+ end: () => { },
149
+ enabled: resizable && !isMaximized,
150
+ });
151
+ // Escape closes the window, same as a real one — only while it has focus.
152
+ useEffect(() => {
153
+ const el = box(frameRef.current);
154
+ if (!el)
155
+ return;
156
+ const onKeyDown = (e) => {
157
+ if (e.key === 'Escape')
158
+ onClose?.();
159
+ };
160
+ el.addEventListener('keydown', onKeyDown);
161
+ return () => el.removeEventListener('keydown', onKeyDown);
162
+ }, [onClose]);
163
+ return (_jsxs(Frame, { ref: frameRef, ...slot('desktop-window'), windowType: windowType, role: "dialog", "aria-labelledby": titleId, tabIndex: -1, onPointerDown: onFocus, style: {
164
+ position: 'fixed',
165
+ left: 0,
166
+ top: 0,
167
+ transform: `translate(${position.x}px, ${position.y}px)`,
168
+ width: size.width,
169
+ height: size.height,
170
+ minWidth,
171
+ minHeight,
172
+ maxWidth: isMaximized ? undefined : maxWidth,
173
+ maxHeight: isMaximized ? undefined : maxHeight,
174
+ zIndex,
175
+ }, children: [_jsxs(XStack, { ...slot('desktop-window-title-bar'), height: TITLE_BAR_H, px: "$3", items: "center", justify: "space-between", borderBottomWidth: 1, borderColor: "$borderColor", cursor: draggable && !isMaximized ? 'grab' : 'default', onDoubleClick: handleMaximize, ...dragGesture, children: [hideControls ? (_jsx(XStack, { width: CONTROL * 3 + 16 })) : (_jsx(DesktopWindowControls, { onClose: onClose, onMinimize: onMinimize, onMaximize: handleMaximize, isMaximized: isMaximized })), _jsxs(XStack, { items: "center", gap: "$2", flex: 1, justify: "center", children: [icon, _jsx(TitleText, { id: titleId, windowType: windowType, numberOfLines: 1, children: title })] }), _jsx(XStack, { width: CONTROL * 3 + 16 })] }), _jsx(YStack, { ...slot('desktop-window-content'), flex: 1, overflow: "scroll", children: children }), resizable && !isMaximized ? (_jsx(XStack, { ...slot('desktop-window-resize-handle'), position: "absolute", b: 0, r: 0, width: RESIZE_HANDLE, height: RESIZE_HANDLE, cursor: "se-resize", ...resizeGesture })) : null] }));
176
+ }
177
+ //# sourceMappingURL=desktop-window.js.map