@xenide-io/the-old-ui-theme 0.3.2 → 0.4.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 (60) hide show
  1. package/dist/{chunk-5HSOBJ4F.mjs → chunk-KORWJCIO.mjs} +600 -932
  2. package/dist/{index-BgG8Homu.d.mts → index-B5NOyoKS.d.mts} +100 -100
  3. package/dist/{index-BgG8Homu.d.ts → index-B5NOyoKS.d.ts} +100 -100
  4. package/dist/index.d.mts +9 -145
  5. package/dist/index.d.ts +9 -145
  6. package/dist/index.js +653 -1111
  7. package/dist/index.mjs +9 -267
  8. package/dist/suite.d.mts +760 -0
  9. package/dist/suite.d.ts +760 -0
  10. package/dist/suite.js +2564 -0
  11. package/dist/suite.mjs +2465 -0
  12. package/dist/ui.d.mts +1 -2
  13. package/dist/ui.d.ts +1 -2
  14. package/dist/ui.js +14 -10
  15. package/dist/ui.mjs +1 -1
  16. package/package.json +43 -24
  17. package/src/components/icons/icons.tsx +0 -2
  18. package/src/components/icons/index.ts +0 -3
  19. package/src/components/sections/IconShowcase.tsx +1 -110
  20. package/src/components/sections/NewComponentsShowcase.tsx +4 -4
  21. package/src/components/sections/SidebarShowcase.tsx +3 -3
  22. package/src/components/sections/SuiteShowcase.tsx +669 -0
  23. package/src/components/ui/ComponentDocs.tsx +2 -2
  24. package/src/components/ui/ContextMenu.tsx +1 -1
  25. package/src/components/ui/Menubar.tsx +1 -1
  26. package/src/components/ui/Sidebar.tsx +10 -8
  27. package/src/styles/suite-skin.css +255 -0
  28. package/src/suite/components/ai-panel.tsx +202 -0
  29. package/src/suite/components/app-switcher.tsx +196 -0
  30. package/src/suite/components/command-palette-host.tsx +62 -0
  31. package/src/suite/components/deferred-chrome.tsx +12 -0
  32. package/src/suite/components/suite-app-layout.tsx +69 -0
  33. package/src/suite/components/suite-bottom-nav.tsx +113 -0
  34. package/src/suite/components/suite-layout.tsx +287 -0
  35. package/src/suite/components/suite-mobile-drawer.tsx +150 -0
  36. package/src/suite/components/suite-mobile-header.tsx +71 -0
  37. package/src/suite/components/suite-notification-bell.tsx +227 -0
  38. package/src/suite/components/suite-settings-mobile-nav.tsx +83 -0
  39. package/src/suite/components/suite-sidebar.tsx +191 -0
  40. package/src/suite/components/suite-skeleton.tsx +191 -0
  41. package/src/suite/components/suite-user-menu.tsx +128 -0
  42. package/src/suite/components/theme-provider.tsx +174 -0
  43. package/src/suite/components/today-calibrating.tsx +95 -0
  44. package/src/suite/components/today-page-frame.tsx +28 -0
  45. package/src/suite/components/today-ui.tsx +370 -0
  46. package/src/suite/icons/app-accents.ts +75 -0
  47. package/src/suite/icons/glyph-parts.tsx +67 -0
  48. package/src/suite/icons/glyphs.ts +203 -0
  49. package/src/suite/icons/index.ts +12 -0
  50. package/src/suite/icons/suite-app-icon.tsx +68 -0
  51. package/src/suite/icons/suite-icon.tsx +93 -0
  52. package/src/suite/index.ts +108 -0
  53. package/src/suite/lib/apps.ts +75 -0
  54. package/src/suite/lib/cn.ts +6 -0
  55. package/src/suite/lib/injected.ts +54 -0
  56. package/src/suite/lib/motion.tsx +48 -0
  57. package/src/suite/lib/use-idle-mount.ts +25 -0
  58. package/src/components/icons/lemon-brand-icons.tsx +0 -16
  59. package/src/components/icons/lemon-category-icons.tsx +0 -72
  60. package/src/components/icons/lemon-icons.tsx +0 -57
