@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.
- package/dist/{chunk-5HSOBJ4F.mjs → chunk-KORWJCIO.mjs} +600 -932
- package/dist/{index-BgG8Homu.d.mts → index-B5NOyoKS.d.mts} +100 -100
- package/dist/{index-BgG8Homu.d.ts → index-B5NOyoKS.d.ts} +100 -100
- package/dist/index.d.mts +9 -145
- package/dist/index.d.ts +9 -145
- package/dist/index.js +653 -1111
- package/dist/index.mjs +9 -267
- package/dist/suite.d.mts +760 -0
- package/dist/suite.d.ts +760 -0
- package/dist/suite.js +2564 -0
- package/dist/suite.mjs +2465 -0
- package/dist/ui.d.mts +1 -2
- package/dist/ui.d.ts +1 -2
- package/dist/ui.js +14 -10
- package/dist/ui.mjs +1 -1
- package/package.json +43 -24
- package/src/components/icons/icons.tsx +0 -2
- package/src/components/icons/index.ts +0 -3
- package/src/components/sections/IconShowcase.tsx +1 -110
- package/src/components/sections/NewComponentsShowcase.tsx +4 -4
- package/src/components/sections/SidebarShowcase.tsx +3 -3
- package/src/components/sections/SuiteShowcase.tsx +669 -0
- package/src/components/ui/ComponentDocs.tsx +2 -2
- package/src/components/ui/ContextMenu.tsx +1 -1
- package/src/components/ui/Menubar.tsx +1 -1
- package/src/components/ui/Sidebar.tsx +10 -8
- package/src/styles/suite-skin.css +255 -0
- package/src/suite/components/ai-panel.tsx +202 -0
- package/src/suite/components/app-switcher.tsx +196 -0
- package/src/suite/components/command-palette-host.tsx +62 -0
- package/src/suite/components/deferred-chrome.tsx +12 -0
- package/src/suite/components/suite-app-layout.tsx +69 -0
- package/src/suite/components/suite-bottom-nav.tsx +113 -0
- package/src/suite/components/suite-layout.tsx +287 -0
- package/src/suite/components/suite-mobile-drawer.tsx +150 -0
- package/src/suite/components/suite-mobile-header.tsx +71 -0
- package/src/suite/components/suite-notification-bell.tsx +227 -0
- package/src/suite/components/suite-settings-mobile-nav.tsx +83 -0
- package/src/suite/components/suite-sidebar.tsx +191 -0
- package/src/suite/components/suite-skeleton.tsx +191 -0
- package/src/suite/components/suite-user-menu.tsx +128 -0
- package/src/suite/components/theme-provider.tsx +174 -0
- package/src/suite/components/today-calibrating.tsx +95 -0
- package/src/suite/components/today-page-frame.tsx +28 -0
- package/src/suite/components/today-ui.tsx +370 -0
- package/src/suite/icons/app-accents.ts +75 -0
- package/src/suite/icons/glyph-parts.tsx +67 -0
- package/src/suite/icons/glyphs.ts +203 -0
- package/src/suite/icons/index.ts +12 -0
- package/src/suite/icons/suite-app-icon.tsx +68 -0
- package/src/suite/icons/suite-icon.tsx +93 -0
- package/src/suite/index.ts +108 -0
- package/src/suite/lib/apps.ts +75 -0
- package/src/suite/lib/cn.ts +6 -0
- package/src/suite/lib/injected.ts +54 -0
- package/src/suite/lib/motion.tsx +48 -0
- package/src/suite/lib/use-idle-mount.ts +25 -0
- package/src/components/icons/lemon-brand-icons.tsx +0 -16
- package/src/components/icons/lemon-category-icons.tsx +0 -72
- 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
|
+
});
|