package/dist/suite.js ADDED
@@ -0,0 +1,2564 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
+ var __getProtoOf = Object.getPrototypeOf;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __spreadValues = (a, b) => {
14
+ for (var prop in b || (b = {}))
15
+ if (__hasOwnProp.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ if (__getOwnPropSymbols)
18
+ for (var prop of __getOwnPropSymbols(b)) {
19
+ if (__propIsEnum.call(b, prop))
20
+ __defNormalProp(a, prop, b[prop]);
21
+ }
22
+ return a;
23
+ };
24
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
+ var __export = (target, all) => {
26
+ for (var name in all)
27
+ __defProp(target, name, { get: all[name], enumerable: true });
28
+ };
29
+ var __copyProps = (to, from, except, desc) => {
30
+ if (from && typeof from === "object" || typeof from === "function") {
31
+ for (let key of __getOwnPropNames(from))
32
+ if (!__hasOwnProp.call(to, key) && key !== except)
33
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
34
+ }
35
+ return to;
36
+ };
37
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
38
+ // If the importer is in node compatibility mode or this is not an ESM
39
+ // file that has been converted to a CommonJS file using a Babel-
40
+ // compatible transform (i.e. "__esModule" has not been set), then set
41
+ // "default" to the CommonJS "module.exports" for node compatibility.
42
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
43
+ mod
44
+ ));
45
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
46
+
47
+ // src/suite/index.ts
48
+ var suite_exports = {};
49
+ __export(suite_exports, {
50
+ APP_ACCENTS: () => APP_ACCENTS,
51
+ APP_GLYPHS: () => APP_GLYPHS,
52
+ AnimatedMoon: () => AnimatedMoon,
53
+ AnimatedSun: () => AnimatedSun,
54
+ AppSwitcher: () => AppSwitcher,
55
+ AppSwitcherChevron: () => AppSwitcherChevron,
56
+ AppSwitcherMark: () => AppSwitcherMark,
57
+ CommandPaletteHost: () => CommandPaletteHost,
58
+ DeferredChrome: () => DeferredChrome,
59
+ SUITE_APPS: () => SUITE_APPS2,
60
+ SUITE_APP_MAP: () => SUITE_APP_MAP,
61
+ SUITE_GLYPHS: () => SUITE_GLYPHS,
62
+ SUITE_ICON_NAMES: () => SUITE_ICON_NAMES,
63
+ SUITE_SPRINGS: () => SUITE_SPRINGS,
64
+ SourceAppGlyph: () => SourceAppGlyph,
65
+ SuiteAiPanel: () => SuiteAiPanel,
66
+ SuiteAppIcon: () => SuiteAppIcon,
67
+ SuiteAppLayout: () => SuiteAppLayout,
68
+ SuiteAppStrip: () => SuiteAppStrip,
69
+ SuiteBottomNav: () => SuiteBottomNav,
70
+ SuiteBreadcrumbs: () => SuiteBreadcrumbs,
71
+ SuiteEmptyState: () => SuiteEmptyState,
72
+ SuiteIcon: () => SuiteIcon,
73
+ SuiteMobileDrawer: () => SuiteMobileDrawer,
74
+ SuiteMobileHeader: () => SuiteMobileHeader,
75
+ SuiteMotionProvider: () => SuiteMotionProvider,
76
+ SuiteNotificationBell: () => SuiteNotificationBell,
77
+ SuitePage: () => SuitePage,
78
+ SuitePageHeader: () => SuitePageHeader,
79
+ SuiteSectionHeader: () => SuiteSectionHeader,
80
+ SuiteSettingsMobileNav: () => SuiteSettingsMobileNav,
81
+ SuiteSidebar: () => SuiteSidebar,
82
+ SuiteSkeleton: () => SuiteSkeleton,
83
+ SuiteSkeletonCard: () => SuiteSkeletonCard,
84
+ SuiteSkeletonList: () => SuiteSkeletonList,
85
+ SuiteTabList: () => SuiteTabList,
86
+ SuiteThemeProvider: () => SuiteThemeProvider,
87
+ SuiteToolbar: () => SuiteToolbar,
88
+ SuiteUserMenu: () => SuiteUserMenu,
89
+ TodayCalibrating: () => TodayCalibrating,
90
+ TodayEmptyState: () => TodayEmptyState,
91
+ TodayHero: () => TodayHero,
92
+ TodayPageFrame: () => TodayPageFrame,
93
+ TodayPanel: () => TodayPanel,
94
+ TodaySection: () => TodaySection,
95
+ TodayTimeIcon: () => TodayTimeIcon,
96
+ TodayTopBar: () => TodayTopBar,
97
+ appSwitcherMarkClass: () => appSwitcherMarkClass,
98
+ appSwitcherMenuItemClass: () => appSwitcherMenuItemClass,
99
+ appSwitcherTriggerClass: () => appSwitcherTriggerClass,
100
+ cn: () => cn,
101
+ getTodayGreeting: () => getTodayGreeting,
102
+ m: () => import_react9.m,
103
+ sourceToSuiteApp: () => sourceToSuiteApp,
104
+ suiteAppBaseUrl: () => suiteAppBaseUrl,
105
+ suiteAppLabel: () => suiteAppLabel,
106
+ useIdleMount: () => useIdleMount,
107
+ useSuiteTheme: () => useSuiteTheme
108
+ });
109
+ module.exports = __toCommonJS(suite_exports);
110
+
111
+ // src/suite/components/today-page-frame.tsx
112
+ var import_jsx_runtime = require("react/jsx-runtime");
113
+ function TodayPageFrame({ children }) {
114
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
115
+ "div",
116
+ {
117
+ "data-test": "today-page",
118
+ className: "today-page-frame relative flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden",
119
+ children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
121
+ "div",
122
+ {
123
+ className: "pointer-events-none absolute inset-x-0 top-0 h-[34rem] overflow-hidden",
124
+ "aria-hidden": true,
125
+ children: [
126
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__glow absolute inset-0" }),
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__sun absolute -right-16 -top-16 h-72 w-72 rounded-full bg-gradient-to-br from-amber-300/30 via-rose-300/20 to-rose-400/10 blur-3xl opacity-60 motion-safe:animate-[pulse_8s_ease-in-out_infinite] motion-reduce:opacity-40" }),
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__top-fade absolute inset-x-0 top-0 h-64" }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--1 absolute -left-24 top-28 h-80 w-[26rem] rounded-[45%]" }),
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--2 absolute -right-20 top-40 h-72 w-80 rounded-[40%]" }),
131
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "today-page-frame__cloud today-page-frame__cloud--3 absolute left-[28%] top-[58%] h-64 w-[22rem] rounded-[50%]" })
132
+ ]
133
+ }
134
+ ),
135
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative flex-1 px-4 pb-10 pt-5 sm:px-6 sm:pt-6 lg:px-8 [&_.list-row]:min-h-[52px] [&_.list-row]:sm:min-h-[44px]", children })
136
+ ]
137
+ }
138
+ );
139
+ }
140
+
141
+ // src/suite/components/today-ui.tsx
142
+ var import_iconoir_react = require("iconoir-react");
143
+
144
+ // src/suite/lib/cn.ts
145
+ var import_clsx = require("clsx");
146
+ var import_tailwind_merge = require("tailwind-merge");
147
+ function cn(...inputs) {
148
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
149
+ }
150
+
151
+ // src/suite/icons/app-accents.ts
152
+ var APP_ACCENTS = {
153
+ turtletime: {
154
+ label: "TurtleTime",
155
+ accent: "#3f8f57",
156
+ tile: "#173521",
157
+ onTile: "#f7ead0",
158
+ onTileAccent: "#f3c24e"
159
+ },
160
+ tides: {
161
+ label: "Tides",
162
+ accent: "#a63bb5",
163
+ tile: "#8f2d96",
164
+ onTile: "#ffffff",
165
+ onTileAccent: "#bfe3ff"
166
+ },
167
+ kraken: {
168
+ label: "Kraken",
169
+ accent: "#737373",
170
+ tile: "#191919",
171
+ onTile: "#ffffff",
172
+ onTileAccent: "#d4d4d4"
173
+ },
174
+ shellstack: {
175
+ label: "ShellStack",
176
+ accent: "#5e5893",
177
+ tile: "#5e5893",
178
+ onTile: "#ffffff",
179
+ onTileAccent: "#c4a7e7"
180
+ },
181
+ crew: {
182
+ label: "Crew",
183
+ accent: "#0891b2",
184
+ tile: "#155e75",
185
+ onTile: "#ffffff",
186
+ onTileAccent: "#a5f3fc"
187
+ }
188
+ };
189
+ var APP_GLYPHS = {
190
+ turtletime: "timer-shell",
191
+ tides: "kanban-wave",
192
+ kraken: "squid-doc",
193
+ shellstack: "stack-hex",
194
+ crew: "crew-bot"
195
+ };
196
+
197
+ // src/suite/icons/glyph-parts.tsx
198
+ var import_jsx_runtime2 = require("react/jsx-runtime");
199
+ function GlyphElement({ element }) {
200
+ const fill = element.filled ? "currentColor" : "none";
201
+ const stroke = element.filled ? "none" : void 0;
202
+ switch (element.kind) {
203
+ case "path":
204
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: element.d, fill, stroke });
205
+ case "circle":
206
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
207
+ "circle",
208
+ {
209
+ cx: element.cx,
210
+ cy: element.cy,
211
+ r: element.r,
212
+ fill,
213
+ stroke
214
+ }
215
+ );
216
+ case "rect":
217
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
218
+ "rect",
219
+ {
220
+ x: element.x,
221
+ y: element.y,
222
+ width: element.width,
223
+ height: element.height,
224
+ rx: element.rx,
225
+ fill,
226
+ stroke
227
+ }
228
+ );
229
+ }
230
+ }
231
+ function GlyphParts({
232
+ elements,
233
+ accentClassName
234
+ }) {
235
+ const base = elements.filter((el) => !el.accent);
236
+ const accented = elements.filter((el) => el.accent);
237
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
238
+ base.map((el, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GlyphElement, { element: el }, i)),
239
+ accented.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
240
+ "g",
241
+ {
242
+ className: accentClassName,
243
+ style: { color: "var(--suite-icon-accent, currentColor)" },
244
+ children: accented.map((el, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GlyphElement, { element: el }, i))
245
+ }
246
+ ) : null
247
+ ] });
248
+ }
249
+
250
+ // src/suite/icons/glyphs.ts
251
+ var SUITE_GLYPHS = {
252
+ "timer-shell": {
253
+ description: "TurtleTime \u2014 turtle-shell dome holding clock hands",
254
+ elements: [
255
+ { kind: "path", d: "M3 17h18" },
256
+ { kind: "path", d: "M6.5 17a5.5 5.5 0 0 1 11 0" },
257
+ { kind: "circle", cx: 4, cy: 15.3, r: 1.5 },
258
+ { kind: "path", d: "M12 17v-3.4", accent: true },
259
+ { kind: "path", d: "M12 17l2.4-1.4", accent: true }
260
+ ]
261
+ },
262
+ "kanban-wave": {
263
+ description: "Tides \u2014 three kanban bars riding a wave",
264
+ elements: [
265
+ { kind: "rect", x: 5.5, y: 8, width: 3, height: 8, rx: 1.5 },
266
+ { kind: "rect", x: 10.5, y: 5.5, width: 3, height: 10.5, rx: 1.5 },
267
+ { kind: "rect", x: 15.5, y: 9.5, width: 3, height: 6.5, rx: 1.5 },
268
+ {
269
+ kind: "path",
270
+ d: "M3 19.5q2.25-2.4 4.5 0t4.5 0t4.5 0t4.5 0",
271
+ accent: true
272
+ }
273
+ ]
274
+ },
275
+ "squid-doc": {
276
+ description: "Kraken \u2014 squid with a pen-nib centre tentacle",
277
+ elements: [
278
+ {
279
+ kind: "path",
280
+ d: "M12 3.2c2.7 2.3 4.2 5.3 4.2 9.3H7.8c0-4 1.5-7 4.2-9.3Z"
281
+ },
282
+ { kind: "circle", cx: 10.1, cy: 9, r: 0.9, filled: true },
283
+ { kind: "circle", cx: 13.9, cy: 9, r: 0.9, filled: true },
284
+ { kind: "path", d: "M9.4 12.4v4.8q0 2.3-2.3 2.3" },
285
+ { kind: "path", d: "M14.6 12.4v4.8q0 2.3 2.3 2.3" },
286
+ { kind: "path", d: "M12 12.4v7.1", accent: true }
287
+ ]
288
+ },
289
+ "stack-hex": {
290
+ description: "ShellStack \u2014 hexagon with the resolving spiral",
291
+ elements: [
292
+ { kind: "path", d: "M18 12l-3 5.2H9l-3-5.2 3-5.2h6Z" },
293
+ { kind: "path", d: "M14.2 9.3H9.8v5.4h4.4v-2.7h-2.2", accent: true }
294
+ ]
295
+ },
296
+ "crew-bot": {
297
+ description: "Crew \u2014 friendly robot head with antenna and smile",
298
+ elements: [
299
+ { kind: "path", d: "M12 7.5V5" },
300
+ { kind: "circle", cx: 12, cy: 4, r: 1, filled: true },
301
+ { kind: "rect", x: 5, y: 8, width: 14, height: 9.5, rx: 3 },
302
+ { kind: "path", d: "M5 11.8H3.4" },
303
+ { kind: "path", d: "M19 11.8h1.6" },
304
+ { kind: "circle", cx: 9.2, cy: 11.8, r: 1, filled: true },
305
+ { kind: "circle", cx: 14.8, cy: 11.8, r: 1, filled: true },
306
+ { kind: "path", d: "M9.6 14.6q2.4 1.7 4.8 0", accent: true }
307
+ ]
308
+ },
309
+ workspace: {
310
+ description: "Workspace container \u2014 panel with header bar and content",
311
+ elements: [
312
+ { kind: "rect", x: 3.5, y: 4.5, width: 17, height: 15, rx: 2.5 },
313
+ { kind: "path", d: "M3.5 8.7h17" },
314
+ { kind: "circle", cx: 6.2, cy: 6.6, r: 0.9, accent: true, filled: true },
315
+ { kind: "path", d: "M6.4 12.2h7" },
316
+ { kind: "path", d: "M6.4 15.4h10.5" }
317
+ ]
318
+ },
319
+ organisation: {
320
+ description: "Organisation chart \u2014 root node with two members",
321
+ elements: [
322
+ { kind: "rect", x: 9.5, y: 3.5, width: 5, height: 5, rx: 1.4, accent: true },
323
+ { kind: "path", d: "M12 8.5v2.5" },
324
+ { kind: "path", d: "M6.75 13.5v-2.5h10.5v2.5" },
325
+ { kind: "rect", x: 4.25, y: 13.5, width: 5, height: 5, rx: 1.4 },
326
+ { kind: "rect", x: 14.75, y: 13.5, width: 5, height: 5, rx: 1.4 }
327
+ ]
328
+ },
329
+ "integration-plug": {
330
+ description: "Integration \u2014 two plug halves meeting under a spark",
331
+ elements: [
332
+ { kind: "rect", x: 3.2, y: 9.8, width: 5.6, height: 4.6, rx: 1.4 },
333
+ { kind: "path", d: "M8.8 11h2.4" },
334
+ { kind: "path", d: "M8.8 13.2h2.4" },
335
+ { kind: "rect", x: 15.2, y: 9.8, width: 5.6, height: 4.6, rx: 1.4 },
336
+ { kind: "path", d: "M15.2 11h-2.4" },
337
+ { kind: "path", d: "M15.2 13.2h-2.4" },
338
+ { kind: "path", d: "M6 14.4v2q0 1.4-1.4 1.4H3.4" },
339
+ { kind: "path", d: "M18 14.4v2q0 1.4 1.4 1.4h1.2" },
340
+ { kind: "path", d: "M12 4.4v2.4", accent: true },
341
+ { kind: "path", d: "M10.4 5l3.2 1.2", accent: true },
342
+ { kind: "path", d: "M13.6 5l-3.2 1.2", accent: true }
343
+ ]
344
+ },
345
+ "suite-grid": {
346
+ description: "Suite app switcher \u2014 four tiles, the active app filled",
347
+ elements: [
348
+ { kind: "rect", x: 4, y: 4, width: 7, height: 7, rx: 2 },
349
+ { kind: "rect", x: 13, y: 4, width: 7, height: 7, rx: 2 },
350
+ { kind: "rect", x: 4, y: 13, width: 7, height: 7, rx: 2 },
351
+ {
352
+ kind: "rect",
353
+ x: 13,
354
+ y: 13,
355
+ width: 7,
356
+ height: 7,
357
+ rx: 2,
358
+ accent: true,
359
+ filled: true
360
+ }
361
+ ]
362
+ },
363
+ "bell-wave": {
364
+ description: "Notifications \u2014 bell with sound waves",
365
+ elements: [
366
+ { kind: "path", d: "M7 9.2a5 5 0 0 1 10 0c0 5.3 2.4 7.3 2.4 7.3H4.6S7 14.5 7 9.2" },
367
+ { kind: "path", d: "M10.7 19.3a1.5 1.5 0 0 0 2.6 0" },
368
+ { kind: "path", d: "M17.9 6.4a5.5 5.5 0 0 1 1.4 3.7", accent: true },
369
+ { kind: "path", d: "M15.7 4a9.4 9.4 0 0 1 2.4 6", accent: true }
370
+ ]
371
+ },
372
+ "today-sun": {
373
+ description: "Today \u2014 sun with slowly turning rays (motion-safe)",
374
+ animatedAccent: true,
375
+ elements: [
376
+ { kind: "circle", cx: 12, cy: 12, r: 4 },
377
+ { kind: "path", d: "M12 2.8v2.4", accent: true },
378
+ { kind: "path", d: "M12 18.8v2.4", accent: true },
379
+ { kind: "path", d: "M2.8 12h2.4", accent: true },
380
+ { kind: "path", d: "M18.8 12h2.4", accent: true },
381
+ { kind: "path", d: "M5.5 5.5l1.7 1.7", accent: true },
382
+ { kind: "path", d: "M16.8 16.8l1.7 1.7", accent: true },
383
+ { kind: "path", d: "M5.5 18.5l1.7-1.7", accent: true },
384
+ { kind: "path", d: "M16.8 7.2l1.7-1.7", accent: true }
385
+ ]
386
+ }
387
+ };
388
+ var SUITE_ICON_NAMES = Object.keys(SUITE_GLYPHS);
389
+
390
+ // src/suite/icons/suite-app-icon.tsx
391
+ var import_jsx_runtime3 = require("react/jsx-runtime");
392
+ var SIZE_PRESETS = { sm: 28, md: 36, lg: 44 };
393
+ function SuiteAppIcon({
394
+ app,
395
+ size = "md",
396
+ label,
397
+ className,
398
+ style
399
+ }) {
400
+ const px = typeof size === "number" ? size : SIZE_PRESETS[size];
401
+ const brand = APP_ACCENTS[app];
402
+ const glyph = SUITE_GLYPHS[APP_GLYPHS[app]];
403
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
404
+ "svg",
405
+ {
406
+ width: px,
407
+ height: px,
408
+ viewBox: "0 0 24 24",
409
+ className: cn("shrink-0", className),
410
+ style,
411
+ role: label ? "img" : void 0,
412
+ "aria-label": label,
413
+ "aria-hidden": label ? void 0 : true,
414
+ children: [
415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "1", y: "1", width: "22", height: "22", rx: "5.5", fill: brand.tile }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
417
+ "g",
418
+ {
419
+ transform: "translate(4.4 4.4) scale(0.6333)",
420
+ fill: "none",
421
+ stroke: "currentColor",
422
+ strokeWidth: 2.7,
423
+ strokeLinecap: "round",
424
+ strokeLinejoin: "round",
425
+ style: {
426
+ color: brand.onTile,
427
+ "--suite-icon-accent": brand.onTileAccent
428
+ },
429
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GlyphParts, { elements: glyph.elements })
430
+ }
431
+ )
432
+ ]
433
+ }
434
+ );
435
+ }
436
+
437
+ // src/suite/icons/suite-icon.tsx
438
+ var import_jsx_runtime4 = require("react/jsx-runtime");
439
+ var TODAY_SUN_STYLE = `
440
+ @media (prefers-reduced-motion: no-preference) {
441
+ .suite-icon-today-sun-rays {
442
+ animation: suite-icon-today-sun-spin 18s linear infinite;
443
+ transform-box: fill-box;
444
+ transform-origin: center;
445
+ }
446
+ }
447
+ @keyframes suite-icon-today-sun-spin {
448
+ to { transform: rotate(360deg); }
449
+ }
450
+ `;
451
+ function SuiteIcon({
452
+ name,
453
+ size = 24,
454
+ accent,
455
+ strokeWidth = 2,
456
+ animated = true,
457
+ label,
458
+ className,
459
+ style
460
+ }) {
461
+ const glyph = SUITE_GLYPHS[name];
462
+ const accentHex = accent ? APP_ACCENTS[accent].accent : void 0;
463
+ const mergedStyle = accentHex ? __spreadProps(__spreadValues({}, style), { "--suite-icon-accent": accentHex }) : style;
464
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
465
+ "svg",
466
+ {
467
+ width: size,
468
+ height: size,
469
+ viewBox: "0 0 24 24",
470
+ fill: "none",
471
+ stroke: "currentColor",
472
+ strokeWidth,
473
+ strokeLinecap: "round",
474
+ strokeLinejoin: "round",
475
+ className: cn("shrink-0", className),
476
+ style: mergedStyle,
477
+ role: label ? "img" : void 0,
478
+ "aria-label": label,
479
+ "aria-hidden": label ? void 0 : true,
480
+ children: [
481
+ glyph.animatedAccent && animated ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: TODAY_SUN_STYLE }) : null,
482
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
483
+ GlyphParts,
484
+ {
485
+ elements: glyph.elements,
486
+ accentClassName: glyph.animatedAccent && animated ? "suite-icon-today-sun-rays" : void 0
487
+ }
488
+ )
489
+ ]
490
+ }
491
+ );
492
+ }
493
+
494
+ // src/suite/components/today-ui.tsx
495
+ var import_jsx_runtime5 = require("react/jsx-runtime");
496
+ var APP_META = {
497
+ shellstack: { name: "ShellStack" },
498
+ tides: { name: "Tides" },
499
+ turtletime: { name: "TurtleTime" },
500
+ kraken: { name: "Kraken" }
501
+ };
502
+ var SUITE_APPS = [
503
+ "shellstack",
504
+ "tides",
505
+ "turtletime",
506
+ "kraken"
507
+ ];
508
+ function sourceToSuiteApp(source) {
509
+ if (source === "tides" || source === "turtletime" || source === "kraken") {
510
+ return source;
511
+ }
512
+ return "shellstack";
513
+ }
514
+ function suiteAppLabel(source) {
515
+ return APP_META[sourceToSuiteApp(source)].name;
516
+ }
517
+ var SOURCE_APP_GLYPH = {
518
+ shellstack: "stack-hex",
519
+ tides: "kanban-wave",
520
+ turtletime: "timer-shell",
521
+ kraken: "squid-doc"
522
+ };
523
+ function SourceAppGlyph({
524
+ source,
525
+ className
526
+ }) {
527
+ const app = sourceToSuiteApp(source);
528
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
529
+ SuiteIcon,
530
+ {
531
+ name: SOURCE_APP_GLYPH[app],
532
+ accent: app,
533
+ className: cn("h-4 w-4 shrink-0 text-ph-subtle", className)
534
+ }
535
+ );
536
+ }
537
+ function formatLongDate(date = /* @__PURE__ */ new Date()) {
538
+ return new Intl.DateTimeFormat(void 0, {
539
+ month: "long",
540
+ day: "numeric"
541
+ }).format(date);
542
+ }
543
+ function SuiteAppStrip({
544
+ className,
545
+ icons
546
+ }) {
547
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
548
+ "div",
549
+ {
550
+ className: cn(
551
+ "flex flex-wrap items-center justify-center gap-2.5",
552
+ className
553
+ ),
554
+ "aria-label": "ShellStack suite apps",
555
+ children: SUITE_APPS.map((app) => {
556
+ var _a;
557
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
558
+ "div",
559
+ {
560
+ className: "flex items-center gap-2 rounded-full border border-ph-border bg-ph-surface px-2.5 py-1.5 shadow-sm",
561
+ children: [
562
+ (_a = icons == null ? void 0 : icons[app]) != null ? _a : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SuiteAppIcon, { app, size: "sm" }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs font-medium text-ph-subtle", children: APP_META[app].name })
564
+ ]
565
+ },
566
+ app
567
+ );
568
+ })
569
+ }
570
+ );
571
+ }
572
+ function AnimatedSun({ className }) {
573
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
574
+ "svg",
575
+ {
576
+ width: "40",
577
+ height: "40",
578
+ viewBox: "0 0 40 40",
579
+ fill: "none",
580
+ xmlns: "http://www.w3.org/2000/svg",
581
+ className: cn(className),
582
+ "aria-hidden": "true",
583
+ children: [
584
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("linearGradient", { id: "animatedSunCenter", x1: "0", y1: "0", x2: "40", y2: "40", children: [
585
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { stopColor: "#f59e0b" }),
586
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "1", stopColor: "#fb923c" })
587
+ ] }) }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("g", { className: "origin-center motion-safe:animate-[spin_20s_linear_infinite] motion-reduce:animate-none", children: [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
589
+ "line",
590
+ {
591
+ x1: "20",
592
+ y1: "4",
593
+ x2: "20",
594
+ y2: "9",
595
+ stroke: "#fbbf24",
596
+ strokeWidth: "2",
597
+ strokeLinecap: "round",
598
+ transform: `rotate(${angle} 20 20)`
599
+ },
600
+ angle
601
+ )) }),
602
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "20", cy: "20", r: "9", fill: "url(#animatedSunCenter)" })
603
+ ]
604
+ }
605
+ );
606
+ }
607
+ function AnimatedMoon({ className }) {
608
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
609
+ "svg",
610
+ {
611
+ width: "40",
612
+ height: "40",
613
+ viewBox: "0 0 40 40",
614
+ fill: "none",
615
+ xmlns: "http://www.w3.org/2000/svg",
616
+ className: cn(className),
617
+ "aria-hidden": "true",
618
+ children: [
619
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("linearGradient", { id: "animatedMoonBody", x1: "0", y1: "0", x2: "40", y2: "40", children: [
620
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { stopColor: "#a5b4fc" }),
621
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "1", stopColor: "#818cf8" })
622
+ ] }) }),
623
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
624
+ "path",
625
+ {
626
+ d: "M27.5 25.5A11 11 0 1 1 14.5 9.6a9 9 0 1 0 13 15.9Z",
627
+ fill: "url(#animatedMoonBody)"
628
+ }
629
+ ),
630
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { className: "motion-safe:animate-[pulse_4s_ease-in-out_infinite] motion-reduce:animate-none", children: [
631
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "29", cy: "10", r: "1.4", fill: "#c7d2fe" }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "34", cy: "16", r: "1", fill: "#c7d2fe" }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "25", cy: "5.5", r: "0.9", fill: "#c7d2fe" })
634
+ ] })
635
+ ]
636
+ }
637
+ );
638
+ }
639
+ function TodayTimeIcon({ className }) {
640
+ const hour = (/* @__PURE__ */ new Date()).getHours();
641
+ const isNight = hour >= 18 || hour < 6;
642
+ return isNight ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AnimatedMoon, { className }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AnimatedSun, { className });
643
+ }
644
+ function TodayTopBar({
645
+ refreshing,
646
+ loading,
647
+ onRefresh,
648
+ appIcon,
649
+ appName
650
+ }) {
651
+ const icon = appIcon != null ? appIcon : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TodayTimeIcon, { className: "h-6 w-6 sm:h-7 sm:w-7 text-amber-500" });
652
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "flex items-center justify-between gap-4", children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 items-center gap-3", children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hidden shrink-0 sm:block", "aria-hidden": "true", children: icon }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
656
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: "font-display text-[1.625rem] font-bold leading-tight tracking-tight text-ph-ink sm:text-[2.25rem]", children: "Today" }),
657
+ appName ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: appName }) : null,
658
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-sm text-ph-subtle", children: formatLongDate() })
659
+ ] })
660
+ ] }),
661
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex shrink-0 items-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
662
+ "button",
663
+ {
664
+ type: "button",
665
+ onClick: onRefresh,
666
+ disabled: refreshing || loading,
667
+ className: "press-subtle inline-flex h-10 w-10 items-center justify-center rounded-lg text-ph-subtle transition hover:bg-ph-surface hover:text-ph-ink disabled:opacity-50",
668
+ "data-test": "today-refresh",
669
+ "aria-label": "Refresh today",
670
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
671
+ import_iconoir_react.Refresh,
672
+ {
673
+ className: cn("h-4 w-4", refreshing && "animate-spin"),
674
+ "aria-hidden": true
675
+ }
676
+ )
677
+ }
678
+ ) })
679
+ ] });
680
+ }
681
+ function getTodayGreeting(user, stats) {
682
+ var _a, _b, _c, _d, _e;
683
+ const hour = (/* @__PURE__ */ new Date()).getHours();
684
+ let period = "morning";
685
+ if (hour >= 12 && hour < 17) period = "afternoon";
686
+ else if (hour >= 17 && hour < 21) period = "evening";
687
+ else if (hour >= 21 || hour < 5) period = "night";
688
+ const name = (user == null ? void 0 : user.first_name) || ((_a = user == null ? void 0 : user.name) == null ? void 0 : _a.split(" ")[0]) || ((_b = user == null ? void 0 : user.email) == null ? void 0 : _b.split("@")[0]) || null;
689
+ let greeting = `Good ${period}`;
690
+ if (name) greeting += `, ${name}`;
691
+ const taskCount = (_c = stats == null ? void 0 : stats.taskCount) != null ? _c : 0;
692
+ const overdueCount = (_d = stats == null ? void 0 : stats.overdueCount) != null ? _d : 0;
693
+ const hours = (_e = stats == null ? void 0 : stats.hours) != null ? _e : 0;
694
+ if (overdueCount > 0) {
695
+ return `${greeting}. You have ${taskCount} task${taskCount === 1 ? "" : "s"} due today, ${overdueCount} overdue.`;
696
+ }
697
+ if (taskCount > 0) {
698
+ return `${greeting}. You have ${taskCount} task${taskCount === 1 ? "" : "s"} due today.`;
699
+ }
700
+ if (hours > 0) {
701
+ return `${greeting}. You logged ${hours} hour${hours === 1 ? "" : "s"} today.`;
702
+ }
703
+ if (period === "evening" || period === "night") {
704
+ return `${greeting}. Time to wrap up.`;
705
+ }
706
+ return `${greeting}. Nothing overdue \u2014 take a nice, deep breath.`;
707
+ }
708
+ function TodayHero({
709
+ message = "Take a nice, deep breath."
710
+ }) {
711
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-8 text-center sm:mt-10", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
712
+ "p",
713
+ {
714
+ className: "today-hero font-display text-2xl font-bold tracking-tight text-ph-ink sm:text-[1.75rem] sm:leading-tight",
715
+ children: message
716
+ },
717
+ typeof message === "string" ? message : "hero"
718
+ ) });
719
+ }
720
+ function TodaySection({
721
+ icon: Icon,
722
+ iconClassName,
723
+ title,
724
+ description,
725
+ action,
726
+ testId,
727
+ children
728
+ }) {
729
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { "data-test": testId, className: "space-y-4", children: [
730
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-start justify-between gap-4", children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 items-start gap-3.5", children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
733
+ "span",
734
+ {
735
+ className: cn(
736
+ "inline-flex h-10 w-10 sm:h-11 sm:w-11 shrink-0 items-center justify-center rounded-2xl shadow-sm ring-1 ring-black/[0.06]",
737
+ iconClassName != null ? iconClassName : "bg-ph-muted text-ph-ink"
738
+ ),
739
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { className: "h-5 w-5", "aria-hidden": true })
740
+ }
741
+ ),
742
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-w-0 pt-0.5", children: [
743
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "font-display text-lg font-bold tracking-tight text-ph-ink", children: title }),
744
+ description ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-1 max-w-xl text-sm leading-relaxed text-ph-subtle", children: description }) : null
745
+ ] })
746
+ ] }),
747
+ action
748
+ ] }),
749
+ children
750
+ ] });
751
+ }
752
+ function TodayPanel({
753
+ children,
754
+ className
755
+ }) {
756
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
757
+ "div",
758
+ {
759
+ className: cn(
760
+ "border-ph-border/70 rounded-2xl border bg-ph-surface p-6 shadow-[0_2px_28px_-10px_rgba(15,23,42,0.14)] transition-all duration-200 ease-out sm:p-6",
761
+ "hover:shadow-[0_8px_32px_-12px_rgba(15,23,42,0.18)] hover:-translate-y-0.5 motion-reduce:transition-none motion-reduce:hover:transform-none",
762
+ className
763
+ ),
764
+ children
765
+ }
766
+ );
767
+ }
768
+ function TodayEmptyState({
769
+ title,
770
+ description
771
+ }) {
772
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "border-ph-border/80 rounded-xl border border-dashed px-5 py-8 text-center", children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-semibold text-ph-ink", children: title }),
774
+ description ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-1.5 text-sm leading-relaxed text-ph-subtle", children: description }) : null
775
+ ] });
776
+ }
777
+
778
+ // src/suite/components/app-switcher.tsx
779
+ var import_image = __toESM(require("next/image"));
780
+ var import_react = require("react");
781
+ var import_iconoir_react2 = require("iconoir-react");
782
+ var import_jsx_runtime6 = require("react/jsx-runtime");
783
+ function appSwitcherTriggerClass(open, collapsed = false) {
784
+ return cn(
785
+ "group flex min-w-0 items-center gap-2 rounded-lg border px-2 py-1.5 text-left transition-all duration-200",
786
+ collapsed ? "h-11 w-11 justify-center px-0 py-0" : "w-full",
787
+ open ? "border-ph-brand/30 bg-ph-muted shadow-[0_1px_2px_rgba(0,0,0,0.06)] ring-1 ring-ph-brand/15" : "border-transparent hover:border-ph-border hover:bg-ph-muted/80 hover:shadow-sm"
788
+ );
789
+ }
790
+ function appSwitcherMarkClass() {
791
+ return "h-8 w-8 shrink-0 rounded-lg shadow-[0_1px_2px_rgba(0,0,0,0.1),0_4px_12px_rgba(0,0,0,0.08)] ring-1 ring-black/[0.06]";
792
+ }
793
+ function appSwitcherMenuItemClass() {
794
+ return "rounded-lg px-2 py-2 data-[highlighted]:bg-transparent hover:bg-ph-muted/80";
795
+ }
796
+ function AppSwitcherMark({
797
+ children,
798
+ collapsed
799
+ }) {
800
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
801
+ "span",
802
+ {
803
+ className: cn(
804
+ "relative shrink-0",
805
+ collapsed && "group-hover:scale-[1.02]"
806
+ ),
807
+ children
808
+ }
809
+ );
810
+ }
811
+ function AppSwitcherChevron({
812
+ open,
813
+ collapsed = false
814
+ }) {
815
+ if (collapsed) {
816
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
817
+ "span",
818
+ {
819
+ className: cn(
820
+ "absolute bottom-0 right-0 flex h-3 w-3 items-center justify-center rounded-full border border-ph-border bg-ph-surface shadow-sm transition-colors",
821
+ open && "border-ph-brand/40 bg-ph-brand/10"
822
+ ),
823
+ "aria-hidden": true,
824
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
825
+ import_iconoir_react2.NavArrowDown,
826
+ {
827
+ className: cn(
828
+ "h-2 w-2 text-ph-mutedtext transition-transform duration-200",
829
+ open && "rotate-180 text-ph-brand"
830
+ )
831
+ }
832
+ )
833
+ }
834
+ );
835
+ }
836
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
837
+ "span",
838
+ {
839
+ className: cn(
840
+ "border-ph-border/80 flex h-5 w-5 shrink-0 items-center justify-center rounded-lg border bg-ph-canvas shadow-sm transition-all duration-200",
841
+ open ? "border-ph-brand/35 bg-ph-brand/10 text-ph-brand" : "text-ph-mutedtext group-hover:border-ph-border group-hover:bg-ph-surface"
842
+ ),
843
+ "aria-hidden": true,
844
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
845
+ import_iconoir_react2.NavArrowDown,
846
+ {
847
+ className: cn(
848
+ "h-3 w-3 transition-transform duration-200",
849
+ open && "rotate-180"
850
+ )
851
+ }
852
+ )
853
+ }
854
+ );
855
+ }
856
+ function AppSwitcher({
857
+ apps,
858
+ currentApp,
859
+ onSelect,
860
+ mark,
861
+ title,
862
+ collapsed = false,
863
+ dropdownMenu: DropdownMenu,
864
+ dropdownItem: DropdownItem
865
+ }) {
866
+ const [open, setOpen] = (0, import_react.useState)(false);
867
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
868
+ DropdownMenu,
869
+ {
870
+ "aria-label": "Switch application",
871
+ className: collapsed ? "w-11 shrink-0" : "min-w-0 flex-1",
872
+ panelClassName: "w-64",
873
+ align: "start",
874
+ open,
875
+ onOpenChange: setOpen,
876
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
877
+ "span",
878
+ {
879
+ className: appSwitcherTriggerClass(open, collapsed),
880
+ "data-test": "app-switcher-trigger",
881
+ "aria-expanded": open,
882
+ children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(AppSwitcherMark, { collapsed, children: [
884
+ mark,
885
+ collapsed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AppSwitcherChevron, { open, collapsed: true }) : null
886
+ ] }),
887
+ !collapsed ? title : null,
888
+ !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AppSwitcherChevron, { open }) : null
889
+ ]
890
+ }
891
+ ),
892
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-1", "data-test": "app-switcher-menu", children: [
893
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-ph-mutedtext", children: "Switch application" }),
894
+ apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
895
+ DropdownItem,
896
+ {
897
+ "data-test": `switch-app-${app.slug}`,
898
+ onMouseDown: (e) => e.preventDefault(),
899
+ onClick: () => {
900
+ setOpen(false);
901
+ onSelect(app);
902
+ },
903
+ className: appSwitcherMenuItemClass(),
904
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex min-w-0 items-center gap-3", children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
906
+ import_image.default,
907
+ {
908
+ src: app.icon,
909
+ alt: "",
910
+ width: 28,
911
+ height: 28,
912
+ className: "h-7 w-7 shrink-0 rounded-md object-contain"
913
+ }
914
+ ),
915
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "min-w-0 flex-1", children: [
916
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block truncate text-sm font-medium text-ph-ink", children: app.name }),
917
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block truncate text-xs text-ph-mutedtext", children: app.description })
918
+ ] }),
919
+ app.slug === currentApp ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
920
+ import_iconoir_react2.Check,
921
+ {
922
+ className: "h-4 w-4 shrink-0 text-ph-brand",
923
+ "aria-hidden": "true"
924
+ }
925
+ ) : null
926
+ ] })
927
+ },
928
+ app.slug
929
+ ))
930
+ ] })
931
+ }
932
+ );
933
+ }
934
+
935
+ // src/suite/components/suite-notification-bell.tsx
936
+ var import_react2 = require("react");
937
+ var import_navigation = require("next/navigation");
938
+ var import_iconoir_react3 = require("iconoir-react");
939
+ var import_jsx_runtime7 = require("react/jsx-runtime");
940
+ var POLL_MS = 6e4;
941
+ function timeAgo(iso) {
942
+ const then = new Date(iso).getTime();
943
+ if (Number.isNaN(then)) return "";
944
+ const secs = Math.max(0, Math.round((Date.now() - then) / 1e3));
945
+ if (secs < 60) return "just now";
946
+ const mins = Math.round(secs / 60);
947
+ if (mins < 60) return `${mins}m ago`;
948
+ const hrs = Math.round(mins / 60);
949
+ if (hrs < 24) return `${hrs}h ago`;
950
+ return `${Math.round(hrs / 24)}d ago`;
951
+ }
952
+ function SuiteNotificationBell({
953
+ fetchNotifications,
954
+ markRead,
955
+ markAllRead,
956
+ dropdownMenu: DropdownMenu,
957
+ cacheKey = "suite-notifications-cache"
958
+ }) {
959
+ const router = (0, import_navigation.useRouter)();
960
+ const [open, setOpen] = (0, import_react2.useState)(false);
961
+ const [items, setItems] = (0, import_react2.useState)([]);
962
+ const [unread, setUnread] = (0, import_react2.useState)(0);
963
+ const load = (0, import_react2.useCallback)(async () => {
964
+ try {
965
+ const data = await fetchNotifications();
966
+ setItems(data.notifications);
967
+ setUnread(data.unread_count);
968
+ if (cacheKey) {
969
+ try {
970
+ sessionStorage.setItem(
971
+ cacheKey,
972
+ JSON.stringify({
973
+ notifications: data.notifications,
974
+ unread_count: data.unread_count
975
+ })
976
+ );
977
+ } catch (e) {
978
+ }
979
+ }
980
+ } catch (e) {
981
+ }
982
+ }, [fetchNotifications, cacheKey]);
983
+ (0, import_react2.useEffect)(() => {
984
+ if (cacheKey) {
985
+ queueMicrotask(() => {
986
+ var _a;
987
+ try {
988
+ const raw = sessionStorage.getItem(cacheKey);
989
+ if (raw) {
990
+ const cached = JSON.parse(raw);
991
+ if (Array.isArray(cached.notifications)) {
992
+ setItems(cached.notifications);
993
+ setUnread((_a = cached.unread_count) != null ? _a : 0);
994
+ }
995
+ }
996
+ } catch (e) {
997
+ }
998
+ });
999
+ }
1000
+ const kick = window.setTimeout(() => void load(), 0);
1001
+ const id = window.setInterval(() => void load(), POLL_MS);
1002
+ return () => {
1003
+ window.clearTimeout(kick);
1004
+ window.clearInterval(id);
1005
+ };
1006
+ }, [load, cacheKey]);
1007
+ const onOpenChange = (0, import_react2.useCallback)(
1008
+ (next) => {
1009
+ setOpen(next);
1010
+ if (next) void load();
1011
+ },
1012
+ [load]
1013
+ );
1014
+ const openItem = (0, import_react2.useCallback)(
1015
+ async (n) => {
1016
+ setOpen(false);
1017
+ if (!n.read_at) {
1018
+ setItems(
1019
+ (prev) => prev.map(
1020
+ (i) => i.id === n.id ? __spreadProps(__spreadValues({}, i), { read_at: (/* @__PURE__ */ new Date()).toISOString() }) : i
1021
+ )
1022
+ );
1023
+ setUnread((u) => Math.max(0, u - 1));
1024
+ try {
1025
+ await markRead(n.id);
1026
+ } catch (e) {
1027
+ }
1028
+ }
1029
+ if (!n.href) return;
1030
+ if (/^https?:\/\//i.test(n.href)) window.location.assign(n.href);
1031
+ else router.push(n.href);
1032
+ },
1033
+ [router, markRead]
1034
+ );
1035
+ const markAll = (0, import_react2.useCallback)(async () => {
1036
+ setItems(
1037
+ (prev) => prev.map((i) => {
1038
+ var _a;
1039
+ return __spreadProps(__spreadValues({}, i), {
1040
+ read_at: (_a = i.read_at) != null ? _a : (/* @__PURE__ */ new Date()).toISOString()
1041
+ });
1042
+ })
1043
+ );
1044
+ setUnread(0);
1045
+ try {
1046
+ await markAllRead();
1047
+ } catch (e) {
1048
+ }
1049
+ }, [markAllRead]);
1050
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1051
+ DropdownMenu,
1052
+ {
1053
+ "aria-label": "Notifications",
1054
+ align: "end",
1055
+ panelClassName: "w-80 overflow-hidden p-0",
1056
+ open,
1057
+ onOpenChange,
1058
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "relative inline-flex h-9 w-9 items-center justify-center rounded-full text-ph-mutedtext transition hover:bg-ph-muted hover:text-ph-ink", children: [
1059
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_iconoir_react3.Bell, { className: "h-5 w-5", strokeWidth: 1.75, "aria-hidden": true }),
1060
+ unread > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-ph-brand px-1 text-[10px] font-semibold leading-none text-white ring-2 ring-ph-surface", children: unread > 9 ? "9+" : unread }) : null
1061
+ ] }),
1062
+ children: [
1063
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between border-b border-ph-border px-3 py-2", children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm font-semibold text-ph-ink", children: "Notifications" }),
1065
+ unread > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1066
+ "button",
1067
+ {
1068
+ type: "button",
1069
+ onClick: markAll,
1070
+ className: "text-xs font-medium text-ph-brand hover:underline",
1071
+ children: "Mark all read"
1072
+ }
1073
+ ) : null
1074
+ ] }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "max-h-80 overflow-y-auto py-1", children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "px-3 py-6 text-center text-sm text-ph-mutedtext", children: "You're all caught up." }) : items.map((n) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1076
+ "button",
1077
+ {
1078
+ type: "button",
1079
+ onClick: () => void openItem(n),
1080
+ className: "flex w-full flex-col gap-0.5 px-3 py-2 text-left transition hover:bg-ph-muted",
1081
+ children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "flex items-center gap-2", children: [
1083
+ !n.read_at ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1084
+ "span",
1085
+ {
1086
+ className: "h-1.5 w-1.5 shrink-0 rounded-full bg-ph-brand",
1087
+ "aria-hidden": true
1088
+ }
1089
+ ) : null,
1090
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate text-sm font-medium text-ph-ink", children: n.title })
1091
+ ] }),
1092
+ n.body ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "line-clamp-2 text-xs text-ph-mutedtext", children: n.body }) : null,
1093
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-[11px] uppercase tracking-wide text-ph-subtle", children: [
1094
+ n.source_app,
1095
+ " \xB7 ",
1096
+ timeAgo(n.created_at)
1097
+ ] })
1098
+ ]
1099
+ },
1100
+ n.id
1101
+ )) })
1102
+ ]
1103
+ }
1104
+ );
1105
+ }
1106
+
1107
+ // src/suite/components/command-palette-host.tsx
1108
+ var import_react3 = require("react");
1109
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1110
+ function isEditableTarget() {
1111
+ if (typeof document === "undefined") return false;
1112
+ const el = document.activeElement;
1113
+ return el instanceof HTMLElement && Boolean(
1114
+ el.closest(
1115
+ "input, textarea, select, [contenteditable='true'], [role='textbox']"
1116
+ )
1117
+ );
1118
+ }
1119
+ function CommandPaletteHost({
1120
+ items,
1121
+ isAuthenticated = true,
1122
+ placeholder = "Type a command or search\u2026",
1123
+ commandPalette: CommandPalette
1124
+ }) {
1125
+ const [open, setOpen] = (0, import_react3.useState)(false);
1126
+ (0, import_react3.useEffect)(() => {
1127
+ const onKey = (e) => {
1128
+ if (!((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k")) return;
1129
+ if (!open && isEditableTarget()) return;
1130
+ e.preventDefault();
1131
+ setOpen((o) => !o);
1132
+ };
1133
+ window.addEventListener("keydown", onKey);
1134
+ return () => window.removeEventListener("keydown", onKey);
1135
+ }, [open]);
1136
+ if (!isAuthenticated) return null;
1137
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1138
+ CommandPalette,
1139
+ {
1140
+ items,
1141
+ isOpen: open,
1142
+ onClose: () => setOpen(false),
1143
+ placeholder
1144
+ }
1145
+ );
1146
+ }
1147
+
1148
+ // src/suite/lib/use-idle-mount.ts
1149
+ var import_react4 = require("react");
1150
+ function useIdleMount(timeoutMs = 1200) {
1151
+ const [ready, setReady] = (0, import_react4.useState)(false);
1152
+ (0, import_react4.useEffect)(() => {
1153
+ let timeoutId = 0;
1154
+ let idleId = 0;
1155
+ const activate = () => setReady(true);
1156
+ if (typeof window.requestIdleCallback === "function") {
1157
+ idleId = window.requestIdleCallback(activate, { timeout: timeoutMs });
1158
+ return () => window.cancelIdleCallback(idleId);
1159
+ }
1160
+ timeoutId = window.setTimeout(activate, Math.min(timeoutMs, 200));
1161
+ return () => window.clearTimeout(timeoutId);
1162
+ }, [timeoutMs]);
1163
+ return ready;
1164
+ }
1165
+
1166
+ // src/suite/components/deferred-chrome.tsx
1167
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1168
+ function DeferredChrome({ children }) {
1169
+ const ready = useIdleMount();
1170
+ if (!ready) return null;
1171
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
1172
+ }
1173
+
1174
+ // src/suite/components/suite-mobile-drawer.tsx
1175
+ var import_react5 = require("react");
1176
+ var import_iconoir_react4 = require("iconoir-react");
1177
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1178
+ function SuiteMobileDrawer({
1179
+ open,
1180
+ onClose,
1181
+ children,
1182
+ side = "left",
1183
+ showCloseButton = false,
1184
+ closeLabel = "Close navigation",
1185
+ backdropLabel = "Close navigation",
1186
+ dialogLabel = "Navigation",
1187
+ durationMs = 200,
1188
+ dataTest,
1189
+ panelDataTest,
1190
+ panelClassName
1191
+ }) {
1192
+ const [rendered, setRendered] = (0, import_react5.useState)(open);
1193
+ const [shown, setShown] = (0, import_react5.useState)(open);
1194
+ const panelRef = (0, import_react5.useRef)(null);
1195
+ const onCloseRef = (0, import_react5.useRef)(onClose);
1196
+ (0, import_react5.useEffect)(() => {
1197
+ onCloseRef.current = onClose;
1198
+ }, [onClose]);
1199
+ (0, import_react5.useEffect)(() => {
1200
+ if (open) {
1201
+ queueMicrotask(() => setRendered(true));
1202
+ let inner = 0;
1203
+ const outer = requestAnimationFrame(() => {
1204
+ inner = requestAnimationFrame(() => setShown(true));
1205
+ });
1206
+ return () => {
1207
+ cancelAnimationFrame(outer);
1208
+ cancelAnimationFrame(inner);
1209
+ };
1210
+ }
1211
+ queueMicrotask(() => setShown(false));
1212
+ const timeout = window.setTimeout(() => setRendered(false), durationMs);
1213
+ return () => window.clearTimeout(timeout);
1214
+ }, [open, durationMs]);
1215
+ (0, import_react5.useEffect)(() => {
1216
+ if (!open) return;
1217
+ const onKeyDown = (event) => {
1218
+ if (event.key === "Escape") onCloseRef.current();
1219
+ };
1220
+ window.addEventListener("keydown", onKeyDown);
1221
+ const previousOverflow = document.body.style.overflow;
1222
+ document.body.style.overflow = "hidden";
1223
+ const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
1224
+ const frame = requestAnimationFrame(() => {
1225
+ var _a;
1226
+ return (_a = panelRef.current) == null ? void 0 : _a.focus();
1227
+ });
1228
+ return () => {
1229
+ window.removeEventListener("keydown", onKeyDown);
1230
+ document.body.style.overflow = previousOverflow;
1231
+ cancelAnimationFrame(frame);
1232
+ if (previouslyFocused == null ? void 0 : previouslyFocused.isConnected) previouslyFocused.focus();
1233
+ };
1234
+ }, [open]);
1235
+ if (!rendered) return null;
1236
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { "data-test": dataTest, className: "no-print fixed inset-0 z-50 lg:hidden", children: [
1237
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1238
+ "button",
1239
+ {
1240
+ type: "button",
1241
+ tabIndex: -1,
1242
+ className: cn(
1243
+ "absolute inset-0 bg-black/25 backdrop-blur-sm motion-safe:transition-opacity motion-safe:ease-spring-subtle",
1244
+ shown ? "opacity-100" : "opacity-0"
1245
+ ),
1246
+ style: { transitionDuration: `${durationMs}ms` },
1247
+ onClick: onClose,
1248
+ "aria-label": backdropLabel
1249
+ }
1250
+ ),
1251
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1252
+ "aside",
1253
+ {
1254
+ ref: panelRef,
1255
+ role: "dialog",
1256
+ "aria-modal": "true",
1257
+ "aria-label": dialogLabel,
1258
+ tabIndex: -1,
1259
+ "data-test": panelDataTest,
1260
+ className: cn(
1261
+ "suite-scroll-lock relative flex h-full w-[86%] max-w-64 flex-col overflow-hidden bg-ph-surface shadow-xl outline-none motion-safe:transition-transform motion-safe:ease-spring-fast",
1262
+ side === "right" && "ml-auto",
1263
+ shown ? "translate-x-0" : side === "right" ? "translate-x-full" : "-translate-x-full",
1264
+ panelClassName
1265
+ ),
1266
+ style: { transitionDuration: `${durationMs}ms` },
1267
+ children: [
1268
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex h-14 shrink-0 items-center justify-end border-b border-ph-border px-3", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1269
+ "button",
1270
+ {
1271
+ type: "button",
1272
+ onClick: onClose,
1273
+ className: "-mr-1.5 inline-flex h-11 w-11 items-center justify-center rounded-lg text-ph-mutedtext transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1274
+ "aria-label": closeLabel,
1275
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_iconoir_react4.Xmark, { className: "h-4 w-4", "aria-hidden": true })
1276
+ }
1277
+ ) }) : null,
1278
+ children
1279
+ ]
1280
+ }
1281
+ )
1282
+ ] });
1283
+ }
1284
+
1285
+ // src/suite/components/suite-mobile-header.tsx
1286
+ var import_iconoir_react5 = require("iconoir-react");
1287
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1288
+ function SuiteMobileHeader({
1289
+ onMenuClick,
1290
+ menuIcon: MenuIcon = import_iconoir_react5.Menu,
1291
+ title,
1292
+ actions,
1293
+ menuLabel = "Open navigation",
1294
+ menuDataTest,
1295
+ dataTest,
1296
+ className
1297
+ }) {
1298
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1299
+ "header",
1300
+ {
1301
+ "data-test": dataTest,
1302
+ className: cn(
1303
+ "suite-app-bar no-print sticky top-0 z-30 flex min-h-[56px] shrink-0 items-center gap-1 border-b border-ph-border px-2 lg:hidden",
1304
+ className
1305
+ ),
1306
+ children: [
1307
+ onMenuClick ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1308
+ "button",
1309
+ {
1310
+ type: "button",
1311
+ "data-test": menuDataTest,
1312
+ className: "suite-press inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl text-ph-ink transition-colors hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1313
+ onClick: onMenuClick,
1314
+ "aria-label": menuLabel,
1315
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MenuIcon, { className: "h-5 w-5", "aria-hidden": true })
1316
+ }
1317
+ ) : null,
1318
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1319
+ "div",
1320
+ {
1321
+ className: cn(
1322
+ "flex min-w-0 flex-1 items-center",
1323
+ !onMenuClick && "pl-2"
1324
+ ),
1325
+ children: title
1326
+ }
1327
+ ),
1328
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex shrink-0 items-center gap-1", children: actions }) : null
1329
+ ]
1330
+ }
1331
+ );
1332
+ }
1333
+
1334
+ // src/suite/components/suite-user-menu.tsx
1335
+ var import_image2 = __toESM(require("next/image"));
1336
+ var import_link = __toESM(require("next/link"));
1337
+ var import_react6 = require("react");
1338
+ var import_iconoir_react6 = require("iconoir-react");
1339
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1340
+ function computeInitials(name, email) {
1341
+ const source = (name || email || "?").trim();
1342
+ return source.split(/[\s@]+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
1343
+ }
1344
+ function SuiteUserMenu({
1345
+ name,
1346
+ email,
1347
+ image,
1348
+ settingsHref,
1349
+ onSignOut,
1350
+ fallbackInitials,
1351
+ dataTest = "suite-user-menu",
1352
+ className
1353
+ }) {
1354
+ const [open, setOpen] = (0, import_react6.useState)(false);
1355
+ const initials = fallbackInitials != null ? fallbackInitials : computeInitials(name, email);
1356
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: cn("relative", className), "data-test": dataTest, children: [
1357
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1358
+ "button",
1359
+ {
1360
+ type: "button",
1361
+ "aria-label": "Account menu",
1362
+ "aria-expanded": open,
1363
+ "aria-haspopup": "menu",
1364
+ title: "Account",
1365
+ onClick: () => setOpen((value) => !value),
1366
+ className: "suite-press inline-flex h-11 w-11 items-center justify-center rounded-full transition-colors hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1367
+ children: image ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1368
+ import_image2.default,
1369
+ {
1370
+ src: image,
1371
+ alt: "",
1372
+ width: 32,
1373
+ height: 32,
1374
+ className: "h-8 w-8 rounded-full object-cover",
1375
+ unoptimized: true
1376
+ }
1377
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-ph-muted text-sm font-semibold text-ph-brand", children: initials })
1378
+ }
1379
+ ),
1380
+ open ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1382
+ "button",
1383
+ {
1384
+ type: "button",
1385
+ "aria-label": "Close account menu",
1386
+ className: "fixed inset-0 z-40 cursor-default",
1387
+ onClick: () => setOpen(false)
1388
+ }
1389
+ ),
1390
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1391
+ "div",
1392
+ {
1393
+ role: "menu",
1394
+ className: "absolute right-0 top-full z-50 mt-1 w-56 rounded-xl border border-ph-border bg-ph-surface p-1 shadow-xl",
1395
+ children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "px-2 py-1.5", children: [
1397
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "truncate text-sm font-medium text-ph-ink", children: name || email || "Account" }),
1398
+ email ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "truncate text-xs text-ph-mutedtext", children: email }) : null
1399
+ ] }),
1400
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "my-1 border-t border-ph-border" }),
1401
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1402
+ import_link.default,
1403
+ {
1404
+ role: "menuitem",
1405
+ href: settingsHref,
1406
+ "data-test": `${dataTest}-settings`,
1407
+ className: "flex min-h-11 items-center gap-2 rounded-lg px-2 text-sm text-ph-ink transition-colors hover:bg-ph-muted",
1408
+ onClick: () => setOpen(false),
1409
+ children: [
1410
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_iconoir_react6.Settings, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1411
+ "Profile settings"
1412
+ ]
1413
+ }
1414
+ ),
1415
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1416
+ "button",
1417
+ {
1418
+ role: "menuitem",
1419
+ type: "button",
1420
+ "data-test": `${dataTest}-sign-out`,
1421
+ className: "flex min-h-11 w-full items-center gap-2 rounded-lg px-2 text-left text-sm text-ph-ink transition-colors hover:bg-ph-muted",
1422
+ onClick: () => {
1423
+ setOpen(false);
1424
+ onSignOut();
1425
+ },
1426
+ children: [
1427
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_iconoir_react6.LogOut, { className: "h-4 w-4 text-ph-mutedtext", "aria-hidden": true }),
1428
+ "Sign out"
1429
+ ]
1430
+ }
1431
+ )
1432
+ ]
1433
+ }
1434
+ )
1435
+ ] }) : null
1436
+ ] });
1437
+ }
1438
+
1439
+ // src/suite/components/suite-bottom-nav.tsx
1440
+ var import_link2 = __toESM(require("next/link"));
1441
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1442
+ function SuiteBottomNav({
1443
+ items,
1444
+ ariaLabel = "Primary navigation",
1445
+ dataTest = "suite-bottom-nav",
1446
+ className
1447
+ }) {
1448
+ if (process.env.NODE_ENV !== "production" && items.length > 5) {
1449
+ console.warn(
1450
+ `SuiteBottomNav: ${items.length} items \u2014 bottom nav should have at most 5 (Hick's Law).`
1451
+ );
1452
+ }
1453
+ const activeIndex = items.findIndex((item) => item.active);
1454
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1455
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1456
+ "div",
1457
+ {
1458
+ "aria-hidden": true,
1459
+ className: "shrink-0 lg:hidden",
1460
+ style: { height: "calc(60px + env(safe-area-inset-bottom))" }
1461
+ }
1462
+ ),
1463
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1464
+ "nav",
1465
+ {
1466
+ "aria-label": ariaLabel,
1467
+ "data-test": dataTest,
1468
+ className: cn(
1469
+ "suite-bottom-nav no-print z-40 grid auto-cols-fr grid-flow-col lg:hidden",
1470
+ className
1471
+ ),
1472
+ style: {
1473
+ gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))`,
1474
+ "--suite-nav-items": items.length,
1475
+ "--suite-nav-active": activeIndex >= 0 ? activeIndex : 0
1476
+ },
1477
+ children: [
1478
+ activeIndex >= 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { "aria-hidden": true, className: "suite-bottom-nav__indicator" }) : null,
1479
+ items.map((item) => {
1480
+ var _a;
1481
+ const Icon = item.icon;
1482
+ const active = Boolean(item.active);
1483
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1484
+ import_link2.default,
1485
+ {
1486
+ href: item.href,
1487
+ "data-test": (_a = item.dataTest) != null ? _a : `suite-nav-${item.label.toLowerCase().replace(/\s+/g, "-")}`,
1488
+ "aria-current": active ? "page" : void 0,
1489
+ className: cn(
1490
+ "suite-bottom-nav__item relative flex min-h-[60px] min-w-0 flex-col items-center justify-center gap-1 px-1 pb-1.5 pt-2 text-[10px] font-medium leading-none text-ph-mutedtext transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ph-brand motion-reduce:transition-none",
1491
+ active && "font-semibold text-ph-brand"
1492
+ ),
1493
+ children: [
1494
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1495
+ "span",
1496
+ {
1497
+ className: cn(
1498
+ "suite-bottom-nav__icon relative z-10 inline-flex items-center justify-center",
1499
+ active && "suite-bottom-nav__icon--active"
1500
+ ),
1501
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1502
+ Icon,
1503
+ {
1504
+ className: "h-6 w-6",
1505
+ strokeWidth: active ? 2.25 : 1.75,
1506
+ "aria-hidden": true
1507
+ }
1508
+ )
1509
+ }
1510
+ ),
1511
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "relative z-10 max-w-full truncate", children: item.label })
1512
+ ]
1513
+ },
1514
+ item.href
1515
+ );
1516
+ })
1517
+ ]
1518
+ }
1519
+ )
1520
+ ] });
1521
+ }
1522
+
1523
+ // src/suite/lib/apps.ts
1524
+ var SUITE_APPS2 = [
1525
+ {
1526
+ slug: "shellstack",
1527
+ name: "ShellStack",
1528
+ description: "Organisation and billing",
1529
+ icon: "/logo-shellstack-static.svg",
1530
+ landing: "/home",
1531
+ baseUrlEnv: "NEXT_PUBLIC_SHELLSTACK_URL",
1532
+ baseUrlFallback: "http://localhost:3002"
1533
+ },
1534
+ {
1535
+ slug: "tides",
1536
+ name: "Tides",
1537
+ description: "Projects and tasks",
1538
+ icon: "/tides-icon.svg",
1539
+ landing: "/today",
1540
+ baseUrlEnv: "NEXT_PUBLIC_TIDES_URL",
1541
+ baseUrlFallback: "http://localhost:3001"
1542
+ },
1543
+ {
1544
+ slug: "turtletime",
1545
+ name: "TurtleTime",
1546
+ description: "Time tracking",
1547
+ icon: "/turtletime-icon.svg",
1548
+ landing: "/tracker",
1549
+ baseUrlEnv: "NEXT_PUBLIC_TURTLETIME_URL",
1550
+ baseUrlFallback: "http://localhost:3000"
1551
+ },
1552
+ {
1553
+ slug: "kraken",
1554
+ name: "Kraken",
1555
+ description: "Documents and knowledge",
1556
+ icon: "/kraken-icon.svg",
1557
+ landing: "/today",
1558
+ baseUrlEnv: "NEXT_PUBLIC_KRAKEN_URL",
1559
+ baseUrlFallback: "http://localhost:3003"
1560
+ }
1561
+ ];
1562
+ var SUITE_APP_MAP = Object.fromEntries(SUITE_APPS2.map((app) => [app.slug, app]));
1563
+ function suiteAppBaseUrl(slug) {
1564
+ const def = SUITE_APP_MAP[slug];
1565
+ const fromEnv = typeof process !== "undefined" ? process.env[def.baseUrlEnv] : void 0;
1566
+ return (fromEnv || def.baseUrlFallback).replace(/\/$/, "");
1567
+ }
1568
+
1569
+ // src/suite/components/today-calibrating.tsx
1570
+ var import_iconoir_react7 = require("iconoir-react");
1571
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1572
+ function TodayCalibrating({
1573
+ onRetry,
1574
+ retrying = false,
1575
+ className,
1576
+ spinner: Spinner,
1577
+ variant = "ring",
1578
+ retrySpinnerClassName = "text-ph-brand"
1579
+ }) {
1580
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1581
+ "div",
1582
+ {
1583
+ className: cn(
1584
+ "relative flex min-h-[14rem] flex-col items-center justify-center overflow-hidden rounded-2xl border border-ph-border bg-ph-surface px-5 py-8 text-center shadow-sm",
1585
+ className
1586
+ ),
1587
+ "data-test": "today-calibrating",
1588
+ children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1590
+ "div",
1591
+ {
1592
+ className: "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(251,191,36,0.12),transparent_55%)]",
1593
+ "aria-hidden": true
1594
+ }
1595
+ ),
1596
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative mb-6 flex h-24 w-24 items-center justify-center", children: [
1597
+ variant === "ring" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1598
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1599
+ Spinner,
1600
+ {
1601
+ size: "lg",
1602
+ className: "absolute inset-0 m-auto opacity-40",
1603
+ label: "Loading today"
1604
+ }
1605
+ ),
1606
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1607
+ "span",
1608
+ {
1609
+ className: "bg-ph-muted/30 absolute inset-2 animate-ping rounded-full",
1610
+ "aria-hidden": true
1611
+ }
1612
+ )
1613
+ ] }) : null,
1614
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "relative flex h-14 w-14 items-center justify-center rounded-2xl border border-ph-border bg-ph-surface text-ph-ink shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1615
+ import_iconoir_react7.SunLight,
1616
+ {
1617
+ className: cn("h-9 w-9", variant === "pulse" && "animate-pulse"),
1618
+ strokeWidth: 2,
1619
+ "aria-hidden": true
1620
+ }
1621
+ ) })
1622
+ ] }),
1623
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "relative text-base font-semibold tracking-tight text-ph-ink sm:text-lg md:text-xl", children: "Calibrating your day" }),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "relative mt-1.5 max-w-sm text-sm sm:text-[15px] leading-relaxed text-ph-subtle", children: "Pulling together tasks, time and updates from your workspace\u2026" }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "relative mt-5 flex gap-1.5", "aria-hidden": true, children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1626
+ "span",
1627
+ {
1628
+ className: "h-1.5 w-5 animate-pulse rounded-full bg-ph-muted",
1629
+ style: { animationDelay: `${i * 120}ms` }
1630
+ },
1631
+ i
1632
+ )) }),
1633
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1634
+ "button",
1635
+ {
1636
+ type: "button",
1637
+ onClick: onRetry,
1638
+ disabled: retrying,
1639
+ className: "relative mt-6 inline-flex items-center gap-2 rounded-xl border border-ph-border bg-ph-canvas px-4 py-2 text-sm font-medium text-ph-ink transition hover:bg-ph-muted disabled:opacity-60",
1640
+ children: [
1641
+ retrying ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Spinner, { size: "sm", className: retrySpinnerClassName }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_iconoir_react7.Refresh, { className: "h-4 w-4", "aria-hidden": true }),
1642
+ "Try again"
1643
+ ]
1644
+ }
1645
+ ) : null
1646
+ ]
1647
+ }
1648
+ );
1649
+ }
1650
+
1651
+ // src/suite/components/theme-provider.tsx
1652
+ var import_react7 = require("react");
1653
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1654
+ var SuiteThemeContext = (0, import_react7.createContext)(null);
1655
+ function systemTheme(fallback) {
1656
+ if (typeof window === "undefined") return fallback;
1657
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1658
+ }
1659
+ function resolveTheme(theme, fallback) {
1660
+ return theme === "system" ? systemTheme(fallback) : theme;
1661
+ }
1662
+ function applyTheme(theme, config) {
1663
+ const resolved = resolveTheme(theme, config.fallbackTheme);
1664
+ if (typeof document === "undefined") return resolved;
1665
+ document.documentElement.setAttribute(
1666
+ "data-theme",
1667
+ resolved === "dark" ? config.darkThemeId : config.lightThemeId
1668
+ );
1669
+ document.documentElement.classList.toggle("dark", resolved === "dark");
1670
+ if (config.themeColorLight && config.themeColorDark) {
1671
+ const meta = document.getElementById(
1672
+ "theme-color-meta"
1673
+ );
1674
+ if (meta) {
1675
+ meta.content = resolved === "light" ? config.themeColorLight : config.themeColorDark;
1676
+ }
1677
+ }
1678
+ return resolved;
1679
+ }
1680
+ function readStoredTheme(storageKey) {
1681
+ if (typeof window === "undefined") return "system";
1682
+ const stored = localStorage.getItem(storageKey);
1683
+ if (stored === "light" || stored === "dark" || stored === "system")
1684
+ return stored;
1685
+ return "system";
1686
+ }
1687
+ function SuiteThemeProvider({
1688
+ config,
1689
+ children
1690
+ }) {
1691
+ const [state, setState] = (0, import_react7.useState)(() => {
1692
+ const theme2 = readStoredTheme(config.storageKey);
1693
+ if (typeof document === "undefined") {
1694
+ return { theme: theme2, resolvedTheme: config.fallbackTheme };
1695
+ }
1696
+ const domTheme = document.documentElement.getAttribute("data-theme");
1697
+ const resolvedTheme2 = domTheme === config.darkThemeId ? "dark" : domTheme === config.lightThemeId ? "light" : resolveTheme(theme2, config.fallbackTheme);
1698
+ return { theme: theme2, resolvedTheme: resolvedTheme2 };
1699
+ });
1700
+ const { theme, resolvedTheme } = state;
1701
+ (0, import_react7.useEffect)(() => {
1702
+ if (theme !== "system") return;
1703
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
1704
+ const update = () => {
1705
+ const resolved = applyTheme("system", config);
1706
+ setState((current) => __spreadProps(__spreadValues({}, current), { resolvedTheme: resolved }));
1707
+ };
1708
+ media.addEventListener("change", update);
1709
+ return () => media.removeEventListener("change", update);
1710
+ }, [theme, config]);
1711
+ const setTheme = (0, import_react7.useCallback)(
1712
+ (next) => {
1713
+ const resolved = applyTheme(next, config);
1714
+ setState({ theme: next, resolvedTheme: resolved });
1715
+ localStorage.setItem(config.storageKey, next);
1716
+ },
1717
+ [config]
1718
+ );
1719
+ const toggleTheme = (0, import_react7.useCallback)(() => {
1720
+ setState((prev) => {
1721
+ const next = resolveTheme(prev.theme, config.fallbackTheme) === "dark" ? "light" : "dark";
1722
+ const resolved = applyTheme(next, config);
1723
+ localStorage.setItem(config.storageKey, next);
1724
+ return { theme: next, resolvedTheme: resolved };
1725
+ });
1726
+ }, [config]);
1727
+ const value = (0, import_react7.useMemo)(
1728
+ () => ({ theme, resolvedTheme, setTheme, toggleTheme }),
1729
+ [theme, resolvedTheme, setTheme, toggleTheme]
1730
+ );
1731
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SuiteThemeContext.Provider, { value, children });
1732
+ }
1733
+ function useSuiteTheme() {
1734
+ const ctx = (0, import_react7.useContext)(SuiteThemeContext);
1735
+ if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
1736
+ return ctx;
1737
+ }
1738
+
1739
+ // src/suite/components/ai-panel.tsx
1740
+ var import_react8 = require("react");
1741
+ var import_iconoir_react8 = require("iconoir-react");
1742
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1743
+ function SuiteAiPanel({
1744
+ presets,
1745
+ emptyState,
1746
+ fetchStatus,
1747
+ complete,
1748
+ brandIcon: BrandIcon,
1749
+ spinner: Spinner
1750
+ }) {
1751
+ const [open, setOpen] = (0, import_react8.useState)(false);
1752
+ const [prompt, setPrompt] = (0, import_react8.useState)("");
1753
+ const [loading, setLoading] = (0, import_react8.useState)(false);
1754
+ const [answer, setAnswer] = (0, import_react8.useState)("");
1755
+ const [error, setError] = (0, import_react8.useState)("");
1756
+ const [copied, setCopied] = (0, import_react8.useState)(false);
1757
+ async function ask(text) {
1758
+ const trimmed = text.trim();
1759
+ if (!trimmed || loading) return;
1760
+ setLoading(true);
1761
+ setError("");
1762
+ setAnswer("");
1763
+ setCopied(false);
1764
+ try {
1765
+ const status = await fetchStatus();
1766
+ if (!status.configured) {
1767
+ setError("ShellStack AI is not configured for this workspace yet.");
1768
+ return;
1769
+ }
1770
+ const result = await complete({ prompt: trimmed });
1771
+ setAnswer(result.text);
1772
+ } catch (err) {
1773
+ setError(
1774
+ err instanceof Error ? err.message : "The AI request failed. Try again."
1775
+ );
1776
+ } finally {
1777
+ setLoading(false);
1778
+ }
1779
+ }
1780
+ async function copyAnswer() {
1781
+ if (!answer) return;
1782
+ try {
1783
+ await navigator.clipboard.writeText(answer);
1784
+ setCopied(true);
1785
+ window.setTimeout(() => setCopied(false), 1500);
1786
+ } catch (e) {
1787
+ }
1788
+ }
1789
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1790
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1791
+ "button",
1792
+ {
1793
+ type: "button",
1794
+ "data-test": "ai-panel-launcher",
1795
+ onClick: () => setOpen(true),
1796
+ className: "fixed bottom-5 right-5 z-30 flex items-center gap-2 rounded-full border border-ph-border bg-ph-surface px-4 py-2.5 text-sm font-medium text-ph-ink shadow-ph hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
1797
+ "aria-label": "Open ShellStack AI",
1798
+ children: [
1799
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BrandIcon, { className: "h-4 w-4 text-ph-brand" }),
1800
+ "Ask AI"
1801
+ ]
1802
+ }
1803
+ ),
1804
+ open && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "fixed inset-0 z-40", role: "dialog", "aria-modal": "true", children: [
1805
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1806
+ "button",
1807
+ {
1808
+ type: "button",
1809
+ className: "absolute inset-0 bg-black/25",
1810
+ onClick: () => setOpen(false),
1811
+ "aria-label": "Close AI panel"
1812
+ }
1813
+ ),
1814
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("aside", { className: "absolute inset-y-0 right-0 flex w-full max-w-md flex-col border-l border-ph-border bg-ph-surface shadow-2xl", children: [
1815
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center justify-between border-b border-ph-border px-4 py-3", children: [
1816
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
1817
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BrandIcon, { className: "h-4 w-4 text-ph-brand" }),
1818
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-semibold text-ph-ink", children: "ShellStack AI" })
1819
+ ] }),
1820
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1821
+ "button",
1822
+ {
1823
+ type: "button",
1824
+ onClick: () => setOpen(false),
1825
+ className: "rounded p-1 hover:bg-ph-muted",
1826
+ "aria-label": "Close",
1827
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_iconoir_react8.Xmark, { className: "h-4 w-4" })
1828
+ }
1829
+ )
1830
+ ] }),
1831
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex flex-wrap gap-1.5 border-b border-ph-border px-4 py-2", children: presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1832
+ "button",
1833
+ {
1834
+ type: "button",
1835
+ onClick: () => {
1836
+ setPrompt(preset.prompt);
1837
+ void ask(preset.prompt);
1838
+ },
1839
+ disabled: loading,
1840
+ className: "rounded-md border border-ph-border px-2 py-1 text-xs text-ph-ink hover:bg-ph-muted disabled:opacity-50",
1841
+ children: preset.label
1842
+ },
1843
+ preset.label
1844
+ )) }),
1845
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
1846
+ error && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "bg-ph-danger/10 rounded-lg px-3 py-2 text-sm text-ph-danger", children: error }),
1847
+ loading && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 text-sm text-ph-mutedtext", children: [
1848
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Spinner, { className: "h-4 w-4 animate-spin" }),
1849
+ "Thinking\u2026"
1850
+ ] }),
1851
+ answer && !loading && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "rounded-lg bg-ph-muted px-3 py-2 text-sm text-ph-ink", children: [
1852
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "whitespace-pre-wrap", children: answer }),
1853
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1854
+ "button",
1855
+ {
1856
+ type: "button",
1857
+ onClick: () => void copyAnswer(),
1858
+ className: "mt-2 inline-flex items-center gap-1 text-xs text-ph-mutedtext hover:text-ph-ink",
1859
+ children: [
1860
+ copied ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_iconoir_react8.Check, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_iconoir_react8.Copy, { className: "h-3 w-3" }),
1861
+ copied ? "Copied" : "Copy"
1862
+ ]
1863
+ }
1864
+ )
1865
+ ] }),
1866
+ !answer && !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-8 text-center text-xs text-ph-mutedtext", children: emptyState })
1867
+ ] }),
1868
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "border-t border-ph-border p-3", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex gap-2", children: [
1869
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1870
+ "textarea",
1871
+ {
1872
+ value: prompt,
1873
+ onChange: (e) => setPrompt(e.target.value),
1874
+ onKeyDown: (e) => {
1875
+ if (e.key === "Enter" && !e.shiftKey) {
1876
+ e.preventDefault();
1877
+ void ask(prompt);
1878
+ }
1879
+ },
1880
+ placeholder: "Ask anything\u2026",
1881
+ rows: 2,
1882
+ disabled: loading,
1883
+ className: "flex-1 resize-none rounded-lg border border-ph-border bg-ph-canvas p-2 text-sm text-ph-ink outline-none focus:ring-1 focus:ring-ph-brand"
1884
+ }
1885
+ ),
1886
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1887
+ "button",
1888
+ {
1889
+ type: "button",
1890
+ onClick: () => void ask(prompt),
1891
+ disabled: loading || !prompt.trim(),
1892
+ className: "self-end rounded-lg bg-ph-brand px-3 py-2 text-sm font-medium text-white disabled:opacity-50",
1893
+ children: "Ask"
1894
+ }
1895
+ )
1896
+ ] }) })
1897
+ ] })
1898
+ ] })
1899
+ ] });
1900
+ }
1901
+
1902
+ // src/suite/lib/motion.tsx
1903
+ var import_react9 = require("motion/react");
1904
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1905
+ function SuiteMotionProvider({ children }) {
1906
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react9.LazyMotion, { features: import_react9.domAnimation, strict: true, children });
1907
+ }
1908
+ var SUITE_SPRINGS = {
1909
+ /** Snappy panel/sheet entrances — small overshoot. */
1910
+ fast: { type: "spring", stiffness: 400, damping: 32, mass: 0.8 },
1911
+ /** Gentle scrims, fades, list staggers. */
1912
+ subtle: { type: "spring", stiffness: 260, damping: 26, mass: 1 },
1913
+ /** Press/tap feedback — quick settle, no wobble. */
1914
+ press: { type: "spring", stiffness: 600, damping: 35, mass: 0.5 }
1915
+ };
1916
+
1917
+ // src/suite/components/suite-layout.tsx
1918
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1919
+ var WIDTHS = {
1920
+ full: "max-w-none",
1921
+ wide: "max-w-7xl",
1922
+ content: "max-w-6xl",
1923
+ narrow: "max-w-4xl"
1924
+ };
1925
+ function SuiteBreadcrumbs({ items, className }) {
1926
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("nav", { "aria-label": "Breadcrumb", className, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ol", { className: "flex min-w-0 items-center gap-1.5", children: items.map((item, index) => {
1927
+ const isLast = index === items.length - 1;
1928
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("li", { className: "flex min-w-0 items-center gap-1.5", children: [
1929
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "select-none text-ph-border", "aria-hidden": "true", children: "/" }) : null,
1930
+ item.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1931
+ "a",
1932
+ {
1933
+ href: item.href,
1934
+ className: "truncate transition hover:text-ph-ink",
1935
+ children: item.label
1936
+ }
1937
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1938
+ "span",
1939
+ {
1940
+ className: cn(
1941
+ "truncate",
1942
+ isLast ? "font-medium text-ph-ink" : "text-ph-subtle"
1943
+ ),
1944
+ "aria-current": isLast ? "page" : void 0,
1945
+ children: item.label
1946
+ }
1947
+ )
1948
+ ] }, index);
1949
+ }) }) });
1950
+ }
1951
+ function SuitePage({
1952
+ children,
1953
+ width = "wide",
1954
+ inset = true,
1955
+ className,
1956
+ as: Component = "div",
1957
+ dataTest,
1958
+ id,
1959
+ role,
1960
+ "aria-live": ariaLive
1961
+ }) {
1962
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1963
+ Component,
1964
+ {
1965
+ id,
1966
+ "data-test": dataTest,
1967
+ role,
1968
+ "aria-live": ariaLive,
1969
+ className: cn(
1970
+ "mx-auto flex w-full min-w-0 flex-col",
1971
+ WIDTHS[width],
1972
+ // Same inset as TodayPageFrame so every page title sits at the
1973
+ // same baseline regardless of app.
1974
+ inset && "px-4 pb-10 pt-5 sm:px-6 sm:pt-6 lg:px-8",
1975
+ className
1976
+ ),
1977
+ children
1978
+ }
1979
+ );
1980
+ }
1981
+ function SuitePageHeader({
1982
+ title,
1983
+ description,
1984
+ eyebrow,
1985
+ actions,
1986
+ className,
1987
+ titleClassName,
1988
+ dataTest,
1989
+ /** Optional icon rendered before the title. */
1990
+ icon,
1991
+ /** Use the animated sun/moon icon from the Today page. */
1992
+ todayIcon,
1993
+ todayIconClassName,
1994
+ /** Small badge/pill next to the title (e.g. "Beta", org name). */
1995
+ badge,
1996
+ /** Breadcrumb line rendered above the title. */
1997
+ breadcrumb
1998
+ }) {
1999
+ const resolvedIcon = todayIcon ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2000
+ TodayTimeIcon,
2001
+ {
2002
+ className: cn(
2003
+ "h-6 w-6 sm:h-7 sm:w-7 text-amber-500",
2004
+ todayIconClassName
2005
+ )
2006
+ }
2007
+ ) : icon;
2008
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2009
+ "header",
2010
+ {
2011
+ "data-test": dataTest,
2012
+ className: cn(
2013
+ "flex items-start justify-between gap-4 pb-6 sm:pb-7",
2014
+ className
2015
+ ),
2016
+ children: [
2017
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-w-0 items-start gap-3", children: [
2018
+ resolvedIcon ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2019
+ "div",
2020
+ {
2021
+ className: "hidden shrink-0 pt-1 sm:block",
2022
+ "aria-hidden": "true",
2023
+ children: resolvedIcon
2024
+ }
2025
+ ) : null,
2026
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-w-0 flex-col gap-1", children: [
2027
+ eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: eyebrow }) : null,
2028
+ breadcrumb ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mb-0.5 flex min-w-0 items-center gap-1.5 text-xs text-ph-subtle", children: breadcrumb }) : null,
2029
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
2030
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2031
+ "h1",
2032
+ {
2033
+ className: cn(
2034
+ "font-display text-[1.625rem] font-bold leading-tight tracking-tight text-ph-ink sm:text-[2.25rem]",
2035
+ titleClassName
2036
+ ),
2037
+ children: title
2038
+ }
2039
+ ),
2040
+ badge ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "rounded-md bg-ph-muted px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-ph-subtle", children: badge }) : null
2041
+ ] }),
2042
+ description ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
2043
+ ] })
2044
+ ] }),
2045
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center gap-2 pt-1", children: actions }) : null
2046
+ ]
2047
+ }
2048
+ );
2049
+ }
2050
+ function SuiteToolbar({
2051
+ children,
2052
+ className,
2053
+ label = "Page tools",
2054
+ dataTest
2055
+ }) {
2056
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2057
+ "div",
2058
+ {
2059
+ role: "toolbar",
2060
+ "aria-label": label,
2061
+ "data-test": dataTest,
2062
+ className: cn("flex min-w-0 flex-wrap items-center gap-2", className),
2063
+ children
2064
+ }
2065
+ );
2066
+ }
2067
+ function SuiteTabList({
2068
+ children,
2069
+ className,
2070
+ label,
2071
+ dataTest
2072
+ }) {
2073
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2074
+ "nav",
2075
+ {
2076
+ "aria-label": label,
2077
+ "data-test": dataTest,
2078
+ className: cn(
2079
+ "flex min-w-0 flex-wrap gap-2 [&_a]:min-h-[48px] [&_button]:min-h-[48px] sm:[&_a]:min-h-[44px] sm:[&_button]:min-h-[44px]",
2080
+ className
2081
+ ),
2082
+ children
2083
+ }
2084
+ );
2085
+ }
2086
+ function SuiteSectionHeader({
2087
+ title,
2088
+ description,
2089
+ action,
2090
+ className
2091
+ }) {
2092
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2093
+ "div",
2094
+ {
2095
+ className: cn(
2096
+ "flex min-w-0 flex-col gap-4 sm:flex-row sm:items-end sm:justify-between",
2097
+ className
2098
+ ),
2099
+ children: [
2100
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "min-w-0", children: [
2101
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "font-display text-balance text-lg font-semibold tracking-tight text-ph-ink sm:text-xl", children: title }),
2102
+ description ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mt-1 max-w-2xl text-pretty text-sm leading-5 text-ph-subtle", children: description }) : null
2103
+ ] }),
2104
+ action ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex min-h-[44px] shrink-0 items-center", children: action }) : null
2105
+ ]
2106
+ }
2107
+ );
2108
+ }
2109
+
2110
+ // src/suite/components/suite-skeleton.tsx
2111
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2112
+ function SuiteSkeleton({
2113
+ className,
2114
+ lines = 1,
2115
+ circle,
2116
+ width
2117
+ }) {
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2119
+ "div",
2120
+ {
2121
+ className: cn("space-y-2", circle ? "flex flex-col items-center" : ""),
2122
+ "aria-hidden": true,
2123
+ children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2124
+ "div",
2125
+ {
2126
+ className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
2127
+ style: { animationDelay: `${index * 80}ms` },
2128
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2129
+ "div",
2130
+ {
2131
+ className: cn(
2132
+ "ph-skeleton",
2133
+ circle ? "h-10 w-10 rounded-full" : "h-3",
2134
+ className
2135
+ ),
2136
+ style: !circle ? {
2137
+ width: width != null ? width : `${70 + index % 3 * 15}%`
2138
+ } : void 0
2139
+ }
2140
+ )
2141
+ },
2142
+ index
2143
+ ))
2144
+ }
2145
+ );
2146
+ }
2147
+ function SuiteSkeletonCard({
2148
+ className,
2149
+ rows = 3,
2150
+ header = true
2151
+ }) {
2152
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2153
+ "div",
2154
+ {
2155
+ className: cn(
2156
+ "ph-skeleton-card animate-[skeleton-stagger_0.5s_ease-out_both] rounded-2xl border border-ph-border/70 bg-ph-surface p-6 shadow-sm",
2157
+ className
2158
+ ),
2159
+ "aria-hidden": true,
2160
+ children: [
2161
+ header ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2162
+ "div",
2163
+ {
2164
+ className: "mb-5 flex items-center gap-3 animate-[skeleton-stagger_0.5s_ease-out_both]",
2165
+ style: { animationDelay: "100ms" },
2166
+ children: [
2167
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-xl" }),
2168
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1 space-y-2", children: [
2169
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ph-skeleton h-4 w-1/3" }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
2171
+ ] })
2172
+ ]
2173
+ }
2174
+ ) : null,
2175
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "space-y-3", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2176
+ "div",
2177
+ {
2178
+ className: "animate-[skeleton-stagger_0.5s_ease-out_both]",
2179
+ style: { animationDelay: `${200 + index * 80}ms` },
2180
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2181
+ "div",
2182
+ {
2183
+ className: "ph-skeleton h-3",
2184
+ style: { width: `${70 + index % 3 * 15}%` }
2185
+ }
2186
+ )
2187
+ },
2188
+ index
2189
+ )) })
2190
+ ]
2191
+ }
2192
+ );
2193
+ }
2194
+ function SuiteSkeletonList({
2195
+ className,
2196
+ items = 4
2197
+ }) {
2198
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("space-y-3", className), "aria-hidden": true, children: Array.from({ length: items }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2199
+ "div",
2200
+ {
2201
+ className: "animate-[skeleton-stagger_0.6s_ease-out_both]",
2202
+ style: { animationDelay: `${index * 100}ms` },
2203
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-3 rounded-xl border border-ph-border/60 bg-ph-surface p-4", children: [
2204
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ph-skeleton h-10 w-10 rounded-lg" }),
2205
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "min-w-0 flex-1 space-y-2", children: [
2206
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ph-skeleton h-3.5 w-3/4" }),
2207
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "ph-skeleton h-3 w-1/2" })
2208
+ ] })
2209
+ ] })
2210
+ },
2211
+ index
2212
+ )) });
2213
+ }
2214
+ function SuiteEmptyState({
2215
+ icon,
2216
+ title,
2217
+ description,
2218
+ action,
2219
+ className,
2220
+ size = "md"
2221
+ }) {
2222
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2223
+ "div",
2224
+ {
2225
+ className: cn(
2226
+ "relative overflow-hidden rounded-2xl border border-ph-border/80 bg-ph-surface text-center",
2227
+ size === "sm" ? "px-5 py-8" : "px-6 py-12 sm:px-10 sm:py-16",
2228
+ className
2229
+ ),
2230
+ children: [
2231
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden opacity-60", "aria-hidden": true, children: [
2232
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "absolute -left-10 -top-10 h-40 w-40 rounded-full bg-ph-brand/10 blur-3xl" }),
2233
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "absolute -bottom-10 -right-10 h-40 w-40 rounded-full bg-ph-accent/10 blur-3xl" })
2234
+ ] }),
2235
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "relative", children: [
2236
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mx-auto mb-4 inline-flex items-center justify-center rounded-2xl bg-ph-muted p-3 shadow-sm ring-1 ring-black/[0.06]", children: icon }) : null,
2237
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2238
+ "h3",
2239
+ {
2240
+ className: cn(
2241
+ "font-semibold text-ph-ink",
2242
+ size === "sm" ? "text-base" : "text-lg"
2243
+ ),
2244
+ children: title
2245
+ }
2246
+ ),
2247
+ description ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mx-auto mt-2 max-w-sm text-sm leading-relaxed text-ph-subtle", children: description }) : null,
2248
+ action ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: action }) : null
2249
+ ] })
2250
+ ]
2251
+ }
2252
+ );
2253
+ }
2254
+
2255
+ // src/suite/components/suite-settings-mobile-nav.tsx
2256
+ var import_react10 = require("react");
2257
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2258
+ function SuiteSettingsMobileNav({
2259
+ items,
2260
+ activeHref,
2261
+ onSelect,
2262
+ dataTest
2263
+ }) {
2264
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2265
+ "nav",
2266
+ {
2267
+ "data-test": dataTest,
2268
+ "aria-label": "Settings sections",
2269
+ className: "-mx-4 px-4 lg:hidden",
2270
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex snap-x gap-2 overflow-x-auto pb-3 pt-1 scrollbar-hide [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item) => {
2271
+ const Icon = item.icon;
2272
+ const active = activeHref === item.href || activeHref.startsWith(`${item.href}/`);
2273
+ const iconNode = (0, import_react10.isValidElement)(item.icon) ? item.icon : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2274
+ Icon,
2275
+ {
2276
+ className: cn(
2277
+ "h-4 w-4 shrink-0 transition",
2278
+ active ? "text-ph-surface" : "text-ph-mutedtext"
2279
+ ),
2280
+ "aria-hidden": true
2281
+ }
2282
+ );
2283
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2284
+ "button",
2285
+ {
2286
+ type: "button",
2287
+ "data-test": item.testId,
2288
+ onClick: () => onSelect(item.href),
2289
+ "aria-current": active ? "page" : void 0,
2290
+ className: cn(
2291
+ "group relative flex shrink-0 snap-start items-center gap-2 rounded-full px-3.5 py-2.5 text-sm font-medium transition",
2292
+ "min-h-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand",
2293
+ active ? "bg-ph-ink text-ph-surface shadow-sm" : "bg-ph-surface text-ph-subtle ring-1 ring-ph-border hover:bg-ph-muted hover:text-ph-ink"
2294
+ ),
2295
+ children: [
2296
+ iconNode,
2297
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "whitespace-nowrap", children: item.label }),
2298
+ active ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "absolute inset-x-0 -bottom-3 h-0.5 rounded-full bg-ph-ink lg:hidden" }) : null
2299
+ ]
2300
+ },
2301
+ item.href
2302
+ );
2303
+ }) })
2304
+ }
2305
+ );
2306
+ }
2307
+
2308
+ // src/suite/components/suite-sidebar.tsx
2309
+ var import_link3 = __toESM(require("next/link"));
2310
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2311
+ function renderNode(node, collapsed) {
2312
+ if (typeof node === "function") return node(collapsed);
2313
+ return node;
2314
+ }
2315
+ function SuiteSidebar({
2316
+ appSwitcher,
2317
+ contextSwitcher,
2318
+ navItems,
2319
+ secondaryNav,
2320
+ userMenu,
2321
+ notificationBell,
2322
+ footerExtras,
2323
+ collapsed = false,
2324
+ className,
2325
+ surface = false
2326
+ }) {
2327
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2328
+ "div",
2329
+ {
2330
+ className: cn(
2331
+ "flex h-full w-full min-w-0 flex-col text-ph-ink",
2332
+ surface ? "bg-ph-surface" : "bg-ph-canvas",
2333
+ className
2334
+ ),
2335
+ children: [
2336
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2337
+ "div",
2338
+ {
2339
+ className: cn(
2340
+ "flex shrink-0 border-b border-ph-border",
2341
+ surface ? "bg-ph-surface" : "bg-ph-canvas",
2342
+ collapsed ? "flex-col items-center gap-1 px-1 py-2" : "h-14 items-center justify-between gap-2 px-3"
2343
+ ),
2344
+ children: [
2345
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn("min-w-0", collapsed ? "shrink-0" : "flex-1"), children: renderNode(appSwitcher, collapsed) }),
2346
+ collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "shrink-0", children: renderNode(notificationBell, collapsed) })
2347
+ ]
2348
+ }
2349
+ ),
2350
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2351
+ "div",
2352
+ {
2353
+ className: cn(
2354
+ "flex min-h-0 flex-1 flex-col",
2355
+ surface ? "bg-ph-surface" : "bg-ph-canvas",
2356
+ collapsed ? "px-1.5 py-2" : "p-2"
2357
+ ),
2358
+ children: [
2359
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2360
+ "nav",
2361
+ {
2362
+ "aria-label": "Pages",
2363
+ className: "shrink-0 space-y-0.5",
2364
+ children: [
2365
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn("mb-3", collapsed && "flex justify-center"), children: renderNode(contextSwitcher, collapsed) }),
2366
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "space-y-0.5", children: navItems.map((item) => {
2367
+ const Icon = item.icon;
2368
+ const active = Boolean(item.active);
2369
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2370
+ import_link3.default,
2371
+ {
2372
+ href: item.href,
2373
+ "data-test": "nav-link",
2374
+ title: collapsed ? item.label : void 0,
2375
+ "aria-current": active ? "page" : void 0,
2376
+ onClick: item.onClick,
2377
+ className: cn(
2378
+ "group relative flex items-center gap-2.5 rounded-[var(--ph-radius-app)] text-sm font-medium transition-colors",
2379
+ active ? "bg-ph-muted" : "text-ph-subtle hover:bg-ph-muted hover:text-ph-ink",
2380
+ collapsed ? "mx-auto size-10 shrink-0 justify-center gap-0 px-0 py-0" : "w-full px-2.5 py-2"
2381
+ ),
2382
+ children: [
2383
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2384
+ "span",
2385
+ {
2386
+ className: cn(
2387
+ "relative flex h-4 w-4 shrink-0 items-center justify-center",
2388
+ item.iconClassName
2389
+ ),
2390
+ "aria-hidden": true,
2391
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2392
+ Icon,
2393
+ {
2394
+ className: "h-full w-full",
2395
+ strokeWidth: active ? 2 : 1.75
2396
+ }
2397
+ )
2398
+ }
2399
+ ),
2400
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2401
+ "span",
2402
+ {
2403
+ className: cn(
2404
+ "relative truncate",
2405
+ active ? "text-ph-ink" : "text-ph-subtle group-hover:text-ph-ink",
2406
+ collapsed && "sr-only"
2407
+ ),
2408
+ children: item.label
2409
+ }
2410
+ ),
2411
+ !collapsed && item.badge ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "relative ml-auto shrink-0", children: item.badge }) : null
2412
+ ]
2413
+ },
2414
+ item.label
2415
+ );
2416
+ }) })
2417
+ ]
2418
+ }
2419
+ ),
2420
+ secondaryNav ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2421
+ "div",
2422
+ {
2423
+ className: "min-h-0 flex-1 overflow-y-auto",
2424
+ "data-test": "suite-sidebar-secondary-nav",
2425
+ children: secondaryNav
2426
+ }
2427
+ ) : null
2428
+ ]
2429
+ }
2430
+ ),
2431
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2432
+ "div",
2433
+ {
2434
+ className: cn(
2435
+ "shrink-0 border-t border-ph-border",
2436
+ surface ? "bg-ph-surface" : "bg-ph-canvas",
2437
+ collapsed ? "p-1.5" : "p-3"
2438
+ ),
2439
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2440
+ "div",
2441
+ {
2442
+ className: cn(
2443
+ "flex items-center gap-2",
2444
+ collapsed && "justify-center"
2445
+ ),
2446
+ children: [
2447
+ renderNode(userMenu, collapsed),
2448
+ !collapsed && footerExtras ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ml-auto shrink-0", children: renderNode(footerExtras, collapsed) }) : null
2449
+ ]
2450
+ }
2451
+ )
2452
+ }
2453
+ )
2454
+ ]
2455
+ }
2456
+ );
2457
+ }
2458
+
2459
+ // src/suite/components/suite-app-layout.tsx
2460
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2461
+ function SuiteAppLayout({
2462
+ sidebar,
2463
+ children,
2464
+ mobileHeader,
2465
+ bottomNav,
2466
+ sidebarWidth = 240,
2467
+ collapsed = false,
2468
+ onStartResize,
2469
+ onResetWidth,
2470
+ className
2471
+ }) {
2472
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { "data-test": "app-shell", className: cn("min-h-screen", className), children: [
2473
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "hidden lg:flex h-screen overflow-hidden", children: [
2474
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2475
+ "aside",
2476
+ {
2477
+ className: "relative flex shrink-0 flex-col border-r border-ph-border transition-[width] duration-200 ease-out",
2478
+ style: { width: `${sidebarWidth}px` },
2479
+ children: [
2480
+ sidebar,
2481
+ onStartResize ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2482
+ "div",
2483
+ {
2484
+ role: "separator",
2485
+ "aria-orientation": "vertical",
2486
+ "aria-label": "Resize sidebar",
2487
+ onPointerDown: onStartResize,
2488
+ onDoubleClick: onResetWidth,
2489
+ className: "absolute inset-y-0 right-0 z-10 w-1 cursor-col-resize transition-colors hover:bg-[color-mix(in_oklab,var(--ph-accent)_30%,transparent)]"
2490
+ }
2491
+ ) : null
2492
+ ]
2493
+ }
2494
+ ),
2495
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("main", { className: "min-w-0 flex-1 overflow-y-auto bg-ph-canvas", children })
2496
+ ] }),
2497
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-h-screen flex-col lg:hidden", children: [
2498
+ mobileHeader ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "shrink-0", children: mobileHeader }) : null,
2499
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("main", { className: "min-w-0 flex-1 overflow-y-auto bg-ph-canvas", children }),
2500
+ bottomNav ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "shrink-0", children: bottomNav }) : null
2501
+ ] })
2502
+ ] });
2503
+ }
2504
+ // Annotate the CommonJS export names for ESM import in node:
2505
+ 0 && (module.exports = {
2506
+ APP_ACCENTS,
2507
+ APP_GLYPHS,
2508
+ AnimatedMoon,
2509
+ AnimatedSun,
2510
+ AppSwitcher,
2511
+ AppSwitcherChevron,
2512
+ AppSwitcherMark,
2513
+ CommandPaletteHost,
2514
+ DeferredChrome,
2515
+ SUITE_APPS,
2516
+ SUITE_APP_MAP,
2517
+ SUITE_GLYPHS,
2518
+ SUITE_ICON_NAMES,
2519
+ SUITE_SPRINGS,
2520
+ SourceAppGlyph,
2521
+ SuiteAiPanel,
2522
+ SuiteAppIcon,
2523
+ SuiteAppLayout,
2524
+ SuiteAppStrip,
2525
+ SuiteBottomNav,
2526
+ SuiteBreadcrumbs,
2527
+ SuiteEmptyState,
2528
+ SuiteIcon,
2529
+ SuiteMobileDrawer,
2530
+ SuiteMobileHeader,
2531
+ SuiteMotionProvider,
2532
+ SuiteNotificationBell,
2533
+ SuitePage,
2534
+ SuitePageHeader,
2535
+ SuiteSectionHeader,
2536
+ SuiteSettingsMobileNav,
2537
+ SuiteSidebar,
2538
+ SuiteSkeleton,
2539
+ SuiteSkeletonCard,
2540
+ SuiteSkeletonList,
2541
+ SuiteTabList,
2542
+ SuiteThemeProvider,
2543
+ SuiteToolbar,
2544
+ SuiteUserMenu,
2545
+ TodayCalibrating,
2546
+ TodayEmptyState,
2547
+ TodayHero,
2548
+ TodayPageFrame,
2549
+ TodayPanel,
2550
+ TodaySection,
2551
+ TodayTimeIcon,
2552
+ TodayTopBar,
2553
+ appSwitcherMarkClass,
2554
+ appSwitcherMenuItemClass,
2555
+ appSwitcherTriggerClass,
2556
+ cn,
2557
+ getTodayGreeting,
2558
+ m,
2559
+ sourceToSuiteApp,
2560
+ suiteAppBaseUrl,
2561
+ suiteAppLabel,
2562
+ useIdleMount,
2563
+ useSuiteTheme
2564
+ });