@tomismeta/paperclip-aperture 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/manifest.js +1 -80
- package/dist/ui/index.js +1 -1851
- package/dist/worker.js +11 -18842
- package/package.json +2 -2
package/dist/ui/index.js
CHANGED
|
@@ -1,1851 +1 @@
|
|
|
1
|
-
// src/ui/index.tsx
|
|
2
|
-
import { useEffect as useEffect5, useMemo as useMemo2, useRef as useRef5, useState as useState5 } from "react";
|
|
3
|
-
|
|
4
|
-
// src/aperture/frame-model.ts
|
|
5
|
-
var AMBIENT_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
6
|
-
function isBudgetOverride(frame) {
|
|
7
|
-
return frame.provenance?.factors?.includes("budget stop") ?? false;
|
|
8
|
-
}
|
|
9
|
-
function frameUpdatedAt(frame, snapshotUpdatedAt) {
|
|
10
|
-
return frame.timing.updatedAt || snapshotUpdatedAt;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// src/aperture/attention-language.ts
|
|
14
|
-
var GENERIC_QUEUED_JUDGMENT = "Important enough to keep visible, but not enough to displace now.";
|
|
15
|
-
var GENERIC_NEXT_JUDGMENT = GENERIC_QUEUED_JUDGMENT;
|
|
16
|
-
function approvalBlockingSummary(budgetOverride) {
|
|
17
|
-
return budgetOverride ? "Budget controls are blocking work until a board decision lands." : "A board decision is blocking work in Paperclip.";
|
|
18
|
-
}
|
|
19
|
-
function genericJudgmentLine(frame, lane) {
|
|
20
|
-
if (lane === "now") {
|
|
21
|
-
if (isBudgetOverride(frame)) return approvalBlockingSummary(true);
|
|
22
|
-
if (frame.mode === "approval") return "A human decision is blocking work right now.";
|
|
23
|
-
if (frame.tone === "critical") return "This surfaced because it can displace the operator now.";
|
|
24
|
-
return "This is the clearest current operator focus.";
|
|
25
|
-
}
|
|
26
|
-
if (isBudgetOverride(frame)) return "Budget review is staged behind the current focus.";
|
|
27
|
-
if (lane === "next") return GENERIC_NEXT_JUDGMENT;
|
|
28
|
-
return "Useful for awareness, but not interrupting.";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/ui/chrome.tsx
|
|
32
|
-
import { useEffect, useRef, useState } from "react";
|
|
33
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
34
|
-
var ACCENT_COLOR = "#007ACC";
|
|
35
|
-
var ACCENT_BG = `${ACCENT_COLOR}14`;
|
|
36
|
-
var ACCENT_BORDER = `${ACCENT_COLOR}33`;
|
|
37
|
-
var ACCENT_BG_STYLE = { backgroundColor: ACCENT_COLOR };
|
|
38
|
-
function cn(...parts) {
|
|
39
|
-
return parts.filter(Boolean).join(" ");
|
|
40
|
-
}
|
|
41
|
-
function useAccentColor() {
|
|
42
|
-
const ref = useRef(null);
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
ref.current?.style.setProperty("color", ACCENT_COLOR, "important");
|
|
45
|
-
});
|
|
46
|
-
return ref;
|
|
47
|
-
}
|
|
48
|
-
function Accent({ children, className }) {
|
|
49
|
-
const ref = useAccentColor();
|
|
50
|
-
return /* @__PURE__ */ jsx("span", { ref, className, children });
|
|
51
|
-
}
|
|
52
|
-
function QuietMark({ size = "sm" }) {
|
|
53
|
-
const dimension = size === "md" ? 10 : 8;
|
|
54
|
-
return /* @__PURE__ */ jsx(
|
|
55
|
-
"span",
|
|
56
|
-
{
|
|
57
|
-
"aria-hidden": "true",
|
|
58
|
-
className: "inline-block shrink-0 rounded-full border",
|
|
59
|
-
style: { width: dimension, height: dimension, borderColor: ACCENT_BORDER }
|
|
60
|
-
}
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
function useWideLayout(minWidth = 1024) {
|
|
64
|
-
const [matches, setMatches] = useState(false);
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
67
|
-
const query = window.matchMedia(`(min-width: ${minWidth}px)`);
|
|
68
|
-
const update = () => setMatches(query.matches);
|
|
69
|
-
update();
|
|
70
|
-
query.addEventListener("change", update);
|
|
71
|
-
return () => query.removeEventListener("change", update);
|
|
72
|
-
}, [minWidth]);
|
|
73
|
-
return matches;
|
|
74
|
-
}
|
|
75
|
-
function UnreadDot(props) {
|
|
76
|
-
const tone = props.tone ?? "default";
|
|
77
|
-
if (!props.visible && !props.reserveSpace) return null;
|
|
78
|
-
const style = props.visible ? tone === "muted" ? { backgroundColor: ACCENT_BORDER } : ACCENT_BG_STYLE : { backgroundColor: "transparent" };
|
|
79
|
-
return /* @__PURE__ */ jsx("span", { className: "inline-block h-2 w-2 rounded-full shrink-0", style, "aria-label": props.visible ? "Unread attention item" : void 0 });
|
|
80
|
-
}
|
|
81
|
-
function ExternalLinkIcon() {
|
|
82
|
-
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", className: "h-3 w-3", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: [
|
|
83
|
-
/* @__PURE__ */ jsx("path", { d: "M6.5 3.5h-3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-3" }),
|
|
84
|
-
/* @__PURE__ */ jsx("path", { d: "M8.5 1.5h6v6" }),
|
|
85
|
-
/* @__PURE__ */ jsx("path", { d: "M14.5 1.5l-7 7" })
|
|
86
|
-
] });
|
|
87
|
-
}
|
|
88
|
-
function SkeletonLine(props) {
|
|
89
|
-
return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded bg-secondary/80", props.className), "aria-hidden": "true" });
|
|
90
|
-
}
|
|
91
|
-
function MessageCard(props) {
|
|
92
|
-
return /* @__PURE__ */ jsxs("div", { className: "border border-border bg-card p-4 shadow-sm", children: [
|
|
93
|
-
/* @__PURE__ */ jsx("div", { className: cn("text-sm font-medium", props.accent && "text-foreground"), children: props.title }),
|
|
94
|
-
/* @__PURE__ */ jsx("div", { className: "mt-1 text-sm text-muted-foreground", children: props.body })
|
|
95
|
-
] });
|
|
96
|
-
}
|
|
97
|
-
function WidgetLoadingState({ label }) {
|
|
98
|
-
return /* @__PURE__ */ jsxs("div", { className: "border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
99
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
100
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "mt-0.5 h-3 w-3 rounded-full" }),
|
|
101
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
|
|
102
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-3 w-20" }),
|
|
103
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-4 w-48" })
|
|
104
|
-
] }),
|
|
105
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-3 w-24" })
|
|
106
|
-
] }),
|
|
107
|
-
/* @__PURE__ */ jsx("div", { className: "sr-only", children: label })
|
|
108
|
-
] });
|
|
109
|
-
}
|
|
110
|
-
function PageLoadingState({ label }) {
|
|
111
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-5", "aria-label": label, children: [
|
|
112
|
-
/* @__PURE__ */ jsx("div", { className: "border border-border bg-card px-4 py-3 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
113
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-4 w-44" }),
|
|
114
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-8 w-28" })
|
|
115
|
-
] }) }),
|
|
116
|
-
/* @__PURE__ */ jsxs("div", { className: "border border-border bg-card shadow-sm", children: [
|
|
117
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-border px-4 py-3 sm:px-6", children: /* @__PURE__ */ jsx(SkeletonLine, { className: "h-4 w-36" }) }),
|
|
118
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-4 px-4 py-5 sm:px-6", children: [
|
|
119
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-3 w-16" }),
|
|
120
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-7 w-2/3" }),
|
|
121
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
122
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-6 w-24" }),
|
|
123
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-6 w-20" }),
|
|
124
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-6 w-16" })
|
|
125
|
-
] }),
|
|
126
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-10 w-56" })
|
|
127
|
-
] })
|
|
128
|
-
] }),
|
|
129
|
-
/* @__PURE__ */ jsxs("div", { className: "border border-border bg-card shadow-sm", children: [
|
|
130
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-border px-4 py-3 sm:px-6", children: /* @__PURE__ */ jsx(SkeletonLine, { className: "h-3 w-12" }) }),
|
|
131
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 py-4 sm:px-6", children: [
|
|
132
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-4 w-full" }),
|
|
133
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-4 w-5/6" }),
|
|
134
|
-
/* @__PURE__ */ jsx(SkeletonLine, { className: "h-4 w-2/3" })
|
|
135
|
-
] })
|
|
136
|
-
] })
|
|
137
|
-
] });
|
|
138
|
-
}
|
|
139
|
-
function StatusToast({ message }) {
|
|
140
|
-
return /* @__PURE__ */ jsx(
|
|
141
|
-
"div",
|
|
142
|
-
{
|
|
143
|
-
className: cn(
|
|
144
|
-
"pointer-events-none fixed bottom-6 left-1/2 z-50 -translate-x-1/2 transition-all duration-200",
|
|
145
|
-
message ? "translate-y-0 opacity-100" : "translate-y-2 opacity-0"
|
|
146
|
-
),
|
|
147
|
-
"aria-live": "polite",
|
|
148
|
-
"aria-atomic": "true",
|
|
149
|
-
children: message ? /* @__PURE__ */ jsx("div", { className: "min-w-72 max-w-md rounded-lg border border-border bg-card px-4 py-3 text-sm text-foreground shadow-lg", children: message }) : null
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
function Badge(props) {
|
|
154
|
-
return /* @__PURE__ */ jsx(
|
|
155
|
-
"span",
|
|
156
|
-
{
|
|
157
|
-
className: cn(
|
|
158
|
-
"inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap shrink-0",
|
|
159
|
-
props.className
|
|
160
|
-
),
|
|
161
|
-
style: props.style,
|
|
162
|
-
children: props.children
|
|
163
|
-
}
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
function ActionButton(props) {
|
|
167
|
-
const [hovered, setHovered] = useState(false);
|
|
168
|
-
const toneClass = props.tone === "primary" ? "bg-green-700 text-white hover:bg-green-600" : props.tone === "accent" ? "text-white" : props.tone === "danger" ? "bg-destructive text-white hover:bg-destructive/90 dark:bg-destructive/60" : "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50";
|
|
169
|
-
return /* @__PURE__ */ jsx(
|
|
170
|
-
"button",
|
|
171
|
-
{
|
|
172
|
-
type: "button",
|
|
173
|
-
onClick: props.onClick,
|
|
174
|
-
onMouseEnter: () => setHovered(true),
|
|
175
|
-
onMouseLeave: () => setHovered(false),
|
|
176
|
-
disabled: props.disabled,
|
|
177
|
-
className: cn(
|
|
178
|
-
"inline-flex h-8 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium",
|
|
179
|
-
"transition-[color,background-color,border-color,box-shadow,opacity,filter]",
|
|
180
|
-
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
181
|
-
"disabled:pointer-events-none disabled:opacity-50",
|
|
182
|
-
props.disabled && /…|\.\.\./.test(props.label) && "animate-pulse",
|
|
183
|
-
toneClass,
|
|
184
|
-
props.className
|
|
185
|
-
),
|
|
186
|
-
style: props.tone === "accent" ? { ...ACCENT_BG_STYLE, filter: hovered && !props.disabled ? "brightness(1.08)" : void 0 } : void 0,
|
|
187
|
-
children: props.label
|
|
188
|
-
}
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
function QueueMovementBadge({ movement }) {
|
|
192
|
-
if (!movement) return null;
|
|
193
|
-
return /* @__PURE__ */ jsxs(
|
|
194
|
-
"span",
|
|
195
|
-
{
|
|
196
|
-
className: "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium",
|
|
197
|
-
style: {
|
|
198
|
-
color: ACCENT_COLOR,
|
|
199
|
-
backgroundColor: ACCENT_BG,
|
|
200
|
-
borderColor: ACCENT_BORDER
|
|
201
|
-
},
|
|
202
|
-
children: [
|
|
203
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: movement === "up" ? "\u2191" : "\u2193" }),
|
|
204
|
-
movement === "up" ? "moved up" : "moved down"
|
|
205
|
-
]
|
|
206
|
-
}
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// src/ui/focus-model.tsx
|
|
211
|
-
import { usePluginData, usePluginStream } from "@paperclipai/plugin-sdk/ui";
|
|
212
|
-
import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
|
|
213
|
-
var ATTENTION_UPDATES_STREAM = "attention-updates";
|
|
214
|
-
function currentSurfaceBrand() {
|
|
215
|
-
return {
|
|
216
|
-
key: "focus",
|
|
217
|
-
wordmark: "Focus",
|
|
218
|
-
headingEmptyState: "No focus state yet.",
|
|
219
|
-
loadingLabel: "Loading Focus\u2026"
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
function pluginPagePath(companyPrefix) {
|
|
223
|
-
return companyPrefix ? `/${companyPrefix}/aperture` : "/aperture";
|
|
224
|
-
}
|
|
225
|
-
function formatRelativeTime(value) {
|
|
226
|
-
const timestamp = new Date(value).getTime();
|
|
227
|
-
if (Number.isNaN(timestamp)) return "recently";
|
|
228
|
-
const elapsedMs = timestamp - Date.now();
|
|
229
|
-
const elapsedMinutes = Math.round(elapsedMs / 6e4);
|
|
230
|
-
const formatter = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
231
|
-
if (Math.abs(elapsedMinutes) < 60) return formatter.format(elapsedMinutes, "minute");
|
|
232
|
-
const elapsedHours = Math.round(elapsedMinutes / 60);
|
|
233
|
-
if (Math.abs(elapsedHours) < 24) return formatter.format(elapsedHours, "hour");
|
|
234
|
-
const elapsedDays = Math.round(elapsedHours / 24);
|
|
235
|
-
return formatter.format(elapsedDays, "day");
|
|
236
|
-
}
|
|
237
|
-
function sourceLabel(frame) {
|
|
238
|
-
return frame.source?.label ?? "Paperclip";
|
|
239
|
-
}
|
|
240
|
-
function postureForSnapshot(snapshot) {
|
|
241
|
-
const current = snapshot.now;
|
|
242
|
-
if (current?.tone === "critical" || current?.consequence === "high" || snapshot.counts.now + snapshot.counts.next >= 3) {
|
|
243
|
-
return { glyph: "\u25CF", label: "busy" };
|
|
244
|
-
}
|
|
245
|
-
if (snapshot.counts.now > 0 || snapshot.counts.next > 0 || snapshot.counts.ambient > 0) {
|
|
246
|
-
return { glyph: "\u25D0", label: "elevated" };
|
|
247
|
-
}
|
|
248
|
-
return { glyph: "\u25CB", label: "calm" };
|
|
249
|
-
}
|
|
250
|
-
function actionableCount(snapshot) {
|
|
251
|
-
return (snapshot.now ? 1 : 0) + snapshot.next.length;
|
|
252
|
-
}
|
|
253
|
-
function actionableLabel(snapshot) {
|
|
254
|
-
const actionable = actionableCount(snapshot);
|
|
255
|
-
return actionable > 0 ? `${actionable} action${actionable === 1 ? "" : "s"}` : null;
|
|
256
|
-
}
|
|
257
|
-
function unreadCount(snapshot) {
|
|
258
|
-
return snapshot?.review?.unread.total ?? 0;
|
|
259
|
-
}
|
|
260
|
-
function useAttentionPolling(companyId, refreshers, intervalMs = 15e3) {
|
|
261
|
-
const refreshersRef = useRef2(refreshers);
|
|
262
|
-
refreshersRef.current = refreshers;
|
|
263
|
-
useEffect2(() => {
|
|
264
|
-
if (!companyId) return;
|
|
265
|
-
function refreshVisible() {
|
|
266
|
-
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
267
|
-
for (const refresh of refreshersRef.current) refresh();
|
|
268
|
-
}
|
|
269
|
-
const timer = window.setInterval(() => {
|
|
270
|
-
refreshVisible();
|
|
271
|
-
}, intervalMs);
|
|
272
|
-
function handleVisibilityChange() {
|
|
273
|
-
if (typeof document !== "undefined" && document.visibilityState === "visible") {
|
|
274
|
-
for (const refresh of refreshersRef.current) refresh();
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
278
|
-
return () => {
|
|
279
|
-
window.clearInterval(timer);
|
|
280
|
-
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
281
|
-
};
|
|
282
|
-
}, [companyId, intervalMs]);
|
|
283
|
-
}
|
|
284
|
-
function useQueueMovement(frames, ttlMs = 3500) {
|
|
285
|
-
const previousPositionsRef = useRef2({});
|
|
286
|
-
const timersRef = useRef2({});
|
|
287
|
-
const [movementById, setMovementById] = useState2({});
|
|
288
|
-
useEffect2(() => {
|
|
289
|
-
const nextPositions = Object.fromEntries(frames.map((frame, index) => [frame.interactionId, index]));
|
|
290
|
-
setMovementById((current) => {
|
|
291
|
-
const updates = {};
|
|
292
|
-
for (const frame of frames) {
|
|
293
|
-
const previousIndex = previousPositionsRef.current[frame.interactionId];
|
|
294
|
-
const nextIndex = nextPositions[frame.interactionId];
|
|
295
|
-
if (typeof previousIndex !== "number" || previousIndex === nextIndex) continue;
|
|
296
|
-
updates[frame.interactionId] = nextIndex < previousIndex ? "up" : "down";
|
|
297
|
-
}
|
|
298
|
-
if (Object.keys(updates).length === 0) return current;
|
|
299
|
-
const merged = { ...current, ...updates };
|
|
300
|
-
for (const [interactionId, movement] of Object.entries(updates)) {
|
|
301
|
-
window.clearTimeout(timersRef.current[interactionId]);
|
|
302
|
-
timersRef.current[interactionId] = window.setTimeout(() => {
|
|
303
|
-
setMovementById((active) => {
|
|
304
|
-
if (active[interactionId] !== movement) return active;
|
|
305
|
-
const next = { ...active };
|
|
306
|
-
delete next[interactionId];
|
|
307
|
-
return next;
|
|
308
|
-
});
|
|
309
|
-
delete timersRef.current[interactionId];
|
|
310
|
-
}, ttlMs);
|
|
311
|
-
}
|
|
312
|
-
return merged;
|
|
313
|
-
});
|
|
314
|
-
previousPositionsRef.current = nextPositions;
|
|
315
|
-
return () => {
|
|
316
|
-
for (const timer of Object.values(timersRef.current)) window.clearTimeout(timer);
|
|
317
|
-
};
|
|
318
|
-
}, [frames, ttlMs]);
|
|
319
|
-
return movementById;
|
|
320
|
-
}
|
|
321
|
-
function useAttentionModel(companyId) {
|
|
322
|
-
const displayQuery = usePluginData("attention-display", companyId ? { companyId } : void 0);
|
|
323
|
-
const updates = usePluginStream(
|
|
324
|
-
ATTENTION_UPDATES_STREAM,
|
|
325
|
-
companyId ? { companyId } : void 0
|
|
326
|
-
);
|
|
327
|
-
useAttentionPolling(companyId, [displayQuery.refresh]);
|
|
328
|
-
useEffect2(() => {
|
|
329
|
-
if (!companyId || !updates.lastEvent) return;
|
|
330
|
-
displayQuery.refresh();
|
|
331
|
-
}, [companyId, updates.lastEvent?.updatedAt, updates.lastEvent?.eventType]);
|
|
332
|
-
return useMemo(() => ({
|
|
333
|
-
snapshot: displayQuery.data?.snapshot ?? null,
|
|
334
|
-
review: displayQuery.data?.reviewState ?? null,
|
|
335
|
-
loading: displayQuery.loading,
|
|
336
|
-
error: displayQuery.error,
|
|
337
|
-
refresh() {
|
|
338
|
-
displayQuery.refresh();
|
|
339
|
-
}
|
|
340
|
-
}), [displayQuery]);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// src/aperture/contracts.ts
|
|
344
|
-
function asRecord(value) {
|
|
345
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
346
|
-
}
|
|
347
|
-
function readString(value) {
|
|
348
|
-
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
349
|
-
}
|
|
350
|
-
function readNullableString(value) {
|
|
351
|
-
if (value === null) return null;
|
|
352
|
-
return readString(value);
|
|
353
|
-
}
|
|
354
|
-
function readStringArray(value) {
|
|
355
|
-
if (!Array.isArray(value)) return void 0;
|
|
356
|
-
const entries = value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
357
|
-
return entries.length > 0 ? entries : void 0;
|
|
358
|
-
}
|
|
359
|
-
function readRelationHints(value) {
|
|
360
|
-
if (!Array.isArray(value)) return void 0;
|
|
361
|
-
const hints = value.filter((entry) => {
|
|
362
|
-
if (typeof entry !== "object" || entry === null) return false;
|
|
363
|
-
const hint = entry;
|
|
364
|
-
return hint.kind === "same_issue" || hint.kind === "resolves" || hint.kind === "supersedes" || hint.kind === "repeats" || hint.kind === "escalates";
|
|
365
|
-
});
|
|
366
|
-
return hints.length > 0 ? hints : void 0;
|
|
367
|
-
}
|
|
368
|
-
function readFocusMetadata(frame) {
|
|
369
|
-
const metadata = asRecord(frame.metadata);
|
|
370
|
-
const attention = asRecord(metadata?.attention);
|
|
371
|
-
const semantic = asRecord(metadata?.semantic);
|
|
372
|
-
const episode = asRecord(metadata?.episode);
|
|
373
|
-
const issueIntelligence = asRecord(metadata?.issueIntelligence);
|
|
374
|
-
return {
|
|
375
|
-
entityType: readString(metadata?.entityType),
|
|
376
|
-
issueStatus: readString(metadata?.issueStatus),
|
|
377
|
-
issuePriority: readString(metadata?.issuePriority),
|
|
378
|
-
approvalStatus: readString(metadata?.approvalStatus),
|
|
379
|
-
approvalType: readString(metadata?.approvalType),
|
|
380
|
-
agentStatus: readString(metadata?.agentStatus),
|
|
381
|
-
pauseReason: readNullableString(metadata?.pauseReason),
|
|
382
|
-
liveReconciled: typeof metadata?.liveReconciled === "boolean" ? metadata.liveReconciled : void 0,
|
|
383
|
-
activityPath: readString(metadata?.activityPath),
|
|
384
|
-
attention: attention ? {
|
|
385
|
-
rationale: readStringArray(attention.rationale)
|
|
386
|
-
} : void 0,
|
|
387
|
-
semantic: semantic ? {
|
|
388
|
-
confidence: semantic.confidence === "low" || semantic.confidence === "medium" || semantic.confidence === "high" ? semantic.confidence : void 0,
|
|
389
|
-
relationHints: readRelationHints(semantic.relationHints)
|
|
390
|
-
} : void 0,
|
|
391
|
-
episode: episode ? {
|
|
392
|
-
size: typeof episode.size === "number" ? episode.size : void 0,
|
|
393
|
-
state: readString(episode.state)
|
|
394
|
-
} : void 0,
|
|
395
|
-
issueIntelligence: issueIntelligence ? {
|
|
396
|
-
matchedRuleIds: readStringArray(issueIntelligence.matchedRuleIds),
|
|
397
|
-
owner: readNullableString(issueIntelligence.owner),
|
|
398
|
-
ownerKind: issueIntelligence.ownerKind === "agent" || issueIntelligence.ownerKind === "human" ? issueIntelligence.ownerKind : issueIntelligence.ownerKind === null ? null : void 0,
|
|
399
|
-
blockingTarget: readNullableString(issueIntelligence.blockingTarget)
|
|
400
|
-
} : void 0
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// src/aperture/attention-context.ts
|
|
405
|
-
var ATTENTION_CONTEXT_IDS = {
|
|
406
|
-
approvalType: "approval-type",
|
|
407
|
-
requestedAmount: "requested-amount",
|
|
408
|
-
budgetReason: "budget-reason",
|
|
409
|
-
decisionContext: "decision-context",
|
|
410
|
-
linkedIssues: "linked-issues",
|
|
411
|
-
needsActionFrom: "needs-action-from",
|
|
412
|
-
blocksTarget: "blocks-target",
|
|
413
|
-
recommendedMove: "recommended-move",
|
|
414
|
-
issueStatus: "issue-status",
|
|
415
|
-
issuePriority: "issue-priority",
|
|
416
|
-
latestComment: "latest-comment",
|
|
417
|
-
agentStatus: "agent-status",
|
|
418
|
-
pauseReason: "pause-reason",
|
|
419
|
-
agentTitle: "agent-title"
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
// src/aperture/task-ref.ts
|
|
423
|
-
function decodeComponent(value) {
|
|
424
|
-
try {
|
|
425
|
-
const decoded = decodeURIComponent(value);
|
|
426
|
-
return decoded.trim().length > 0 ? decoded : null;
|
|
427
|
-
} catch {
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
function parseTaskId(taskId) {
|
|
432
|
-
if (typeof taskId !== "string") return null;
|
|
433
|
-
const separatorIndex = taskId.indexOf(":");
|
|
434
|
-
if (separatorIndex <= 0 || separatorIndex >= taskId.length - 1) return null;
|
|
435
|
-
const kind = decodeComponent(taskId.slice(0, separatorIndex));
|
|
436
|
-
const id = decodeComponent(taskId.slice(separatorIndex + 1));
|
|
437
|
-
if (!kind || !id) return null;
|
|
438
|
-
return { kind, id };
|
|
439
|
-
}
|
|
440
|
-
function taskKind(taskId) {
|
|
441
|
-
return parseTaskId(taskId)?.kind ?? null;
|
|
442
|
-
}
|
|
443
|
-
function taskEntityId(taskId) {
|
|
444
|
-
return parseTaskId(taskId)?.id ?? null;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// src/aperture/issue-intelligence.ts
|
|
448
|
-
function issueBlocksTargetLine(target) {
|
|
449
|
-
return `Blocks ${target} until the review lands.`;
|
|
450
|
-
}
|
|
451
|
-
function issueNeedsActionFromLine(owner) {
|
|
452
|
-
return `Needs action from ${owner}.`;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// src/ui/frame-helpers.tsx
|
|
456
|
-
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
457
|
-
function contextValue(frame, id) {
|
|
458
|
-
const item = frame.context?.items?.find((entry) => entry.id === id);
|
|
459
|
-
return typeof item?.value === "string" && item.value.trim().length > 0 ? item.value : void 0;
|
|
460
|
-
}
|
|
461
|
-
function requestedAmount(frame) {
|
|
462
|
-
return contextValue(frame, ATTENTION_CONTEXT_IDS.requestedAmount);
|
|
463
|
-
}
|
|
464
|
-
function budgetReason(frame) {
|
|
465
|
-
return contextValue(frame, ATTENTION_CONTEXT_IDS.budgetReason);
|
|
466
|
-
}
|
|
467
|
-
function recommendedMoveValue(frame) {
|
|
468
|
-
return contextValue(frame, ATTENTION_CONTEXT_IDS.recommendedMove);
|
|
469
|
-
}
|
|
470
|
-
function actionOwnerValue(frame) {
|
|
471
|
-
return contextValue(frame, ATTENTION_CONTEXT_IDS.needsActionFrom);
|
|
472
|
-
}
|
|
473
|
-
function blockingTargetValue(frame) {
|
|
474
|
-
return contextValue(frame, ATTENTION_CONTEXT_IDS.blocksTarget);
|
|
475
|
-
}
|
|
476
|
-
function impactLabel(frame) {
|
|
477
|
-
return `${frame.consequence} impact`;
|
|
478
|
-
}
|
|
479
|
-
function operatorDriverLabel(value) {
|
|
480
|
-
switch (value) {
|
|
481
|
-
case "in_review":
|
|
482
|
-
return "review required";
|
|
483
|
-
case "pending_approval":
|
|
484
|
-
return "approval required";
|
|
485
|
-
default:
|
|
486
|
-
return value.replace(/_/g, " ");
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
function driverLabel(frame) {
|
|
490
|
-
if (isBudgetOverride(frame)) return "budget stop";
|
|
491
|
-
const metadata = readFocusMetadata(frame);
|
|
492
|
-
const issueStatus = metadata.issueStatus;
|
|
493
|
-
if (typeof issueStatus === "string" && issueStatus.trim().length > 0) {
|
|
494
|
-
return operatorDriverLabel(issueStatus);
|
|
495
|
-
}
|
|
496
|
-
const pauseReason = metadata.pauseReason;
|
|
497
|
-
if (typeof pauseReason === "string" && pauseReason.trim().length > 0) {
|
|
498
|
-
return operatorDriverLabel(pauseReason);
|
|
499
|
-
}
|
|
500
|
-
const agentStatus = metadata.agentStatus;
|
|
501
|
-
if (typeof agentStatus === "string" && agentStatus.trim().length > 0) {
|
|
502
|
-
return operatorDriverLabel(agentStatus);
|
|
503
|
-
}
|
|
504
|
-
return null;
|
|
505
|
-
}
|
|
506
|
-
function driverBadgeStyle() {
|
|
507
|
-
return {
|
|
508
|
-
className: "border-transparent",
|
|
509
|
-
style: { color: ACCENT_COLOR, backgroundColor: ACCENT_BG, borderColor: ACCENT_BORDER }
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
function requestDescriptor(frame) {
|
|
513
|
-
const driver = driverLabel(frame);
|
|
514
|
-
return driver ? `${sourceLabel(frame)} \xB7 ${driver}` : sourceLabel(frame);
|
|
515
|
-
}
|
|
516
|
-
function judgmentLine(frame, lane) {
|
|
517
|
-
if (frame.provenance?.whyNow) return frame.provenance.whyNow;
|
|
518
|
-
return genericJudgmentLine(frame, lane);
|
|
519
|
-
}
|
|
520
|
-
function nextPrimaryText(frame) {
|
|
521
|
-
const recommendedMove = recommendedMoveValue(frame);
|
|
522
|
-
if (recommendedMove) return recommendedMove;
|
|
523
|
-
const summary = frame.summary?.trim();
|
|
524
|
-
if (summary) return summary;
|
|
525
|
-
const fallback = judgmentLine(frame, "next");
|
|
526
|
-
return fallback === GENERIC_QUEUED_JUDGMENT ? null : fallback;
|
|
527
|
-
}
|
|
528
|
-
function supportingLine(frame, lane) {
|
|
529
|
-
const owner = actionOwnerValue(frame);
|
|
530
|
-
const target = blockingTargetValue(frame);
|
|
531
|
-
if (target && lane === "now" && entityTypeFromFrame(frame) === "issue" && driverLabel(frame) === "review required") {
|
|
532
|
-
return issueBlocksTargetLine(target);
|
|
533
|
-
}
|
|
534
|
-
if (owner && lane === "now" && entityTypeFromFrame(frame) === "issue") {
|
|
535
|
-
return issueNeedsActionFromLine(owner);
|
|
536
|
-
}
|
|
537
|
-
if (lane === "now") {
|
|
538
|
-
const recommendedMove = recommendedMoveValue(frame)?.trim();
|
|
539
|
-
const summary = frame.summary?.trim();
|
|
540
|
-
const judgment2 = judgmentLine(frame, lane).trim();
|
|
541
|
-
if (recommendedMove && summary && summary !== recommendedMove && summary !== judgment2) {
|
|
542
|
-
return summary;
|
|
543
|
-
}
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
const judgment = judgmentLine(frame, lane);
|
|
547
|
-
return judgment.trim().length > 0 ? judgment : null;
|
|
548
|
-
}
|
|
549
|
-
function approvalIdForFrame(frame) {
|
|
550
|
-
const taskRef = parseTaskId(frame.taskId);
|
|
551
|
-
return taskRef?.kind === "approval" ? taskRef.id : null;
|
|
552
|
-
}
|
|
553
|
-
function entityIdFromFrame(frame) {
|
|
554
|
-
return taskEntityId(frame.taskId);
|
|
555
|
-
}
|
|
556
|
-
function entityTypeFromFrame(frame) {
|
|
557
|
-
return taskKind(frame.taskId);
|
|
558
|
-
}
|
|
559
|
-
function itemHref(frame, companyPrefix) {
|
|
560
|
-
const entityType = entityTypeFromFrame(frame);
|
|
561
|
-
const entityId = entityIdFromFrame(frame);
|
|
562
|
-
if (!entityType || !entityId || !companyPrefix) return null;
|
|
563
|
-
const pluralType = entityType === "run" ? "runs" : entityType === "approval" ? "approvals" : entityType === "issue" ? "issues" : entityType === "agent" ? "agents" : null;
|
|
564
|
-
if (!pluralType) return null;
|
|
565
|
-
return `/${companyPrefix}/${pluralType}/${entityId}`;
|
|
566
|
-
}
|
|
567
|
-
function costsHref(companyPrefix) {
|
|
568
|
-
return companyPrefix ? `/${companyPrefix}/costs` : null;
|
|
569
|
-
}
|
|
570
|
-
function activityHref(frame, companyPrefix) {
|
|
571
|
-
if (!companyPrefix) return null;
|
|
572
|
-
const metadata = readFocusMetadata(frame);
|
|
573
|
-
return metadata.activityPath ? `/${companyPrefix}/${metadata.activityPath}` : null;
|
|
574
|
-
}
|
|
575
|
-
function primaryLinkLabel(frame) {
|
|
576
|
-
const entityType = entityTypeFromFrame(frame);
|
|
577
|
-
switch (entityType) {
|
|
578
|
-
case "approval":
|
|
579
|
-
return "Open approval";
|
|
580
|
-
case "issue":
|
|
581
|
-
return "Open issue";
|
|
582
|
-
case "run":
|
|
583
|
-
return "Open run";
|
|
584
|
-
case "agent":
|
|
585
|
-
return "Open agent";
|
|
586
|
-
default:
|
|
587
|
-
return "Open in Paperclip";
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
function responseKind(frame, lane) {
|
|
591
|
-
if (approvalIdForFrame(frame) && (frame.responseSpec?.kind === "approval" || frame.mode === "approval")) {
|
|
592
|
-
return "approval";
|
|
593
|
-
}
|
|
594
|
-
if (lane !== "ambient" && (frame.responseSpec?.kind === "acknowledge" || frame.mode === "status")) {
|
|
595
|
-
return "acknowledge";
|
|
596
|
-
}
|
|
597
|
-
return "none";
|
|
598
|
-
}
|
|
599
|
-
function isFrameUnreadInSnapshot(frame, snapshot) {
|
|
600
|
-
const seenAt = snapshot.review?.lastSeenAt;
|
|
601
|
-
if (!seenAt) return true;
|
|
602
|
-
return frameUpdatedAt(frame, snapshot.updatedAt).localeCompare(seenAt) > 0;
|
|
603
|
-
}
|
|
604
|
-
function compactTitle(frame) {
|
|
605
|
-
return frame.title.trim().length > 0 ? frame.title : "Untitled frame";
|
|
606
|
-
}
|
|
607
|
-
var ENTITY_PATTERN = /\b([A-Z][A-Z0-9]+-\d+)\b|"([^"]+)"/g;
|
|
608
|
-
function HighlightedTitle({ text }) {
|
|
609
|
-
const parts = [];
|
|
610
|
-
let lastIndex = 0;
|
|
611
|
-
let match;
|
|
612
|
-
const re = new RegExp(ENTITY_PATTERN.source, "g");
|
|
613
|
-
while ((match = re.exec(text)) !== null) {
|
|
614
|
-
if (match.index > lastIndex) {
|
|
615
|
-
parts.push(text.slice(lastIndex, match.index));
|
|
616
|
-
}
|
|
617
|
-
const token = match[1] ?? match[2] ?? match[0];
|
|
618
|
-
parts.push(
|
|
619
|
-
/* @__PURE__ */ jsx2(Accent, { children: match[1] ? token : `"${token}"` }, match.index)
|
|
620
|
-
);
|
|
621
|
-
lastIndex = re.lastIndex;
|
|
622
|
-
}
|
|
623
|
-
if (lastIndex < text.length) {
|
|
624
|
-
parts.push(text.slice(lastIndex));
|
|
625
|
-
}
|
|
626
|
-
return /* @__PURE__ */ jsx2(Fragment, { children: parts });
|
|
627
|
-
}
|
|
628
|
-
function renderTitle(frame) {
|
|
629
|
-
return /* @__PURE__ */ jsx2(HighlightedTitle, { text: compactTitle(frame) });
|
|
630
|
-
}
|
|
631
|
-
function visibleContextItems(frame) {
|
|
632
|
-
return (frame.context?.items ?? []).filter((item) => item.id !== ATTENTION_CONTEXT_IDS.recommendedMove);
|
|
633
|
-
}
|
|
634
|
-
function isIssueFrame(frame) {
|
|
635
|
-
return entityTypeFromFrame(frame) === "issue";
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// src/aperture/explainability.ts
|
|
639
|
-
function readStringArray2(value) {
|
|
640
|
-
if (!Array.isArray(value)) return [];
|
|
641
|
-
return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
642
|
-
}
|
|
643
|
-
function readSemanticConfidence(frame) {
|
|
644
|
-
const confidence = readFocusMetadata(frame).semantic?.confidence;
|
|
645
|
-
return confidence === "low" || confidence === "medium" || confidence === "high" ? confidence : null;
|
|
646
|
-
}
|
|
647
|
-
function readSemanticRelationHints(frame) {
|
|
648
|
-
return readFocusMetadata(frame).semantic?.relationHints ?? [];
|
|
649
|
-
}
|
|
650
|
-
function relationHintLabel(hint) {
|
|
651
|
-
switch (hint.kind) {
|
|
652
|
-
case "same_issue":
|
|
653
|
-
return "Part of the same thread";
|
|
654
|
-
case "resolves":
|
|
655
|
-
return "Resolves an earlier blocker";
|
|
656
|
-
case "supersedes":
|
|
657
|
-
return "Moves the request forward";
|
|
658
|
-
case "repeats":
|
|
659
|
-
return "Repeats an earlier ask";
|
|
660
|
-
case "escalates":
|
|
661
|
-
return "Raises the urgency";
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
function laneReason(lane) {
|
|
665
|
-
switch (lane) {
|
|
666
|
-
case "now":
|
|
667
|
-
return "This is the most urgent item in the queue right now.";
|
|
668
|
-
case "next":
|
|
669
|
-
return "This is staged behind the current top item.";
|
|
670
|
-
case "ambient":
|
|
671
|
-
return "This is visible for awareness without needing action yet.";
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
function continuitySummary(frame) {
|
|
675
|
-
const episode = readFocusMetadata(frame).episode;
|
|
676
|
-
const size = episode?.size ?? null;
|
|
677
|
-
const state = episode?.state?.replace(/_/g, " ") ?? null;
|
|
678
|
-
if (!size || size <= 1) return null;
|
|
679
|
-
if (state) return `Part of a ${state} thread with ${size} related interactions.`;
|
|
680
|
-
return `Part of a thread with ${size} related interactions.`;
|
|
681
|
-
}
|
|
682
|
-
function frameSignals(frame) {
|
|
683
|
-
const rationale = readFocusMetadata(frame).attention?.rationale ?? [];
|
|
684
|
-
const factors = readStringArray2(frame.provenance?.factors);
|
|
685
|
-
return [.../* @__PURE__ */ new Set([...rationale.length > 0 ? rationale : factors, ...factors])];
|
|
686
|
-
}
|
|
687
|
-
function signalStrengthLabel(confidence) {
|
|
688
|
-
return `${confidence} confidence`;
|
|
689
|
-
}
|
|
690
|
-
function explainFrame(frame, lane) {
|
|
691
|
-
const relationLabels = [...new Set(readSemanticRelationHints(frame).map(relationHintLabel))];
|
|
692
|
-
return {
|
|
693
|
-
whyNow: frame.provenance?.whyNow ?? frame.summary ?? null,
|
|
694
|
-
laneReason: laneReason(lane),
|
|
695
|
-
signalStrength: readSemanticConfidence(frame),
|
|
696
|
-
signals: frameSignals(frame),
|
|
697
|
-
relationLabels,
|
|
698
|
-
continuity: continuitySummary(frame)
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// src/ui/frame-explainability.tsx
|
|
703
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
704
|
-
function ContextItems({ frame }) {
|
|
705
|
-
const items = visibleContextItems(frame);
|
|
706
|
-
if (items.length === 0) return null;
|
|
707
|
-
return /* @__PURE__ */ jsx3("div", { className: "grid gap-2 md:grid-cols-2", children: items.map((item) => /* @__PURE__ */ jsxs2(
|
|
708
|
-
"div",
|
|
709
|
-
{
|
|
710
|
-
className: cn(
|
|
711
|
-
"border border-border bg-secondary/50 px-3 py-2",
|
|
712
|
-
item.id === ATTENTION_CONTEXT_IDS.latestComment && "md:col-span-2"
|
|
713
|
-
),
|
|
714
|
-
children: [
|
|
715
|
-
/* @__PURE__ */ jsx3("div", { className: "text-xs font-medium text-muted-foreground", children: item.label }),
|
|
716
|
-
/* @__PURE__ */ jsx3("div", { className: "mt-1 text-sm text-foreground", children: item.value ?? "Available" })
|
|
717
|
-
]
|
|
718
|
-
},
|
|
719
|
-
item.id
|
|
720
|
-
)) });
|
|
721
|
-
}
|
|
722
|
-
function ExplainabilityBadges(props) {
|
|
723
|
-
if (props.values.length === 0) return null;
|
|
724
|
-
return /* @__PURE__ */ jsx3("div", { className: "flex flex-wrap items-center gap-2", children: props.values.map((value) => /* @__PURE__ */ jsx3(
|
|
725
|
-
Badge,
|
|
726
|
-
{
|
|
727
|
-
className: props.accent ? "border-transparent" : "border-border bg-background/70 text-foreground/80",
|
|
728
|
-
style: props.accent ? { color: ACCENT_COLOR, backgroundColor: ACCENT_BG, borderColor: ACCENT_BORDER } : void 0,
|
|
729
|
-
children: value
|
|
730
|
-
},
|
|
731
|
-
value
|
|
732
|
-
)) });
|
|
733
|
-
}
|
|
734
|
-
function InlineExplainability(props) {
|
|
735
|
-
const explanation = explainFrame(props.frame, props.lane);
|
|
736
|
-
const chips = [
|
|
737
|
-
...explanation.signalStrength ? [signalStrengthLabel(explanation.signalStrength)] : [],
|
|
738
|
-
...explanation.signals.slice(0, 2),
|
|
739
|
-
...explanation.relationLabels.slice(0, 1)
|
|
740
|
-
];
|
|
741
|
-
const label = props.lane === "next" ? "Why next" : props.lane === "ambient" ? "Why ambient" : "Why now";
|
|
742
|
-
const whyNow = explanation.whyNow ?? judgmentLine(props.frame, props.lane);
|
|
743
|
-
const primaryLine = props.lane === "now" ? props.preferLaneReason ? explanation.laneReason : whyNow : whyNow;
|
|
744
|
-
const secondaryLine = props.lane === "now" ? !props.preferLaneReason && whyNow !== explanation.laneReason ? explanation.laneReason : null : explanation.laneReason;
|
|
745
|
-
return /* @__PURE__ */ jsxs2("div", { className: "space-y-2 border-l-2 pl-4", style: { borderColor: ACCENT_COLOR }, children: [
|
|
746
|
-
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: label }),
|
|
747
|
-
/* @__PURE__ */ jsx3("p", { className: "max-w-3xl text-sm leading-relaxed text-foreground/90", children: primaryLine }),
|
|
748
|
-
secondaryLine ? /* @__PURE__ */ jsx3("p", { className: "text-xs leading-relaxed text-muted-foreground", children: secondaryLine }) : null,
|
|
749
|
-
chips.length > 0 ? /* @__PURE__ */ jsx3(ExplainabilityBadges, { values: chips, accent: true }) : null
|
|
750
|
-
] });
|
|
751
|
-
}
|
|
752
|
-
function ExplainabilityPanel(props) {
|
|
753
|
-
const explanation = explainFrame(props.frame, props.lane);
|
|
754
|
-
const strength = !props.detailOnly && explanation.signalStrength ? signalStrengthLabel(explanation.signalStrength) : null;
|
|
755
|
-
const signalValues = props.detailOnly ? explanation.signals.slice(2) : explanation.signals;
|
|
756
|
-
const relationValues = props.detailOnly ? explanation.relationLabels.slice(1) : explanation.relationLabels;
|
|
757
|
-
const reasoningLabel = props.lane === "now" ? "Reasoning" : props.lane === "next" ? "Why it sits here" : "Why it stays quiet";
|
|
758
|
-
if (props.detailOnly && signalValues.length === 0 && relationValues.length === 0 && !explanation.continuity) {
|
|
759
|
-
return null;
|
|
760
|
-
}
|
|
761
|
-
return /* @__PURE__ */ jsxs2("div", { className: "space-y-4", children: [
|
|
762
|
-
!props.detailOnly ? /* @__PURE__ */ jsxs2("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem]", children: [
|
|
763
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
764
|
-
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: reasoningLabel }),
|
|
765
|
-
/* @__PURE__ */ jsx3("p", { className: "mt-1 text-sm leading-relaxed text-muted-foreground", children: explanation.laneReason })
|
|
766
|
-
] }),
|
|
767
|
-
strength ? /* @__PURE__ */ jsxs2("div", { children: [
|
|
768
|
-
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Confidence" }),
|
|
769
|
-
/* @__PURE__ */ jsx3("p", { className: "mt-1 text-sm text-foreground/90", children: strength })
|
|
770
|
-
] }) : null
|
|
771
|
-
] }) : null,
|
|
772
|
-
signalValues.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
773
|
-
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: props.detailOnly ? "More signals" : "Signals" }),
|
|
774
|
-
/* @__PURE__ */ jsx3(ExplainabilityBadges, { values: signalValues })
|
|
775
|
-
] }) : null,
|
|
776
|
-
relationValues.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
777
|
-
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: props.detailOnly ? "More thread context" : "Thread context" }),
|
|
778
|
-
/* @__PURE__ */ jsx3(ExplainabilityBadges, { values: relationValues, accent: true })
|
|
779
|
-
] }) : null,
|
|
780
|
-
explanation.continuity ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
781
|
-
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Related activity" }),
|
|
782
|
-
/* @__PURE__ */ jsx3("p", { className: "text-sm leading-relaxed text-muted-foreground", children: explanation.continuity })
|
|
783
|
-
] }) : null
|
|
784
|
-
] });
|
|
785
|
-
}
|
|
786
|
-
function ExplainabilityStrip(props) {
|
|
787
|
-
return /* @__PURE__ */ jsx3("div", { className: "space-y-2 border-t border-border/60 pt-3", children: /* @__PURE__ */ jsx3(InlineExplainability, { frame: props.frame, lane: props.lane }) });
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// src/ui/issue-comment-composer.tsx
|
|
791
|
-
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
792
|
-
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
793
|
-
function IssueCommentComposer(props) {
|
|
794
|
-
const [open, setOpen] = useState3(false);
|
|
795
|
-
const [body, setBody] = useState3("");
|
|
796
|
-
const textareaRef = useRef3(null);
|
|
797
|
-
const issueFrame = isIssueFrame(props.frame);
|
|
798
|
-
const isPending = props.pendingId === props.frame.id;
|
|
799
|
-
useEffect3(() => {
|
|
800
|
-
setOpen(false);
|
|
801
|
-
setBody("");
|
|
802
|
-
}, [props.frame.id]);
|
|
803
|
-
useEffect3(() => {
|
|
804
|
-
if (open && issueFrame) textareaRef.current?.focus();
|
|
805
|
-
}, [open, issueFrame]);
|
|
806
|
-
if (!issueFrame) return null;
|
|
807
|
-
async function submit() {
|
|
808
|
-
const nextBody = body.trim();
|
|
809
|
-
if (!nextBody) return;
|
|
810
|
-
await props.onComment(props.frame, nextBody);
|
|
811
|
-
setBody("");
|
|
812
|
-
setOpen(false);
|
|
813
|
-
}
|
|
814
|
-
if (!open) {
|
|
815
|
-
const label = props.triggerLabel ?? "Leave comment";
|
|
816
|
-
const triggerTone = props.triggerTone ?? "link";
|
|
817
|
-
if (triggerTone === "link") {
|
|
818
|
-
return /* @__PURE__ */ jsx4(
|
|
819
|
-
"button",
|
|
820
|
-
{
|
|
821
|
-
type: "button",
|
|
822
|
-
className: "text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",
|
|
823
|
-
onClick: () => setOpen(true),
|
|
824
|
-
children: label
|
|
825
|
-
}
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
return /* @__PURE__ */ jsx4(
|
|
829
|
-
ActionButton,
|
|
830
|
-
{
|
|
831
|
-
label,
|
|
832
|
-
tone: triggerTone,
|
|
833
|
-
className: props.triggerFullWidth ? "w-full" : void 0,
|
|
834
|
-
disabled: isPending,
|
|
835
|
-
onClick: () => setOpen(true)
|
|
836
|
-
}
|
|
837
|
-
);
|
|
838
|
-
}
|
|
839
|
-
return /* @__PURE__ */ jsxs3("div", { className: "w-full space-y-2 rounded-md border border-border bg-secondary/40 p-3", children: [
|
|
840
|
-
/* @__PURE__ */ jsxs3("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: [
|
|
841
|
-
"Commenting on ",
|
|
842
|
-
compactTitle(props.frame)
|
|
843
|
-
] }),
|
|
844
|
-
/* @__PURE__ */ jsx4("div", { className: "text-[11px] text-muted-foreground", children: "Posts to the issue thread without leaving Focus." }),
|
|
845
|
-
/* @__PURE__ */ jsx4(
|
|
846
|
-
"textarea",
|
|
847
|
-
{
|
|
848
|
-
ref: textareaRef,
|
|
849
|
-
value: body,
|
|
850
|
-
onChange: (event) => setBody(event.target.value),
|
|
851
|
-
onKeyDown: (event) => {
|
|
852
|
-
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
853
|
-
event.preventDefault();
|
|
854
|
-
void submit();
|
|
855
|
-
}
|
|
856
|
-
if (event.key === "Escape") {
|
|
857
|
-
event.preventDefault();
|
|
858
|
-
setOpen(false);
|
|
859
|
-
setBody("");
|
|
860
|
-
}
|
|
861
|
-
},
|
|
862
|
-
rows: 3,
|
|
863
|
-
placeholder: "Add a short operator note back to the issue\u2026",
|
|
864
|
-
className: "w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground shadow-xs outline-none transition focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"
|
|
865
|
-
}
|
|
866
|
-
),
|
|
867
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-end gap-2", children: [
|
|
868
|
-
/* @__PURE__ */ jsx4(
|
|
869
|
-
ActionButton,
|
|
870
|
-
{
|
|
871
|
-
label: "Cancel",
|
|
872
|
-
tone: "secondary",
|
|
873
|
-
disabled: isPending,
|
|
874
|
-
onClick: () => {
|
|
875
|
-
setOpen(false);
|
|
876
|
-
setBody("");
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
),
|
|
880
|
-
/* @__PURE__ */ jsx4(
|
|
881
|
-
ActionButton,
|
|
882
|
-
{
|
|
883
|
-
label: isPending ? "Posting\u2026" : "Post comment",
|
|
884
|
-
tone: "accent",
|
|
885
|
-
disabled: isPending || body.trim().length === 0,
|
|
886
|
-
onClick: () => void submit()
|
|
887
|
-
}
|
|
888
|
-
)
|
|
889
|
-
] })
|
|
890
|
-
] });
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
// src/ui/use-focus-page-actions.ts
|
|
894
|
-
import { usePluginAction } from "@paperclipai/plugin-sdk/ui";
|
|
895
|
-
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
896
|
-
|
|
897
|
-
// src/ui/interaction-copy.ts
|
|
898
|
-
function actionErrorMessage(error, fallback) {
|
|
899
|
-
return error instanceof Error ? error.message : fallback;
|
|
900
|
-
}
|
|
901
|
-
function acknowledgeSuccessMessage(title) {
|
|
902
|
-
return `Acknowledged ${title}.`;
|
|
903
|
-
}
|
|
904
|
-
function acknowledgeFailureMessage(error) {
|
|
905
|
-
return actionErrorMessage(error, "Failed to acknowledge frame.");
|
|
906
|
-
}
|
|
907
|
-
function issueFrameUnsupportedMessage() {
|
|
908
|
-
return "This frame is not backed by a Paperclip issue.";
|
|
909
|
-
}
|
|
910
|
-
function commentSuccessMessage(title) {
|
|
911
|
-
return `Posted a comment on ${title}.`;
|
|
912
|
-
}
|
|
913
|
-
function commentFailureMessage(error) {
|
|
914
|
-
return actionErrorMessage(error, "Failed to post comment.");
|
|
915
|
-
}
|
|
916
|
-
function approvalFrameUnsupportedMessage() {
|
|
917
|
-
return "This frame is not backed by a Paperclip approval.";
|
|
918
|
-
}
|
|
919
|
-
function approvalDecisionSuccessMessage(title, decision) {
|
|
920
|
-
return `${decision === "approve" ? "Approved" : "Rejected"} ${title}.`;
|
|
921
|
-
}
|
|
922
|
-
function approvalDecisionFailureMessage(error) {
|
|
923
|
-
return actionErrorMessage(error, "Failed to submit approval decision.");
|
|
924
|
-
}
|
|
925
|
-
function approvalRevisionSuccessMessage(title) {
|
|
926
|
-
return `Requested revision for ${title}.`;
|
|
927
|
-
}
|
|
928
|
-
function approvalRevisionFailureMessage(error) {
|
|
929
|
-
return actionErrorMessage(error, "Failed to request approval revision.");
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// src/ui/use-focus-page-actions.ts
|
|
933
|
-
var FOCUS_HOLD_CONTEXT_MS = 2e4;
|
|
934
|
-
var FOCUS_HOLD_COMPOSE_MS = 45e3;
|
|
935
|
-
function useFocusPageActions(input) {
|
|
936
|
-
const acknowledge = usePluginAction("acknowledge-frame");
|
|
937
|
-
const commentOnIssue = usePluginAction("comment-on-issue");
|
|
938
|
-
const engageFocus = usePluginAction("engage-focus");
|
|
939
|
-
const markAttentionViewed = usePluginAction("mark-attention-viewed");
|
|
940
|
-
const recordApprovalResponse = usePluginAction("record-approval-response");
|
|
941
|
-
const setFocusPresence = usePluginAction("set-focus-presence");
|
|
942
|
-
const [pendingId, setPendingId] = useState4(null);
|
|
943
|
-
const [statusOverride, setStatusOverride] = useState4(null);
|
|
944
|
-
const focusReleaseTimerRef = useRef4(null);
|
|
945
|
-
const lastViewedInteractionRef = useRef4(null);
|
|
946
|
-
const markAttentionViewedRef = useRef4(markAttentionViewed);
|
|
947
|
-
const setFocusPresenceRef = useRef4(setFocusPresence);
|
|
948
|
-
markAttentionViewedRef.current = markAttentionViewed;
|
|
949
|
-
setFocusPresenceRef.current = setFocusPresence;
|
|
950
|
-
useEffect4(() => {
|
|
951
|
-
if (!statusOverride) return;
|
|
952
|
-
const timer = window.setTimeout(() => {
|
|
953
|
-
setStatusOverride(null);
|
|
954
|
-
}, 4e3);
|
|
955
|
-
return () => {
|
|
956
|
-
window.clearTimeout(timer);
|
|
957
|
-
};
|
|
958
|
-
}, [statusOverride]);
|
|
959
|
-
useEffect4(() => {
|
|
960
|
-
return () => {
|
|
961
|
-
if (focusReleaseTimerRef.current) {
|
|
962
|
-
window.clearTimeout(focusReleaseTimerRef.current);
|
|
963
|
-
}
|
|
964
|
-
};
|
|
965
|
-
}, []);
|
|
966
|
-
useEffect4(() => {
|
|
967
|
-
const companyId = input.companyId;
|
|
968
|
-
if (!companyId) return;
|
|
969
|
-
void setFocusPresenceRef.current({ companyId, presence: "present" }).catch(() => {
|
|
970
|
-
});
|
|
971
|
-
return () => {
|
|
972
|
-
void setFocusPresenceRef.current({ companyId, presence: "absent" }).catch(() => {
|
|
973
|
-
});
|
|
974
|
-
};
|
|
975
|
-
}, [input.companyId]);
|
|
976
|
-
useEffect4(() => {
|
|
977
|
-
const companyId = input.companyId;
|
|
978
|
-
const frame = input.displaySnapshot?.now;
|
|
979
|
-
if (!companyId || !frame) {
|
|
980
|
-
lastViewedInteractionRef.current = null;
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
const viewKey = `${companyId}:${frame.interactionId}`;
|
|
984
|
-
if (lastViewedInteractionRef.current === viewKey) return;
|
|
985
|
-
lastViewedInteractionRef.current = viewKey;
|
|
986
|
-
void markAttentionViewedRef.current({
|
|
987
|
-
companyId,
|
|
988
|
-
taskId: frame.taskId,
|
|
989
|
-
interactionId: frame.interactionId,
|
|
990
|
-
surface: "focus"
|
|
991
|
-
}).catch(() => {
|
|
992
|
-
if (lastViewedInteractionRef.current === viewKey) {
|
|
993
|
-
lastViewedInteractionRef.current = null;
|
|
994
|
-
}
|
|
995
|
-
});
|
|
996
|
-
}, [input.companyId, input.displaySnapshot?.now?.interactionId]);
|
|
997
|
-
async function holdNowFrame(frame, reason) {
|
|
998
|
-
const companyId = input.companyId;
|
|
999
|
-
if (!companyId) return;
|
|
1000
|
-
if (input.displaySnapshot?.now?.interactionId !== frame.interactionId) return;
|
|
1001
|
-
const durationMs = reason === "comment_compose" ? FOCUS_HOLD_COMPOSE_MS : FOCUS_HOLD_CONTEXT_MS;
|
|
1002
|
-
try {
|
|
1003
|
-
await engageFocus({
|
|
1004
|
-
companyId,
|
|
1005
|
-
taskId: frame.taskId,
|
|
1006
|
-
interactionId: frame.interactionId,
|
|
1007
|
-
reason,
|
|
1008
|
-
durationMs
|
|
1009
|
-
});
|
|
1010
|
-
input.refresh();
|
|
1011
|
-
if (focusReleaseTimerRef.current) {
|
|
1012
|
-
window.clearTimeout(focusReleaseTimerRef.current);
|
|
1013
|
-
}
|
|
1014
|
-
focusReleaseTimerRef.current = window.setTimeout(() => {
|
|
1015
|
-
input.refresh();
|
|
1016
|
-
focusReleaseTimerRef.current = null;
|
|
1017
|
-
}, durationMs + 150);
|
|
1018
|
-
} catch {
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
async function acknowledgeFrame(frame) {
|
|
1022
|
-
const companyId = input.companyId;
|
|
1023
|
-
if (!companyId) return;
|
|
1024
|
-
setPendingId(frame.id);
|
|
1025
|
-
try {
|
|
1026
|
-
await acknowledge({ companyId, taskId: frame.taskId, interactionId: frame.interactionId });
|
|
1027
|
-
setStatusOverride(acknowledgeSuccessMessage(compactTitle(frame)));
|
|
1028
|
-
input.refresh();
|
|
1029
|
-
} catch (actionError) {
|
|
1030
|
-
setStatusOverride(acknowledgeFailureMessage(actionError));
|
|
1031
|
-
} finally {
|
|
1032
|
-
setPendingId(null);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
async function commentOnIssueFrame(frame, body) {
|
|
1036
|
-
const companyId = input.companyId;
|
|
1037
|
-
const issueId = entityIdFromFrame(frame);
|
|
1038
|
-
if (!companyId || !issueId || entityTypeFromFrame(frame) !== "issue") {
|
|
1039
|
-
setStatusOverride(issueFrameUnsupportedMessage());
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
setPendingId(frame.id);
|
|
1043
|
-
try {
|
|
1044
|
-
await commentOnIssue({ companyId, taskId: frame.taskId, issueId, body });
|
|
1045
|
-
setStatusOverride(commentSuccessMessage(compactTitle(frame)));
|
|
1046
|
-
input.refresh();
|
|
1047
|
-
} catch (actionError) {
|
|
1048
|
-
setStatusOverride(commentFailureMessage(actionError));
|
|
1049
|
-
} finally {
|
|
1050
|
-
setPendingId(null);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
async function submitApprovalDecision(frame, decision) {
|
|
1054
|
-
const companyId = input.companyId;
|
|
1055
|
-
const approvalId = approvalIdForFrame(frame);
|
|
1056
|
-
if (!companyId || !approvalId) {
|
|
1057
|
-
setStatusOverride(approvalFrameUnsupportedMessage());
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
setPendingId(frame.id);
|
|
1061
|
-
try {
|
|
1062
|
-
await recordApprovalResponse({
|
|
1063
|
-
companyId,
|
|
1064
|
-
taskId: frame.taskId,
|
|
1065
|
-
interactionId: frame.interactionId,
|
|
1066
|
-
decision
|
|
1067
|
-
});
|
|
1068
|
-
setStatusOverride(approvalDecisionSuccessMessage(compactTitle(frame), decision));
|
|
1069
|
-
input.refresh();
|
|
1070
|
-
} catch (actionError) {
|
|
1071
|
-
setStatusOverride(approvalDecisionFailureMessage(actionError));
|
|
1072
|
-
} finally {
|
|
1073
|
-
setPendingId(null);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
async function requestApprovalRevision(frame) {
|
|
1077
|
-
const companyId = input.companyId;
|
|
1078
|
-
const approvalId = approvalIdForFrame(frame);
|
|
1079
|
-
if (!companyId || !approvalId) {
|
|
1080
|
-
setStatusOverride(approvalFrameUnsupportedMessage());
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
setPendingId(frame.id);
|
|
1084
|
-
try {
|
|
1085
|
-
await recordApprovalResponse({
|
|
1086
|
-
companyId,
|
|
1087
|
-
taskId: frame.taskId,
|
|
1088
|
-
interactionId: frame.interactionId,
|
|
1089
|
-
decision: "request-revision"
|
|
1090
|
-
});
|
|
1091
|
-
setStatusOverride(approvalRevisionSuccessMessage(compactTitle(frame)));
|
|
1092
|
-
input.refresh();
|
|
1093
|
-
} catch (actionError) {
|
|
1094
|
-
setStatusOverride(approvalRevisionFailureMessage(actionError));
|
|
1095
|
-
} finally {
|
|
1096
|
-
setPendingId(null);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
return {
|
|
1100
|
-
pendingId,
|
|
1101
|
-
statusOverride,
|
|
1102
|
-
holdNowFrame,
|
|
1103
|
-
acknowledgeFrame,
|
|
1104
|
-
commentOnIssueFrame,
|
|
1105
|
-
submitApprovalDecision,
|
|
1106
|
-
requestApprovalRevision
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
// src/ui/index.tsx
|
|
1111
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1112
|
-
function FrameActions(props) {
|
|
1113
|
-
const actionMode = responseKind(props.frame, props.lane);
|
|
1114
|
-
const isPending = props.pendingId === props.frame.id;
|
|
1115
|
-
if (actionMode === "none") return null;
|
|
1116
|
-
return /* @__PURE__ */ jsx5("div", { className: cn("flex items-center gap-2", props.compact && "justify-end"), children: actionMode === "approval" ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1117
|
-
isBudgetOverride(props.frame) ? /* @__PURE__ */ jsx5(
|
|
1118
|
-
ActionButton,
|
|
1119
|
-
{
|
|
1120
|
-
label: "Request revision",
|
|
1121
|
-
tone: "secondary",
|
|
1122
|
-
disabled: isPending,
|
|
1123
|
-
onClick: () => void props.onRequestRevision(props.frame)
|
|
1124
|
-
}
|
|
1125
|
-
) : null,
|
|
1126
|
-
/* @__PURE__ */ jsx5(
|
|
1127
|
-
ActionButton,
|
|
1128
|
-
{
|
|
1129
|
-
label: isPending ? "Submitting\u2026" : "Approve",
|
|
1130
|
-
tone: "primary",
|
|
1131
|
-
disabled: isPending,
|
|
1132
|
-
onClick: () => void props.onApprove(props.frame)
|
|
1133
|
-
}
|
|
1134
|
-
),
|
|
1135
|
-
/* @__PURE__ */ jsx5(
|
|
1136
|
-
ActionButton,
|
|
1137
|
-
{
|
|
1138
|
-
label: "Reject",
|
|
1139
|
-
tone: "danger",
|
|
1140
|
-
disabled: isPending,
|
|
1141
|
-
onClick: () => void props.onReject(props.frame)
|
|
1142
|
-
}
|
|
1143
|
-
)
|
|
1144
|
-
] }) : /* @__PURE__ */ jsx5(
|
|
1145
|
-
ActionButton,
|
|
1146
|
-
{
|
|
1147
|
-
label: isPending ? "Saving\u2026" : "Acknowledge",
|
|
1148
|
-
tone: "accent",
|
|
1149
|
-
disabled: isPending,
|
|
1150
|
-
onClick: () => void props.onAcknowledge(props.frame)
|
|
1151
|
-
}
|
|
1152
|
-
) });
|
|
1153
|
-
}
|
|
1154
|
-
function NowDetails(props) {
|
|
1155
|
-
const { frame, snapshotUpdatedAt } = props;
|
|
1156
|
-
const href = itemHref(frame, props.companyPrefix);
|
|
1157
|
-
const budgetHref = isBudgetOverride(frame) ? costsHref(props.companyPrefix) : null;
|
|
1158
|
-
const activityLink = activityHref(frame, props.companyPrefix);
|
|
1159
|
-
const hasContext = visibleContextItems(frame).length > 0;
|
|
1160
|
-
return /* @__PURE__ */ jsxs4("div", { className: "space-y-3 border-t border-border pt-4", children: [
|
|
1161
|
-
/* @__PURE__ */ jsx5(ExplainabilityPanel, { frame, lane: "now", detailOnly: true }),
|
|
1162
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
1163
|
-
/* @__PURE__ */ jsx5("span", { children: requestDescriptor(frame) }),
|
|
1164
|
-
/* @__PURE__ */ jsx5("span", { children: impactLabel(frame) }),
|
|
1165
|
-
/* @__PURE__ */ jsx5("span", { children: formatRelativeTime(frameUpdatedAt(frame, snapshotUpdatedAt)) }),
|
|
1166
|
-
requestedAmount(frame) ? /* @__PURE__ */ jsxs4("span", { children: [
|
|
1167
|
-
"Amount requested: ",
|
|
1168
|
-
requestedAmount(frame)
|
|
1169
|
-
] }) : null,
|
|
1170
|
-
budgetReason(frame) ? /* @__PURE__ */ jsx5("span", { children: budgetReason(frame) }) : null
|
|
1171
|
-
] }),
|
|
1172
|
-
hasContext ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1173
|
-
/* @__PURE__ */ jsx5("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Context" }),
|
|
1174
|
-
/* @__PURE__ */ jsx5(ContextItems, { frame })
|
|
1175
|
-
] }) : null,
|
|
1176
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
1177
|
-
href ? /* @__PURE__ */ jsxs4(
|
|
1178
|
-
"a",
|
|
1179
|
-
{
|
|
1180
|
-
href,
|
|
1181
|
-
target: "_blank",
|
|
1182
|
-
rel: "noopener noreferrer",
|
|
1183
|
-
className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",
|
|
1184
|
-
children: [
|
|
1185
|
-
primaryLinkLabel(frame),
|
|
1186
|
-
/* @__PURE__ */ jsx5(ExternalLinkIcon, {})
|
|
1187
|
-
]
|
|
1188
|
-
}
|
|
1189
|
-
) : null,
|
|
1190
|
-
activityLink ? /* @__PURE__ */ jsxs4(
|
|
1191
|
-
"a",
|
|
1192
|
-
{
|
|
1193
|
-
href: activityLink,
|
|
1194
|
-
target: "_blank",
|
|
1195
|
-
rel: "noopener noreferrer",
|
|
1196
|
-
className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",
|
|
1197
|
-
children: [
|
|
1198
|
-
"Open activity",
|
|
1199
|
-
/* @__PURE__ */ jsx5(ExternalLinkIcon, {})
|
|
1200
|
-
]
|
|
1201
|
-
}
|
|
1202
|
-
) : null,
|
|
1203
|
-
budgetHref ? /* @__PURE__ */ jsx5(Accent, { children: /* @__PURE__ */ jsxs4(
|
|
1204
|
-
"a",
|
|
1205
|
-
{
|
|
1206
|
-
href: budgetHref,
|
|
1207
|
-
target: "_blank",
|
|
1208
|
-
rel: "noopener noreferrer",
|
|
1209
|
-
className: "inline-flex items-center gap-1 text-xs font-medium underline underline-offset-2 hover:opacity-80",
|
|
1210
|
-
style: { color: "inherit" },
|
|
1211
|
-
children: [
|
|
1212
|
-
"Review in Costs",
|
|
1213
|
-
/* @__PURE__ */ jsx5(ExternalLinkIcon, {})
|
|
1214
|
-
]
|
|
1215
|
-
}
|
|
1216
|
-
) }) : null
|
|
1217
|
-
] })
|
|
1218
|
-
] });
|
|
1219
|
-
}
|
|
1220
|
-
function QuietNow() {
|
|
1221
|
-
return /* @__PURE__ */ jsxs4("div", { className: "flex min-h-20 items-center gap-3", children: [
|
|
1222
|
-
/* @__PURE__ */ jsx5(QuietMark, { size: "md" }),
|
|
1223
|
-
/* @__PURE__ */ jsx5("div", { className: "text-sm text-muted-foreground", children: "Nothing active right now." })
|
|
1224
|
-
] });
|
|
1225
|
-
}
|
|
1226
|
-
function NowActionRail(props) {
|
|
1227
|
-
const actionMode = responseKind(props.frame, props.lane);
|
|
1228
|
-
const isPending = props.pendingId === props.frame.id;
|
|
1229
|
-
const commentEnabled = actionMode === "acknowledge" && isIssueFrame(props.frame);
|
|
1230
|
-
const [commentOpen, setCommentOpen] = useState5(false);
|
|
1231
|
-
const [commentBody, setCommentBody] = useState5("");
|
|
1232
|
-
const textareaRef = useRef5(null);
|
|
1233
|
-
useEffect5(() => {
|
|
1234
|
-
setCommentOpen(false);
|
|
1235
|
-
setCommentBody("");
|
|
1236
|
-
}, [props.frame.id]);
|
|
1237
|
-
useEffect5(() => {
|
|
1238
|
-
if (commentOpen) textareaRef.current?.focus();
|
|
1239
|
-
}, [commentOpen]);
|
|
1240
|
-
async function submitComment() {
|
|
1241
|
-
const nextBody = commentBody.trim();
|
|
1242
|
-
if (!nextBody) return;
|
|
1243
|
-
await props.onComment(props.frame, nextBody);
|
|
1244
|
-
setCommentBody("");
|
|
1245
|
-
setCommentOpen(false);
|
|
1246
|
-
}
|
|
1247
|
-
return /* @__PURE__ */ jsxs4(
|
|
1248
|
-
"div",
|
|
1249
|
-
{
|
|
1250
|
-
className: "space-y-3 rounded-md border border-border/60 bg-secondary/20 px-3 py-3",
|
|
1251
|
-
style: props.wide ? { width: 320, flexShrink: 0, position: "sticky", top: 16, alignSelf: "flex-start" } : void 0,
|
|
1252
|
-
children: [
|
|
1253
|
-
/* @__PURE__ */ jsx5("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Actions" }),
|
|
1254
|
-
actionMode === "approval" ? /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
|
|
1255
|
-
/* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1256
|
-
/* @__PURE__ */ jsx5(
|
|
1257
|
-
ActionButton,
|
|
1258
|
-
{
|
|
1259
|
-
label: isPending ? "Submitting\u2026" : "Approve",
|
|
1260
|
-
tone: "primary",
|
|
1261
|
-
disabled: isPending,
|
|
1262
|
-
className: "w-full",
|
|
1263
|
-
onClick: () => void props.onApprove(props.frame)
|
|
1264
|
-
}
|
|
1265
|
-
),
|
|
1266
|
-
/* @__PURE__ */ jsx5(
|
|
1267
|
-
ActionButton,
|
|
1268
|
-
{
|
|
1269
|
-
label: "Reject",
|
|
1270
|
-
tone: "danger",
|
|
1271
|
-
disabled: isPending,
|
|
1272
|
-
className: "w-full",
|
|
1273
|
-
onClick: () => void props.onReject(props.frame)
|
|
1274
|
-
}
|
|
1275
|
-
)
|
|
1276
|
-
] }),
|
|
1277
|
-
isBudgetOverride(props.frame) ? /* @__PURE__ */ jsx5(
|
|
1278
|
-
ActionButton,
|
|
1279
|
-
{
|
|
1280
|
-
label: "Request revision",
|
|
1281
|
-
tone: "secondary",
|
|
1282
|
-
disabled: isPending,
|
|
1283
|
-
className: "w-full",
|
|
1284
|
-
onClick: () => void props.onRequestRevision(props.frame)
|
|
1285
|
-
}
|
|
1286
|
-
) : null
|
|
1287
|
-
] }) : actionMode === "acknowledge" ? /* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1288
|
-
commentEnabled ? /* @__PURE__ */ jsx5(
|
|
1289
|
-
ActionButton,
|
|
1290
|
-
{
|
|
1291
|
-
label: "Comment",
|
|
1292
|
-
tone: "accent",
|
|
1293
|
-
disabled: isPending,
|
|
1294
|
-
onClick: () => {
|
|
1295
|
-
props.onEngageFocus(props.frame, "comment_compose");
|
|
1296
|
-
setCommentOpen(true);
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
) : null,
|
|
1300
|
-
/* @__PURE__ */ jsx5(
|
|
1301
|
-
ActionButton,
|
|
1302
|
-
{
|
|
1303
|
-
label: isPending ? "Saving\u2026" : "Acknowledge",
|
|
1304
|
-
tone: "accent",
|
|
1305
|
-
disabled: isPending,
|
|
1306
|
-
onClick: () => void props.onAcknowledge(props.frame)
|
|
1307
|
-
}
|
|
1308
|
-
)
|
|
1309
|
-
] }) : null,
|
|
1310
|
-
props.itemLink ? /* @__PURE__ */ jsxs4(
|
|
1311
|
-
"a",
|
|
1312
|
-
{
|
|
1313
|
-
href: props.itemLink,
|
|
1314
|
-
target: "_blank",
|
|
1315
|
-
rel: "noopener noreferrer",
|
|
1316
|
-
className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",
|
|
1317
|
-
children: [
|
|
1318
|
-
primaryLinkLabel(props.frame),
|
|
1319
|
-
/* @__PURE__ */ jsx5(ExternalLinkIcon, {})
|
|
1320
|
-
]
|
|
1321
|
-
}
|
|
1322
|
-
) : null,
|
|
1323
|
-
commentOpen ? /* @__PURE__ */ jsxs4("div", { className: "space-y-2 rounded-md border border-border bg-background/80 p-3", children: [
|
|
1324
|
-
/* @__PURE__ */ jsxs4("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: [
|
|
1325
|
-
"Commenting on ",
|
|
1326
|
-
compactTitle(props.frame)
|
|
1327
|
-
] }),
|
|
1328
|
-
/* @__PURE__ */ jsx5("div", { className: "text-xs text-muted-foreground", children: "Posts to the issue thread without leaving Focus." }),
|
|
1329
|
-
/* @__PURE__ */ jsx5(
|
|
1330
|
-
"textarea",
|
|
1331
|
-
{
|
|
1332
|
-
ref: textareaRef,
|
|
1333
|
-
value: commentBody,
|
|
1334
|
-
onChange: (event) => setCommentBody(event.target.value),
|
|
1335
|
-
onKeyDown: (event) => {
|
|
1336
|
-
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
1337
|
-
event.preventDefault();
|
|
1338
|
-
void submitComment();
|
|
1339
|
-
}
|
|
1340
|
-
if (event.key === "Escape") {
|
|
1341
|
-
event.preventDefault();
|
|
1342
|
-
setCommentOpen(false);
|
|
1343
|
-
setCommentBody("");
|
|
1344
|
-
}
|
|
1345
|
-
},
|
|
1346
|
-
rows: 4,
|
|
1347
|
-
placeholder: "Add a short operator note back to the issue\u2026",
|
|
1348
|
-
className: "w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground shadow-xs outline-none transition focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"
|
|
1349
|
-
}
|
|
1350
|
-
),
|
|
1351
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-end gap-2", children: [
|
|
1352
|
-
/* @__PURE__ */ jsx5(
|
|
1353
|
-
ActionButton,
|
|
1354
|
-
{
|
|
1355
|
-
label: "Cancel",
|
|
1356
|
-
tone: "secondary",
|
|
1357
|
-
disabled: isPending,
|
|
1358
|
-
onClick: () => {
|
|
1359
|
-
setCommentOpen(false);
|
|
1360
|
-
setCommentBody("");
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
),
|
|
1364
|
-
/* @__PURE__ */ jsx5(
|
|
1365
|
-
ActionButton,
|
|
1366
|
-
{
|
|
1367
|
-
label: isPending ? "Posting\u2026" : "Post comment",
|
|
1368
|
-
tone: "accent",
|
|
1369
|
-
disabled: isPending || commentBody.trim().length === 0,
|
|
1370
|
-
onClick: () => void submitComment()
|
|
1371
|
-
}
|
|
1372
|
-
)
|
|
1373
|
-
] })
|
|
1374
|
-
] }) : null
|
|
1375
|
-
]
|
|
1376
|
-
}
|
|
1377
|
-
);
|
|
1378
|
-
}
|
|
1379
|
-
function NowPane(props) {
|
|
1380
|
-
const frame = props.snapshot.now;
|
|
1381
|
-
const [detailsOpen, setDetailsOpen] = useState5(false);
|
|
1382
|
-
const wideLayout = useWideLayout(1120);
|
|
1383
|
-
const activeFrameId = frame?.id ?? null;
|
|
1384
|
-
const activeUnread = !!frame && (props.snapshot.review?.unread.now ?? 0) > 0;
|
|
1385
|
-
const itemLink = frame ? itemHref(frame, props.companyPrefix) : null;
|
|
1386
|
-
const recommendedMove = frame ? recommendedMoveValue(frame) : null;
|
|
1387
|
-
const helperLine = frame ? supportingLine(frame, "now") : null;
|
|
1388
|
-
const driverBadge = driverBadgeStyle();
|
|
1389
|
-
useEffect5(() => {
|
|
1390
|
-
setDetailsOpen(false);
|
|
1391
|
-
}, [activeFrameId]);
|
|
1392
|
-
return /* @__PURE__ */ jsxs4(
|
|
1393
|
-
"section",
|
|
1394
|
-
{
|
|
1395
|
-
className: "border border-border bg-card shadow-sm",
|
|
1396
|
-
style: frame ? { borderLeftWidth: 3, borderLeftColor: ACCENT_COLOR } : void 0,
|
|
1397
|
-
children: [
|
|
1398
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-3 border-b border-border bg-accent px-4 py-3 sm:px-6", children: [
|
|
1399
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
|
|
1400
|
-
/* @__PURE__ */ jsx5(Accent, { className: "text-sm leading-none", children: props.posture.glyph }),
|
|
1401
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
|
|
1402
|
-
/* @__PURE__ */ jsx5(Accent, { className: "text-base leading-none font-semibold tracking-[0.04em]", children: props.brand.wordmark }),
|
|
1403
|
-
/* @__PURE__ */ jsx5("span", { className: "text-[11px] leading-none text-muted-foreground", children: props.posture.label })
|
|
1404
|
-
] })
|
|
1405
|
-
] }),
|
|
1406
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3 text-xs tabular-nums text-muted-foreground", children: [
|
|
1407
|
-
/* @__PURE__ */ jsxs4("span", { className: props.counts.now > 0 ? "font-semibold" : "", children: [
|
|
1408
|
-
"now ",
|
|
1409
|
-
props.counts.now > 0 ? /* @__PURE__ */ jsx5(Accent, { children: props.counts.now }) : props.counts.now
|
|
1410
|
-
] }),
|
|
1411
|
-
/* @__PURE__ */ jsxs4("span", { className: props.counts.next > 0 ? "font-semibold" : "", children: [
|
|
1412
|
-
"next ",
|
|
1413
|
-
props.counts.next > 0 ? /* @__PURE__ */ jsx5(Accent, { children: props.counts.next }) : props.counts.next
|
|
1414
|
-
] }),
|
|
1415
|
-
/* @__PURE__ */ jsxs4("span", { className: props.counts.ambient > 0 ? "font-semibold" : "", children: [
|
|
1416
|
-
"ambient ",
|
|
1417
|
-
props.counts.ambient > 0 ? /* @__PURE__ */ jsx5(Accent, { children: props.counts.ambient }) : props.counts.ambient
|
|
1418
|
-
] })
|
|
1419
|
-
] })
|
|
1420
|
-
] }),
|
|
1421
|
-
/* @__PURE__ */ jsx5("div", { className: "px-5 py-5 sm:px-6", children: !frame ? /* @__PURE__ */ jsx5(QuietNow, {}) : /* @__PURE__ */ jsxs4("div", { className: "space-y-5", children: [
|
|
1422
|
-
/* @__PURE__ */ jsxs4(
|
|
1423
|
-
"div",
|
|
1424
|
-
{
|
|
1425
|
-
className: wideLayout ? "flex items-start gap-6" : "space-y-4",
|
|
1426
|
-
children: [
|
|
1427
|
-
/* @__PURE__ */ jsxs4("div", { className: "space-y-4", style: wideLayout ? { minWidth: 0, flex: 1 } : void 0, children: [
|
|
1428
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: [
|
|
1429
|
-
/* @__PURE__ */ jsx5("span", { children: "Now" }),
|
|
1430
|
-
/* @__PURE__ */ jsx5("span", { className: "normal-case tracking-normal text-muted-foreground/70", children: sourceLabel(frame) })
|
|
1431
|
-
] }),
|
|
1432
|
-
recommendedMove ? /* @__PURE__ */ jsx5("div", { className: "max-w-4xl text-3xl font-semibold leading-tight text-foreground", children: recommendedMove }) : frame.summary ? /* @__PURE__ */ jsx5("div", { className: "max-w-4xl text-3xl font-semibold leading-tight text-foreground", children: frame.summary }) : null,
|
|
1433
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-2 text-xl font-medium leading-snug text-foreground/80", children: [
|
|
1434
|
-
/* @__PURE__ */ jsx5(UnreadDot, { visible: activeUnread }),
|
|
1435
|
-
/* @__PURE__ */ jsx5("span", { children: renderTitle(frame) })
|
|
1436
|
-
] }),
|
|
1437
|
-
helperLine ? /* @__PURE__ */ jsx5("p", { className: "max-w-3xl text-sm leading-relaxed text-muted-foreground", children: helperLine }) : null,
|
|
1438
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1439
|
-
/* @__PURE__ */ jsx5(Badge, { className: "border-border bg-secondary text-foreground/80", children: impactLabel(frame) }),
|
|
1440
|
-
driverLabel(frame) ? /* @__PURE__ */ jsx5(Badge, { className: driverBadge.className, style: driverBadge.style, children: driverLabel(frame) }) : null
|
|
1441
|
-
] }),
|
|
1442
|
-
/* @__PURE__ */ jsx5(InlineExplainability, { frame, lane: "now", preferLaneReason: !!helperLine }),
|
|
1443
|
-
/* @__PURE__ */ jsx5("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx5(Accent, { children: /* @__PURE__ */ jsxs4(
|
|
1444
|
-
"button",
|
|
1445
|
-
{
|
|
1446
|
-
type: "button",
|
|
1447
|
-
onClick: () => {
|
|
1448
|
-
if (!detailsOpen) {
|
|
1449
|
-
props.onEngageFocus(frame, "show_context");
|
|
1450
|
-
}
|
|
1451
|
-
setDetailsOpen(!detailsOpen);
|
|
1452
|
-
},
|
|
1453
|
-
"aria-expanded": detailsOpen,
|
|
1454
|
-
className: "flex items-center gap-1.5 text-xs font-medium transition-opacity hover:opacity-100",
|
|
1455
|
-
style: { color: "inherit" },
|
|
1456
|
-
children: [
|
|
1457
|
-
/* @__PURE__ */ jsx5(
|
|
1458
|
-
"svg",
|
|
1459
|
-
{
|
|
1460
|
-
viewBox: "0 0 16 16",
|
|
1461
|
-
className: cn("h-3 w-3 transition-transform", detailsOpen && "rotate-90"),
|
|
1462
|
-
fill: "currentColor",
|
|
1463
|
-
"aria-hidden": "true",
|
|
1464
|
-
children: /* @__PURE__ */ jsx5("path", { d: "M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z" })
|
|
1465
|
-
}
|
|
1466
|
-
),
|
|
1467
|
-
detailsOpen ? "Hide context" : "Show context"
|
|
1468
|
-
]
|
|
1469
|
-
}
|
|
1470
|
-
) }) })
|
|
1471
|
-
] }),
|
|
1472
|
-
/* @__PURE__ */ jsx5(
|
|
1473
|
-
NowActionRail,
|
|
1474
|
-
{
|
|
1475
|
-
frame,
|
|
1476
|
-
lane: "now",
|
|
1477
|
-
wide: wideLayout,
|
|
1478
|
-
pendingId: props.pendingId,
|
|
1479
|
-
itemLink,
|
|
1480
|
-
onEngageFocus: props.onEngageFocus,
|
|
1481
|
-
onApprove: props.onApprove,
|
|
1482
|
-
onReject: props.onReject,
|
|
1483
|
-
onRequestRevision: props.onRequestRevision,
|
|
1484
|
-
onAcknowledge: props.onAcknowledge,
|
|
1485
|
-
onComment: props.onComment
|
|
1486
|
-
}
|
|
1487
|
-
)
|
|
1488
|
-
]
|
|
1489
|
-
}
|
|
1490
|
-
),
|
|
1491
|
-
detailsOpen ? /* @__PURE__ */ jsx5(
|
|
1492
|
-
NowDetails,
|
|
1493
|
-
{
|
|
1494
|
-
frame,
|
|
1495
|
-
snapshotUpdatedAt: props.snapshot.updatedAt,
|
|
1496
|
-
companyPrefix: props.companyPrefix
|
|
1497
|
-
}
|
|
1498
|
-
) : null
|
|
1499
|
-
] }) })
|
|
1500
|
-
]
|
|
1501
|
-
}
|
|
1502
|
-
);
|
|
1503
|
-
}
|
|
1504
|
-
function NextRow(props) {
|
|
1505
|
-
const { frame } = props;
|
|
1506
|
-
const [expanded, setExpanded] = useState5(false);
|
|
1507
|
-
const unread = isFrameUnreadInSnapshot(frame, props.snapshot);
|
|
1508
|
-
const activityLink = activityHref(frame, props.companyPrefix);
|
|
1509
|
-
const itemLink = itemHref(frame, props.companyPrefix);
|
|
1510
|
-
const secondaryText = nextPrimaryText(frame);
|
|
1511
|
-
const driver = driverLabel(frame);
|
|
1512
|
-
const driverBadge = driverBadgeStyle();
|
|
1513
|
-
const detailText = judgmentLine(frame, "next");
|
|
1514
|
-
const showDetailText = detailText.trim().length > 0 && detailText !== secondaryText && detailText !== GENERIC_QUEUED_JUDGMENT;
|
|
1515
|
-
const rankOpacity = Math.max(0.4, 1 - (props.rank - 1) * 0.14);
|
|
1516
|
-
return /* @__PURE__ */ jsxs4("div", { className: "border-b border-border/80 last:border-b-0", style: expanded ? { borderLeftWidth: 2, borderLeftColor: ACCENT_COLOR } : void 0, children: [
|
|
1517
|
-
/* @__PURE__ */ jsxs4(
|
|
1518
|
-
"button",
|
|
1519
|
-
{
|
|
1520
|
-
type: "button",
|
|
1521
|
-
onClick: () => setExpanded(!expanded),
|
|
1522
|
-
"aria-expanded": expanded,
|
|
1523
|
-
className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50 sm:px-6",
|
|
1524
|
-
children: [
|
|
1525
|
-
/* @__PURE__ */ jsx5(UnreadDot, { visible: unread }),
|
|
1526
|
-
/* @__PURE__ */ jsx5("span", { className: "w-5 shrink-0", style: { opacity: rankOpacity }, children: /* @__PURE__ */ jsx5(Accent, { className: "text-xs font-medium tabular-nums", children: String(props.rank).padStart(2, "0") }) }),
|
|
1527
|
-
/* @__PURE__ */ jsxs4("div", { className: "min-w-0 flex-1", children: [
|
|
1528
|
-
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium leading-5 text-foreground", children: renderTitle(frame) }),
|
|
1529
|
-
secondaryText ? /* @__PURE__ */ jsx5("div", { className: "mt-0.5 max-w-3xl text-xs leading-5 text-muted-foreground", children: secondaryText }) : null
|
|
1530
|
-
] }),
|
|
1531
|
-
/* @__PURE__ */ jsx5(QueueMovementBadge, { movement: props.movement }),
|
|
1532
|
-
driver ? /* @__PURE__ */ jsx5(Badge, { className: cn("shrink-0", driverBadge.className), style: driverBadge.style, children: driver }) : null,
|
|
1533
|
-
/* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-muted-foreground", style: { opacity: 0.7 }, children: formatRelativeTime(frameUpdatedAt(frame, props.snapshotUpdatedAt)) }),
|
|
1534
|
-
/* @__PURE__ */ jsx5(
|
|
1535
|
-
"svg",
|
|
1536
|
-
{
|
|
1537
|
-
viewBox: "0 0 16 16",
|
|
1538
|
-
className: cn("h-3 w-3 shrink-0 text-muted-foreground transition-transform", expanded && "rotate-90"),
|
|
1539
|
-
fill: "currentColor",
|
|
1540
|
-
"aria-hidden": "true",
|
|
1541
|
-
children: /* @__PURE__ */ jsx5("path", { d: "M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z" })
|
|
1542
|
-
}
|
|
1543
|
-
)
|
|
1544
|
-
]
|
|
1545
|
-
}
|
|
1546
|
-
),
|
|
1547
|
-
expanded ? /* @__PURE__ */ jsxs4("div", { className: "space-y-3 px-4 pb-4 pl-12 sm:px-6 sm:pl-14", children: [
|
|
1548
|
-
showDetailText ? /* @__PURE__ */ jsx5("p", { className: "text-sm leading-relaxed text-muted-foreground", children: detailText }) : null,
|
|
1549
|
-
/* @__PURE__ */ jsx5(ExplainabilityStrip, { frame, lane: "next" }),
|
|
1550
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground", children: [
|
|
1551
|
-
/* @__PURE__ */ jsx5("span", { children: impactLabel(frame) }),
|
|
1552
|
-
driver ? /* @__PURE__ */ jsx5(Accent, { children: driver }) : null,
|
|
1553
|
-
props.movement ? /* @__PURE__ */ jsx5(Accent, { children: props.movement === "up" ? "rising in queue" : "falling in queue" }) : null,
|
|
1554
|
-
/* @__PURE__ */ jsxs4("span", { children: [
|
|
1555
|
-
"updated ",
|
|
1556
|
-
formatRelativeTime(frameUpdatedAt(frame, props.snapshotUpdatedAt))
|
|
1557
|
-
] }),
|
|
1558
|
-
itemLink ? /* @__PURE__ */ jsx5(
|
|
1559
|
-
"a",
|
|
1560
|
-
{
|
|
1561
|
-
href: itemLink,
|
|
1562
|
-
className: "font-medium underline underline-offset-2 hover:text-foreground",
|
|
1563
|
-
children: primaryLinkLabel(frame)
|
|
1564
|
-
}
|
|
1565
|
-
) : null,
|
|
1566
|
-
activityLink ? /* @__PURE__ */ jsx5(
|
|
1567
|
-
"a",
|
|
1568
|
-
{
|
|
1569
|
-
href: activityLink,
|
|
1570
|
-
className: "font-medium underline underline-offset-2 hover:text-foreground",
|
|
1571
|
-
children: "Open activity"
|
|
1572
|
-
}
|
|
1573
|
-
) : null
|
|
1574
|
-
] }),
|
|
1575
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
|
|
1576
|
-
/* @__PURE__ */ jsx5(IssueCommentComposer, { frame, pendingId: props.pendingId, onComment: props.onComment }),
|
|
1577
|
-
/* @__PURE__ */ jsx5(
|
|
1578
|
-
FrameActions,
|
|
1579
|
-
{
|
|
1580
|
-
frame,
|
|
1581
|
-
lane: "next",
|
|
1582
|
-
pendingId: props.pendingId,
|
|
1583
|
-
onApprove: props.onApprove,
|
|
1584
|
-
onReject: props.onReject,
|
|
1585
|
-
onRequestRevision: props.onRequestRevision,
|
|
1586
|
-
onAcknowledge: props.onAcknowledge
|
|
1587
|
-
}
|
|
1588
|
-
)
|
|
1589
|
-
] })
|
|
1590
|
-
] }) : null
|
|
1591
|
-
] });
|
|
1592
|
-
}
|
|
1593
|
-
function NextLane(props) {
|
|
1594
|
-
return /* @__PURE__ */ jsxs4("section", { className: "border border-border/80 bg-card", children: [
|
|
1595
|
-
/* @__PURE__ */ jsx5("div", { className: "border-b border-border/80 px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsx5("h2", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: "Next" }) }),
|
|
1596
|
-
props.rows.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "px-4 py-4 text-sm text-muted-foreground sm:px-6", style: { opacity: 0.7 }, children: "Nothing is staged behind the current focus." }) : props.rows.map((row, i) => /* @__PURE__ */ jsx5(
|
|
1597
|
-
NextRow,
|
|
1598
|
-
{
|
|
1599
|
-
rank: i + 1,
|
|
1600
|
-
frame: row.frame,
|
|
1601
|
-
movement: props.movement[row.frame.interactionId],
|
|
1602
|
-
snapshot: props.snapshot,
|
|
1603
|
-
snapshotUpdatedAt: props.snapshotUpdatedAt,
|
|
1604
|
-
companyPrefix: props.companyPrefix,
|
|
1605
|
-
pendingId: props.pendingId,
|
|
1606
|
-
onApprove: props.onApprove,
|
|
1607
|
-
onReject: props.onReject,
|
|
1608
|
-
onRequestRevision: props.onRequestRevision,
|
|
1609
|
-
onAcknowledge: props.onAcknowledge,
|
|
1610
|
-
onComment: props.onComment
|
|
1611
|
-
},
|
|
1612
|
-
row.frame.id
|
|
1613
|
-
))
|
|
1614
|
-
] });
|
|
1615
|
-
}
|
|
1616
|
-
function AmbientRow(props) {
|
|
1617
|
-
const { frame, snapshotUpdatedAt } = props;
|
|
1618
|
-
const href = itemHref(frame, props.companyPrefix);
|
|
1619
|
-
const unread = isFrameUnreadInSnapshot(frame, props.snapshot);
|
|
1620
|
-
const content = /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3 px-4 py-2.5 sm:px-6", children: [
|
|
1621
|
-
/* @__PURE__ */ jsx5(UnreadDot, { visible: unread, tone: "muted", reserveSpace: true }),
|
|
1622
|
-
/* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate text-[13px] text-muted-foreground", children: renderTitle(frame) }),
|
|
1623
|
-
/* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-muted-foreground/80", children: sourceLabel(frame) }),
|
|
1624
|
-
/* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-muted-foreground/70", children: formatRelativeTime(frameUpdatedAt(frame, snapshotUpdatedAt)) })
|
|
1625
|
-
] });
|
|
1626
|
-
return /* @__PURE__ */ jsx5("div", { style: { borderBottomWidth: 1, borderBottomColor: "var(--border)" }, className: "last:border-b-0", children: href ? /* @__PURE__ */ jsx5("a", { href, className: "block transition-colors hover:bg-accent/40", children: content }) : content });
|
|
1627
|
-
}
|
|
1628
|
-
function AmbientLane(props) {
|
|
1629
|
-
return /* @__PURE__ */ jsxs4("section", { style: { borderWidth: 1, borderColor: "var(--border)" }, className: "mt-2 bg-card", "aria-label": "Ambient attention", children: [
|
|
1630
|
-
/* @__PURE__ */ jsx5("div", { style: { borderBottomWidth: 1, borderBottomColor: "var(--border)" }, className: "px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsx5("h2", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground/80", children: "Ambient" }) }),
|
|
1631
|
-
props.rows.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3 px-4 py-4 text-sm text-muted-foreground sm:px-6", children: [
|
|
1632
|
-
/* @__PURE__ */ jsx5(QuietMark, {}),
|
|
1633
|
-
/* @__PURE__ */ jsx5("span", { children: "Nothing in peripheral view." })
|
|
1634
|
-
] }) : props.rows.map((row) => /* @__PURE__ */ jsx5(
|
|
1635
|
-
AmbientRow,
|
|
1636
|
-
{
|
|
1637
|
-
frame: row.frame,
|
|
1638
|
-
snapshot: props.snapshot,
|
|
1639
|
-
snapshotUpdatedAt: props.snapshotUpdatedAt,
|
|
1640
|
-
companyPrefix: props.companyPrefix
|
|
1641
|
-
},
|
|
1642
|
-
row.frame.id
|
|
1643
|
-
))
|
|
1644
|
-
] });
|
|
1645
|
-
}
|
|
1646
|
-
function DashboardWidget(props) {
|
|
1647
|
-
const companyId = props.context.companyId;
|
|
1648
|
-
const brand = currentSurfaceBrand();
|
|
1649
|
-
const href = pluginPagePath(props.context.companyPrefix);
|
|
1650
|
-
const model = useAttentionModel(companyId);
|
|
1651
|
-
const data = model.snapshot;
|
|
1652
|
-
const loading = model.loading;
|
|
1653
|
-
const error = model.error;
|
|
1654
|
-
if (!companyId) return /* @__PURE__ */ jsx5(MessageCard, { title: `Open ${brand.wordmark}`, body: `Open a company to see ${brand.wordmark}.` });
|
|
1655
|
-
if (loading) return /* @__PURE__ */ jsx5(WidgetLoadingState, { label: brand.loadingLabel });
|
|
1656
|
-
if (error) return /* @__PURE__ */ jsx5(MessageCard, { title: "Plugin error", body: error.message });
|
|
1657
|
-
if (!data) return /* @__PURE__ */ jsx5(MessageCard, { title: brand.wordmark, body: brand.headingEmptyState });
|
|
1658
|
-
const posture = postureForSnapshot(data);
|
|
1659
|
-
const leadingText = data.now ? recommendedMoveValue(data.now) ?? data.now.title : null;
|
|
1660
|
-
const actionable = actionableLabel(data);
|
|
1661
|
-
const borderStyle = posture.label === "busy" ? { borderLeftWidth: 2, borderLeftColor: ACCENT_COLOR } : posture.label === "elevated" ? { borderLeftWidth: 2, borderLeftColor: ACCENT_BORDER } : void 0;
|
|
1662
|
-
return /* @__PURE__ */ jsxs4("a", { href, className: "block border border-border bg-card px-4 py-3 shadow-sm transition-colors hover:bg-accent/40", style: borderStyle, children: [
|
|
1663
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-2 text-xs", children: [
|
|
1664
|
-
/* @__PURE__ */ jsx5(Accent, { className: "pt-0.5 text-sm", children: posture.glyph }),
|
|
1665
|
-
/* @__PURE__ */ jsx5("div", { className: "min-w-0", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
|
|
1666
|
-
/* @__PURE__ */ jsx5(Accent, { className: "font-semibold tracking-[0.04em]", children: brand.wordmark }),
|
|
1667
|
-
/* @__PURE__ */ jsx5(Accent, { className: "text-[11px]", children: posture.label }),
|
|
1668
|
-
actionable ? /* @__PURE__ */ jsx5(Accent, { className: "text-[11px]", children: actionable }) : null
|
|
1669
|
-
] }) }),
|
|
1670
|
-
/* @__PURE__ */ jsxs4("span", { className: "ml-auto tabular-nums text-muted-foreground", children: [
|
|
1671
|
-
"now ",
|
|
1672
|
-
data.counts.now,
|
|
1673
|
-
" \xB7 next ",
|
|
1674
|
-
data.counts.next,
|
|
1675
|
-
" \xB7 ambient ",
|
|
1676
|
-
data.counts.ambient
|
|
1677
|
-
] })
|
|
1678
|
-
] }),
|
|
1679
|
-
leadingText ? /* @__PURE__ */ jsx5(
|
|
1680
|
-
"div",
|
|
1681
|
-
{
|
|
1682
|
-
className: "mt-2 text-sm font-medium leading-5 text-foreground",
|
|
1683
|
-
style: {
|
|
1684
|
-
display: "-webkit-box",
|
|
1685
|
-
WebkitLineClamp: 2,
|
|
1686
|
-
WebkitBoxOrient: "vertical",
|
|
1687
|
-
overflow: "hidden"
|
|
1688
|
-
},
|
|
1689
|
-
children: leadingText
|
|
1690
|
-
}
|
|
1691
|
-
) : /* @__PURE__ */ jsx5("div", { className: "mt-2 text-sm text-muted-foreground", children: "No active interruption right now." })
|
|
1692
|
-
] });
|
|
1693
|
-
}
|
|
1694
|
-
function AttentionSidebarLink({ context }) {
|
|
1695
|
-
const companyId = context.companyId;
|
|
1696
|
-
const brand = currentSurfaceBrand();
|
|
1697
|
-
const href = pluginPagePath(context.companyPrefix);
|
|
1698
|
-
const isActive = typeof window !== "undefined" && window.location.pathname === href;
|
|
1699
|
-
const model = useAttentionModel(companyId);
|
|
1700
|
-
const merged = model.snapshot;
|
|
1701
|
-
const actionable = merged ? actionableCount(merged) : 0;
|
|
1702
|
-
const unread = unreadCount(merged);
|
|
1703
|
-
return /* @__PURE__ */ jsxs4(
|
|
1704
|
-
"a",
|
|
1705
|
-
{
|
|
1706
|
-
href,
|
|
1707
|
-
"aria-current": isActive ? "page" : void 0,
|
|
1708
|
-
className: cn(
|
|
1709
|
-
"flex items-center gap-2.5 px-3 py-2 text-[13px] font-medium transition-colors",
|
|
1710
|
-
isActive ? "bg-accent text-foreground" : "text-foreground/80 hover:bg-accent/50 hover:text-foreground"
|
|
1711
|
-
),
|
|
1712
|
-
children: [
|
|
1713
|
-
/* @__PURE__ */ jsxs4("span", { className: "relative shrink-0", "aria-hidden": "true", children: [
|
|
1714
|
-
/* @__PURE__ */ jsxs4(
|
|
1715
|
-
"svg",
|
|
1716
|
-
{
|
|
1717
|
-
viewBox: "0 0 24 24",
|
|
1718
|
-
className: "h-4 w-4",
|
|
1719
|
-
fill: "none",
|
|
1720
|
-
stroke: "currentColor",
|
|
1721
|
-
strokeWidth: "1.9",
|
|
1722
|
-
strokeLinecap: "round",
|
|
1723
|
-
strokeLinejoin: "round",
|
|
1724
|
-
children: [
|
|
1725
|
-
/* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "9" }),
|
|
1726
|
-
/* @__PURE__ */ jsx5("path", { d: "m14.3 8 5.2 9" }),
|
|
1727
|
-
/* @__PURE__ */ jsx5("path", { d: "M9.7 8h10.4" }),
|
|
1728
|
-
/* @__PURE__ */ jsx5("path", { d: "m7.4 12 5.2-9" }),
|
|
1729
|
-
/* @__PURE__ */ jsx5("path", { d: "m9.7 16-5.2-9" }),
|
|
1730
|
-
/* @__PURE__ */ jsx5("path", { d: "M14.3 16H3.9" }),
|
|
1731
|
-
/* @__PURE__ */ jsx5("path", { d: "m16.6 12-5.2 9" })
|
|
1732
|
-
]
|
|
1733
|
-
}
|
|
1734
|
-
),
|
|
1735
|
-
unread > 0 ? /* @__PURE__ */ jsx5(
|
|
1736
|
-
"span",
|
|
1737
|
-
{
|
|
1738
|
-
className: "absolute -right-1 -top-1 h-2 w-2 rounded-full",
|
|
1739
|
-
style: { backgroundColor: ACCENT_COLOR }
|
|
1740
|
-
}
|
|
1741
|
-
) : null
|
|
1742
|
-
] }),
|
|
1743
|
-
/* @__PURE__ */ jsx5("span", { className: "flex-1 truncate", children: brand.wordmark }),
|
|
1744
|
-
actionable > 0 ? /* @__PURE__ */ jsx5("span", { className: "inline-flex items-center justify-center rounded-full px-1.5 py-0.5 text-xs leading-none font-medium text-white", style: ACCENT_BG_STYLE, children: actionable }) : unread > 0 ? /* @__PURE__ */ jsx5(
|
|
1745
|
-
"span",
|
|
1746
|
-
{
|
|
1747
|
-
className: "inline-flex items-center justify-center rounded-full border px-1.5 py-0.5 text-xs leading-none font-medium",
|
|
1748
|
-
style: { color: ACCENT_COLOR, borderColor: ACCENT_BORDER, backgroundColor: "transparent" },
|
|
1749
|
-
children: unread
|
|
1750
|
-
}
|
|
1751
|
-
) : null
|
|
1752
|
-
]
|
|
1753
|
-
}
|
|
1754
|
-
);
|
|
1755
|
-
}
|
|
1756
|
-
function AttentionPage(props) {
|
|
1757
|
-
const companyId = props.context.companyId;
|
|
1758
|
-
const brand = currentSurfaceBrand();
|
|
1759
|
-
const model = useAttentionModel(companyId);
|
|
1760
|
-
const snapshot = model.snapshot;
|
|
1761
|
-
const loading = model.loading;
|
|
1762
|
-
const error = model.error;
|
|
1763
|
-
const displaySnapshot = snapshot;
|
|
1764
|
-
const backgroundErrorMessage = displaySnapshot && error ? `Sync issue: ${error.message}` : null;
|
|
1765
|
-
const posture = useMemo2(() => displaySnapshot ? postureForSnapshot(displaySnapshot) : null, [displaySnapshot]);
|
|
1766
|
-
const nextRows = useMemo2(
|
|
1767
|
-
() => displaySnapshot ? displaySnapshot.next.map((frame) => ({ frame, lane: "next" })) : [],
|
|
1768
|
-
[displaySnapshot]
|
|
1769
|
-
);
|
|
1770
|
-
const ambientRows = useMemo2(
|
|
1771
|
-
() => displaySnapshot ? displaySnapshot.ambient.map((frame) => ({ frame, lane: "ambient" })) : [],
|
|
1772
|
-
[displaySnapshot]
|
|
1773
|
-
);
|
|
1774
|
-
const nextMovement = useQueueMovement(nextRows.map((row) => row.frame));
|
|
1775
|
-
const {
|
|
1776
|
-
pendingId,
|
|
1777
|
-
statusOverride,
|
|
1778
|
-
holdNowFrame,
|
|
1779
|
-
acknowledgeFrame,
|
|
1780
|
-
commentOnIssueFrame,
|
|
1781
|
-
submitApprovalDecision,
|
|
1782
|
-
requestApprovalRevision
|
|
1783
|
-
} = useFocusPageActions({
|
|
1784
|
-
companyId,
|
|
1785
|
-
displaySnapshot,
|
|
1786
|
-
refresh: model.refresh
|
|
1787
|
-
});
|
|
1788
|
-
if (!companyId) return /* @__PURE__ */ jsx5(MessageCard, { title: `Open ${brand.wordmark}`, body: `Select a company to open ${brand.wordmark}.` });
|
|
1789
|
-
if (!displaySnapshot && loading) return /* @__PURE__ */ jsx5(PageLoadingState, { label: "Loading attention center" });
|
|
1790
|
-
if (!displaySnapshot && error) return /* @__PURE__ */ jsx5(MessageCard, { title: "Plugin error", body: error.message });
|
|
1791
|
-
if (!displaySnapshot || !posture) {
|
|
1792
|
-
return /* @__PURE__ */ jsx5(
|
|
1793
|
-
MessageCard,
|
|
1794
|
-
{
|
|
1795
|
-
title: `No ${brand.wordmark} State Yet`,
|
|
1796
|
-
body: `No ${brand.key === "focus" ? "focus" : "attention"} state has been captured for this company yet.`
|
|
1797
|
-
}
|
|
1798
|
-
);
|
|
1799
|
-
}
|
|
1800
|
-
return /* @__PURE__ */ jsxs4("div", { className: "space-y-4", children: [
|
|
1801
|
-
/* @__PURE__ */ jsx5(
|
|
1802
|
-
NowPane,
|
|
1803
|
-
{
|
|
1804
|
-
snapshot: displaySnapshot,
|
|
1805
|
-
posture,
|
|
1806
|
-
brand,
|
|
1807
|
-
companyPrefix: props.context.companyPrefix,
|
|
1808
|
-
counts: displaySnapshot.counts,
|
|
1809
|
-
pendingId,
|
|
1810
|
-
onEngageFocus: holdNowFrame,
|
|
1811
|
-
onApprove: (frame) => submitApprovalDecision(frame, "approve"),
|
|
1812
|
-
onReject: (frame) => submitApprovalDecision(frame, "reject"),
|
|
1813
|
-
onRequestRevision: requestApprovalRevision,
|
|
1814
|
-
onAcknowledge: acknowledgeFrame,
|
|
1815
|
-
onComment: commentOnIssueFrame
|
|
1816
|
-
}
|
|
1817
|
-
),
|
|
1818
|
-
/* @__PURE__ */ jsx5(
|
|
1819
|
-
NextLane,
|
|
1820
|
-
{
|
|
1821
|
-
snapshot: displaySnapshot,
|
|
1822
|
-
rows: nextRows,
|
|
1823
|
-
movement: nextMovement,
|
|
1824
|
-
snapshotUpdatedAt: displaySnapshot.updatedAt,
|
|
1825
|
-
companyPrefix: props.context.companyPrefix,
|
|
1826
|
-
pendingId,
|
|
1827
|
-
onApprove: (frame) => submitApprovalDecision(frame, "approve"),
|
|
1828
|
-
onReject: (frame) => submitApprovalDecision(frame, "reject"),
|
|
1829
|
-
onRequestRevision: requestApprovalRevision,
|
|
1830
|
-
onAcknowledge: acknowledgeFrame,
|
|
1831
|
-
onComment: commentOnIssueFrame
|
|
1832
|
-
}
|
|
1833
|
-
),
|
|
1834
|
-
/* @__PURE__ */ jsx5(
|
|
1835
|
-
AmbientLane,
|
|
1836
|
-
{
|
|
1837
|
-
snapshot: displaySnapshot,
|
|
1838
|
-
rows: ambientRows,
|
|
1839
|
-
snapshotUpdatedAt: displaySnapshot.updatedAt,
|
|
1840
|
-
companyPrefix: props.context.companyPrefix
|
|
1841
|
-
}
|
|
1842
|
-
),
|
|
1843
|
-
/* @__PURE__ */ jsx5(StatusToast, { message: statusOverride ?? backgroundErrorMessage })
|
|
1844
|
-
] });
|
|
1845
|
-
}
|
|
1846
|
-
export {
|
|
1847
|
-
AttentionPage,
|
|
1848
|
-
AttentionSidebarLink,
|
|
1849
|
-
DashboardWidget
|
|
1850
|
-
};
|
|
1851
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import{useEffect as en,useMemo as He,useState as It}from"react";function $(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:null}function I(e){return typeof e=="string"&&e.trim().length>0?e:void 0}function ve(e){return e===null?null:I(e)}function ye(e){if(!Array.isArray(e))return;let t=e.filter(n=>typeof n=="string"&&n.trim().length>0);return t.length>0?t:void 0}function Ct(e){if(!Array.isArray(e))return;let t=e.filter(n=>{if(typeof n!="object"||n===null)return!1;let o=n;return o.kind==="same_issue"||o.kind==="resolves"||o.kind==="supersedes"||o.kind==="repeats"||o.kind==="escalates"});return t.length>0?t:void 0}function E(e){let t=$(e.metadata),n=$(t?.attention),o=$(t?.semantic),i=$(t?.episode),a=$(t?.issueIntelligence),s=$(t?.decision);return{entityType:I(t?.entityType),issueStatus:I(t?.issueStatus),issuePriority:I(t?.issuePriority),approvalStatus:I(t?.approvalStatus),approvalType:I(t?.approvalType),agentStatus:I(t?.agentStatus),pauseReason:ve(t?.pauseReason),liveReconciled:typeof t?.liveReconciled=="boolean"?t.liveReconciled:void 0,activityPath:I(t?.activityPath),attention:n?{rationale:ye(n.rationale)}:void 0,semantic:o?{confidence:o.confidence==="low"||o.confidence==="medium"||o.confidence==="high"?o.confidence:void 0,relationHints:Ct(o.relationHints)}:void 0,episode:i?{size:typeof i.size=="number"?i.size:void 0,state:I(i.state)}:void 0,issueIntelligence:a?{matchedRuleIds:ye(a.matchedRuleIds),owner:ve(a.owner),ownerKind:a.ownerKind==="agent"||a.ownerKind==="human"?a.ownerKind:a.ownerKind===null?null:void 0,blockingTarget:ve(a.blockingTarget)}:void 0,decision:s?{owner:s.owner==="aperture_core"||s.owner==="paperclip_reconciliation"||s.owner==="approval_overlay"||s.owner==="display_merge"?s.owner:void 0,lane:s.lane==="now"||s.lane==="next"||s.lane==="ambient"?s.lane:void 0,sourcePolicy:I(s.sourcePolicy),rationale:ye(s.rationale),promotedFromLane:s.promotedFromLane==="next"||s.promotedFromLane==="ambient"?s.promotedFromLane:void 0}:void 0}}var pn=300*1e3;function R(e){return e.provenance?.factors?.includes("budget stop")??!1}function _(e,t){return e.timing.updatedAt||t}var Q="Important enough to keep visible, but not enough to displace now.",Rt=Q;function Tt(e){return e?"Budget controls are blocking work until a board decision lands.":"A board decision is blocking work in Paperclip."}function Ke(e,t){return t==="now"?R(e)?Tt(!0):e.mode==="approval"?"A human decision is blocking work right now.":e.tone==="critical"?"This surfaced because it can displace the operator now.":"This is the clearest current operator focus.":R(e)?"Budget review is staged behind the current focus.":t==="next"?Rt:"Useful for awareness, but not interrupting."}import{useEffect as We,useRef as Lt,useState as ze}from"react";import{jsx as f,jsxs as k}from"react/jsx-runtime";var A="#007ACC",G=`${A}14`,F=`${A}33`,re={backgroundColor:A};function S(...e){return e.filter(Boolean).join(" ")}function Pt(){let e=Lt(null);return We(()=>{e.current?.style.setProperty("color",A,"important")}),e}function v({children:e,className:t}){let n=Pt();return f("span",{ref:n,className:t,children:e})}function we({size:e="sm"}){let t=e==="md"?10:8;return f("span",{"aria-hidden":"true",className:"inline-block shrink-0 rounded-full border",style:{width:t,height:t,borderColor:F}})}function Qe(e=1024){let[t,n]=ze(!1);return We(()=>{if(typeof window>"u"||typeof window.matchMedia!="function")return;let o=window.matchMedia(`(min-width: ${e}px)`),i=()=>n(o.matches);return i(),o.addEventListener("change",i),()=>o.removeEventListener("change",i)},[e]),t}function oe(e){let t=e.tone??"default";if(!e.visible&&!e.reserveSpace)return null;let n=e.visible?t==="muted"?{backgroundColor:F}:re:{backgroundColor:"transparent"};return f("span",{className:"inline-block h-2 w-2 rounded-full shrink-0",style:n,"aria-label":e.visible?"Unread attention item":void 0})}function V(){return k("svg",{viewBox:"0 0 16 16",className:"h-3 w-3",fill:"none",stroke:"currentColor",strokeWidth:"1.5","aria-hidden":"true",children:[f("path",{d:"M6.5 3.5h-3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-3"}),f("path",{d:"M8.5 1.5h6v6"}),f("path",{d:"M14.5 1.5l-7 7"})]})}function b(e){return f("div",{className:S("animate-pulse rounded bg-secondary/80",e.className),"aria-hidden":"true"})}function D(e){return k("div",{className:"border border-border bg-card p-4 shadow-sm",children:[f("div",{className:S("text-sm font-medium",e.accent&&"text-foreground"),children:e.title}),f("div",{className:"mt-1 text-sm text-muted-foreground",children:e.body})]})}function Ge({label:e}){return k("div",{className:"border border-border bg-card px-4 py-3 shadow-sm",children:[k("div",{className:"flex items-start gap-3",children:[f(b,{className:"mt-0.5 h-3 w-3 rounded-full"}),k("div",{className:"flex-1 space-y-2",children:[f(b,{className:"h-3 w-20"}),f(b,{className:"h-4 w-48"})]}),f(b,{className:"h-3 w-24"})]}),f("div",{className:"sr-only",children:e})]})}function Ve({label:e}){return k("div",{className:"space-y-5","aria-label":e,children:[f("div",{className:"border border-border bg-card px-4 py-3 shadow-sm",children:k("div",{className:"flex items-center justify-between gap-3",children:[f(b,{className:"h-4 w-44"}),f(b,{className:"h-8 w-28"})]})}),k("div",{className:"border border-border bg-card shadow-sm",children:[f("div",{className:"border-b border-border px-4 py-3 sm:px-6",children:f(b,{className:"h-4 w-36"})}),k("div",{className:"space-y-4 px-4 py-5 sm:px-6",children:[f(b,{className:"h-3 w-16"}),f(b,{className:"h-7 w-2/3"}),k("div",{className:"flex gap-2",children:[f(b,{className:"h-6 w-24"}),f(b,{className:"h-6 w-20"}),f(b,{className:"h-6 w-16"})]}),f(b,{className:"h-10 w-56"})]})]}),k("div",{className:"border border-border bg-card shadow-sm",children:[f("div",{className:"border-b border-border px-4 py-3 sm:px-6",children:f(b,{className:"h-3 w-12"})}),k("div",{className:"space-y-3 px-4 py-4 sm:px-6",children:[f(b,{className:"h-4 w-full"}),f(b,{className:"h-4 w-5/6"}),f(b,{className:"h-4 w-2/3"})]})]})]})}function Ze({message:e}){return f("div",{className:S("pointer-events-none fixed bottom-6 left-1/2 z-50 -translate-x-1/2 transition-all duration-200",e?"translate-y-0 opacity-100":"translate-y-2 opacity-0"),"aria-live":"polite","aria-atomic":"true",children:e?f("div",{className:"min-w-72 max-w-md rounded-lg border border-border bg-card px-4 py-3 text-sm text-foreground shadow-lg",children:e}):null})}function q(e){return f("span",{className:S("inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap shrink-0",e.className),style:e.style,children:e.children})}function N(e){let[t,n]=ze(!1),o=e.tone==="primary"?"bg-green-700 text-white hover:bg-green-600":e.tone==="accent"?"text-white":e.tone==="danger"?"bg-destructive text-white hover:bg-destructive/90 dark:bg-destructive/60":"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50";return f("button",{type:"button",onClick:e.onClick,onMouseEnter:()=>n(!0),onMouseLeave:()=>n(!1),disabled:e.disabled,className:S("inline-flex h-8 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium","transition-[color,background-color,border-color,box-shadow,opacity,filter]","focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]","disabled:pointer-events-none disabled:opacity-50",e.disabled&&/…|\.\.\./.test(e.label)&&"animate-pulse",o,e.className),style:e.tone==="accent"?{...re,filter:t&&!e.disabled?"brightness(1.08)":void 0}:void 0,children:e.label})}function Je({movement:e}){return e?k("span",{className:"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium",style:{color:A,backgroundColor:G,borderColor:F},children:[f("span",{"aria-hidden":"true",children:e==="up"?"\u2191":"\u2193"}),e==="up"?"moved up":"moved down"]}):null}import{usePluginData as Et,usePluginStream as Mt}from"@paperclipai/plugin-sdk/ui";import{useEffect as ke,useMemo as _t,useRef as Ae,useState as Dt}from"react";var Ot="attention-updates";function ie(){return{key:"focus",wordmark:"Focus",headingEmptyState:"No focus state yet.",loadingLabel:"Loading Focus\u2026"}}function Se(e){return e?`/${e}/aperture`:"/aperture"}function Z(e){let t=new Date(e).getTime();if(Number.isNaN(t))return"recently";let n=t-Date.now(),o=Math.round(n/6e4),i=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"});if(Math.abs(o)<60)return i.format(o,"minute");let a=Math.round(o/60);if(Math.abs(a)<24)return i.format(a,"hour");let s=Math.round(a/24);return i.format(s,"day")}function j(e){return e.source?.label??"Paperclip"}function Ne(e){let t=e.now;return t?.tone==="critical"||t?.consequence==="high"||e.counts.now+e.counts.next>=3?{glyph:"\u25CF",label:"busy"}:e.counts.now>0||e.counts.next>0||e.counts.ambient>0?{glyph:"\u25D0",label:"elevated"}:{glyph:"\u25CB",label:"calm"}}function Ie(e){return(e.now?1:0)+e.next.length}function Xe(e){let t=Ie(e);return t>0?`${t} action${t===1?"":"s"}`:null}function Ye(e){return e?.review?.unread.total??0}function Bt(e,t,n=15e3){let o=Ae(t);o.current=t,ke(()=>{if(!e)return;function i(){if(!(typeof document<"u"&&document.visibilityState==="hidden"))for(let d of o.current)d()}let a=window.setInterval(()=>{i()},n);function s(){if(typeof document<"u"&&document.visibilityState==="visible")for(let d of o.current)d()}return document.addEventListener("visibilitychange",s),()=>{window.clearInterval(a),document.removeEventListener("visibilitychange",s)}},[e,n])}function et(e,t=3500){let n=Ae({}),o=Ae({}),[i,a]=Dt({});return ke(()=>{let s=Object.fromEntries(e.map((d,m)=>[d.interactionId,m]));return a(d=>{let m={};for(let u of e){let x=n.current[u.interactionId],y=s[u.interactionId];typeof x!="number"||x===y||(m[u.interactionId]=y<x?"up":"down")}if(Object.keys(m).length===0)return d;let p={...d,...m};for(let[u,x]of Object.entries(m))window.clearTimeout(o.current[u]),o.current[u]=window.setTimeout(()=>{a(y=>{if(y[u]!==x)return y;let P={...y};return delete P[u],P}),delete o.current[u]},t);return p}),n.current=s,()=>{for(let d of Object.values(o.current))window.clearTimeout(d)}},[e,t]),i}function ae(e){let t=Et("attention-display",e?{companyId:e}:void 0),n=Mt(Ot,e?{companyId:e}:void 0);return Bt(e,[t.refresh]),ke(()=>{!e||!n.lastEvent||t.refresh()},[e,n.lastEvent?.updatedAt,n.lastEvent?.eventType]),_t(()=>({snapshot:t.data?.snapshot??null,review:t.data?.reviewState??null,loading:t.loading,error:t.error,refresh(){t.refresh()}}),[t])}var T={approvalType:"approval-type",requestedAmount:"requested-amount",budgetReason:"budget-reason",decisionContext:"decision-context",linkedIssues:"linked-issues",needsActionFrom:"needs-action-from",blockedBy:"blocked-by",blocksTarget:"blocks-target",recommendedMove:"recommended-move",issueStatus:"issue-status",issuePriority:"issue-priority",latestComment:"latest-comment",agentStatus:"agent-status",pauseReason:"pause-reason",agentTitle:"agent-title"};function tt(e){try{let t=decodeURIComponent(e);return t.trim().length>0?t:null}catch{return null}}function se(e){if(typeof e!="string")return null;let t=e.indexOf(":");if(t<=0||t>=e.length-1)return null;let n=tt(e.slice(0,t)),o=tt(e.slice(t+1));return!n||!o?null:{kind:n,id:o}}function nt(e){return se(e)?.kind??null}function rt(e){return se(e)?.id??null}function ot(e){return`Blocks ${e} until the review lands.`}function it(e){return`Needs action from ${e}.`}import{Fragment as Ht,jsx as Ce}from"react/jsx-runtime";function J(e,t){let n=e.context?.items?.find(o=>o.id===t);return typeof n?.value=="string"&&n.value.trim().length>0?n.value:void 0}function Re(e){return J(e,T.requestedAmount)}function Te(e){return J(e,T.budgetReason)}function X(e){return J(e,T.recommendedMove)}function Ut(e){return J(e,T.needsActionFrom)}function $t(e){return J(e,T.blocksTarget)}function de(e){return`${e.consequence} impact`}function Fe(e){switch(e){case"in_review":return"review required";case"pending_approval":return"approval required";default:return e.replace(/_/g," ")}}function H(e){if(R(e))return"budget stop";let t=E(e),n=t.issueStatus;if(typeof n=="string"&&n.trim().length>0)return Fe(n);let o=t.pauseReason;if(typeof o=="string"&&o.trim().length>0)return Fe(o);let i=t.agentStatus;return typeof i=="string"&&i.trim().length>0?Fe(i):null}function Le(){return{className:"border-transparent",style:{color:A,backgroundColor:G,borderColor:F}}}function at(e){let t=H(e);return t?`${j(e)} \xB7 ${t}`:j(e)}function O(e,t){return e.provenance?.whyNow?e.provenance.whyNow:Ke(e,t)}function st(e){let t=X(e);if(t)return t;let n=e.summary?.trim();if(n)return n;let o=O(e,"next");return o===Q?null:o}function dt(e,t){let n=Ut(e),o=$t(e);if(o&&t==="now"&&B(e)==="issue"&&H(e)==="review required")return ot(o);if(n&&t==="now"&&B(e)==="issue")return it(n);if(t==="now"){let a=X(e)?.trim(),s=e.summary?.trim(),d=O(e,t).trim();return a&&s&&s!==a&&s!==d?s:null}let i=O(e,t);return i.trim().length>0?i:null}function ce(e){let t=se(e.taskId);return t?.kind==="approval"?t.id:null}function Pe(e){return rt(e.taskId)}function B(e){return nt(e.taskId)}function Y(e,t){let n=B(e),o=Pe(e);if(!n||!o||!t)return null;let i=n==="run"?"runs":n==="approval"?"approvals":n==="issue"?"issues":n==="agent"?"agents":null;return i?`/${t}/${i}/${o}`:null}function ct(e){return e?`/${e}/costs`:null}function Ee(e,t){if(!t)return null;let n=E(e);return n.activityPath?`/${t}/${n.activityPath}`:null}function le(e){switch(B(e)){case"approval":return"Open approval";case"issue":return"Open issue";case"run":return"Open run";case"agent":return"Open agent";default:return"Open in Paperclip"}}function Me(e,t){return ce(e)&&(e.responseSpec?.kind==="approval"||e.mode==="approval")?"approval":t!=="ambient"&&(e.responseSpec?.kind==="acknowledge"||e.mode==="status")?"acknowledge":"none"}function _e(e,t){let n=t.review?.lastSeenAt;return n?_(e,t.updatedAt).localeCompare(n)>0:!0}function M(e){return e.title.trim().length>0?e.title:"Untitled frame"}var qt=/\b([A-Z][A-Z0-9]+-\d+)\b|"([^"]+)"/g;function jt({text:e}){let t=[],n=0,o,i=new RegExp(qt.source,"g");for(;(o=i.exec(e))!==null;){o.index>n&&t.push(e.slice(n,o.index));let a=o[1]??o[2]??o[0];t.push(Ce(v,{children:o[1]?a:`"${a}"`},o.index)),n=i.lastIndex}return n<e.length&&t.push(e.slice(n)),Ce(Ht,{children:t})}function ue(e){return Ce(jt,{text:M(e)})}function me(e){return(e.context?.items??[]).filter(t=>t.id!==T.recommendedMove)}function fe(e){return B(e)==="issue"}function Kt(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"&&t.trim().length>0):[]}function Wt(e){let t=E(e).semantic?.confidence;return t==="low"||t==="medium"||t==="high"?t:null}function zt(e){return E(e).semantic?.relationHints??[]}function Qt(e){switch(e.kind){case"same_issue":return"Part of the same thread";case"resolves":return"Resolves an earlier blocker";case"supersedes":return"Moves the request forward";case"repeats":return"Repeats an earlier ask";case"escalates":return"Raises the urgency"}}function Gt(e){switch(e){case"now":return"This is the most urgent item in the queue right now.";case"next":return"This is staged behind the current top item.";case"ambient":return"This is visible for awareness without needing action yet."}}function Vt(e){let t=E(e).episode,n=t?.size??null,o=t?.state?.replace(/_/g," ")??null;return!n||n<=1?null:o?`Part of a ${o} thread with ${n} related interactions.`:`Part of a thread with ${n} related interactions.`}function Zt(e){let t=E(e).attention?.rationale??[],n=Kt(e.provenance?.factors);return[...new Set([...t.length>0?t:n,...n])]}function De(e){return`${e} confidence`}function Oe(e,t){let n=[...new Set(zt(e).map(Qt))];return{whyNow:e.provenance?.whyNow??e.summary??null,laneReason:Gt(t),signalStrength:Wt(e),signals:Zt(e),relationLabels:n,continuity:Vt(e)}}import{jsx as g,jsxs as L}from"react/jsx-runtime";function lt({frame:e}){let t=me(e);return t.length===0?null:g("div",{className:"grid gap-2 md:grid-cols-2",children:t.map(n=>L("div",{className:S("border border-border bg-secondary/50 px-3 py-2",n.id===T.latestComment&&"md:col-span-2"),children:[g("div",{className:"text-xs font-medium text-muted-foreground",children:n.label}),g("div",{className:"mt-1 text-sm text-foreground",children:n.value??"Available"})]},n.id))})}function Be(e){return e.values.length===0?null:g("div",{className:"flex flex-wrap items-center gap-2",children:e.values.map(t=>g(q,{className:e.accent?"border-transparent":"border-border bg-background/70 text-foreground/80",style:e.accent?{color:A,backgroundColor:G,borderColor:F}:void 0,children:t},t))})}function Ue(e){let t=Oe(e.frame,e.lane),n=[...t.signalStrength?[De(t.signalStrength)]:[],...t.signals.slice(0,2),...t.relationLabels.slice(0,1)],o=e.lane==="next"?"Why next":e.lane==="ambient"?"Why ambient":"Why now",i=t.whyNow??O(e.frame,e.lane),a=e.lane==="now"&&e.preferLaneReason?t.laneReason:i,s=e.lane==="now"?!e.preferLaneReason&&i!==t.laneReason?t.laneReason:null:t.laneReason;return L("div",{className:"space-y-2 border-l-2 pl-4",style:{borderColor:A},children:[g("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:o}),g("p",{className:"max-w-3xl text-sm leading-relaxed text-foreground/90",children:a}),s?g("p",{className:"text-xs leading-relaxed text-muted-foreground",children:s}):null,n.length>0?g(Be,{values:n,accent:!0}):null]})}function ut(e){let t=Oe(e.frame,e.lane),n=!e.detailOnly&&t.signalStrength?De(t.signalStrength):null,o=e.detailOnly?t.signals.slice(2):t.signals,i=e.detailOnly?t.relationLabels.slice(1):t.relationLabels,a=e.lane==="now"?"Reasoning":e.lane==="next"?"Why it sits here":"Why it stays quiet";return e.detailOnly&&o.length===0&&i.length===0&&!t.continuity?null:L("div",{className:"space-y-4",children:[e.detailOnly?null:L("div",{className:"grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem]",children:[L("div",{children:[g("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:a}),g("p",{className:"mt-1 text-sm leading-relaxed text-muted-foreground",children:t.laneReason})]}),n?L("div",{children:[g("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:"Confidence"}),g("p",{className:"mt-1 text-sm text-foreground/90",children:n})]}):null]}),o.length>0?L("div",{className:"space-y-2",children:[g("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:e.detailOnly?"More signals":"Signals"}),g(Be,{values:o})]}):null,i.length>0?L("div",{className:"space-y-2",children:[g("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:e.detailOnly?"More thread context":"Thread context"}),g(Be,{values:i,accent:!0})]}):null,t.continuity?L("div",{className:"space-y-2",children:[g("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:"Related activity"}),g("p",{className:"text-sm leading-relaxed text-muted-foreground",children:t.continuity})]}):null]})}function mt(e){return g("div",{className:"space-y-2 border-t border-border/60 pt-3",children:g(Ue,{frame:e.frame,lane:e.lane})})}import{useEffect as ft,useRef as Jt,useState as pt}from"react";import{jsx as K,jsxs as $e}from"react/jsx-runtime";function qe(e){let[t,n]=pt(!1),[o,i]=pt(""),a=Jt(null),s=fe(e.frame),d=e.pendingId===e.frame.id;if(ft(()=>{n(!1),i("")},[e.frame.id]),ft(()=>{t&&s&&a.current?.focus()},[t,s]),!s)return null;async function m(){let p=o.trim();p&&(await e.onComment(e.frame,p),i(""),n(!1))}if(!t){let p=e.triggerLabel??"Leave comment",u=e.triggerTone??"link";return u==="link"?K("button",{type:"button",className:"text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",onClick:()=>{e.onOpen?.(),n(!0)},children:p}):K(N,{label:p,tone:u,className:e.triggerFullWidth?"w-full":void 0,disabled:d,onClick:()=>{e.onOpen?.(),n(!0)}})}return $e("div",{className:e.panelClassName??"w-full space-y-2 rounded-md border border-border bg-secondary/40 p-3",children:[$e("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:["Commenting on ",M(e.frame)]}),K("div",{className:"text-[11px] text-muted-foreground",children:"Posts to the issue thread without leaving Focus."}),K("textarea",{ref:a,value:o,onChange:p=>i(p.target.value),onKeyDown:p=>{(p.metaKey||p.ctrlKey)&&p.key==="Enter"&&(p.preventDefault(),m()),p.key==="Escape"&&(p.preventDefault(),n(!1),i(""))},rows:e.rows??3,placeholder:"Add a short operator note back to the issue\u2026",className:"w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground shadow-xs outline-none transition focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"}),$e("div",{className:"flex items-center justify-end gap-2",children:[K(N,{label:"Cancel",tone:"secondary",disabled:d,onClick:()=>{n(!1),i("")}}),K(N,{label:d?"Posting\u2026":"Post comment",tone:"accent",disabled:d||o.trim().length===0,onClick:()=>{m()}})]})]})}import{usePluginAction as W}from"@paperclipai/plugin-sdk/ui";import{useEffect as ge,useRef as xe,useState as St}from"react";function pe(e,t){return e instanceof Error?e.message:t}function gt(e){return`Acknowledged ${e}.`}function xt(e){return pe(e,"Failed to acknowledge frame.")}function ht(){return"This frame is not backed by a Paperclip issue."}function bt(e){return`Posted a comment on ${e}.`}function vt(e){return pe(e,"Failed to post comment.")}function je(){return"This frame is not backed by a Paperclip approval."}function yt(e,t){return`${t==="approve"?"Approved":"Rejected"} ${e}.`}function wt(e){return pe(e,"Failed to submit approval decision.")}function At(e){return`Requested revision for ${e}.`}function kt(e){return pe(e,"Failed to request approval revision.")}var Xt=2e4,Yt=45e3;function Nt(e){let t=W("acknowledge-frame"),n=W("comment-on-issue"),o=W("engage-focus"),i=W("mark-attention-viewed"),a=W("record-approval-response"),s=W("set-focus-presence"),[d,m]=St(null),[p,u]=St(null),x=xe(null),y=xe(null),P=xe(i),z=xe(s);P.current=i,z.current=s,ge(()=>{if(!p)return;let c=window.setTimeout(()=>{u(null)},4e3);return()=>{window.clearTimeout(c)}},[p]),ge(()=>()=>{x.current&&window.clearTimeout(x.current)},[]),ge(()=>{let c=e.companyId;if(c)return z.current({companyId:c,presence:"present"}).catch(()=>{}),()=>{z.current({companyId:c,presence:"absent"}).catch(()=>{})}},[e.companyId]),ge(()=>{let c=e.companyId,h=e.displaySnapshot?.now;if(!c||!h){y.current=null;return}let w=`${c}:${h.interactionId}`;y.current!==w&&(y.current=w,P.current({companyId:c,taskId:h.taskId,interactionId:h.interactionId,surface:"focus"}).catch(()=>{y.current===w&&(y.current=null)}))},[e.companyId,e.displaySnapshot?.now?.interactionId]);async function he(c,h){let w=e.companyId;if(!w||e.displaySnapshot?.now?.interactionId!==c.interactionId)return;let C=h==="comment_compose"?Yt:Xt;try{await o({companyId:w,taskId:c.taskId,interactionId:c.interactionId,reason:h,durationMs:C}),e.refresh(),x.current&&window.clearTimeout(x.current),x.current=window.setTimeout(()=>{e.refresh(),x.current=null},C+150)}catch{}}async function ee(c){let h=e.companyId;if(h){m(c.id);try{await t({companyId:h,taskId:c.taskId,interactionId:c.interactionId}),u(gt(M(c))),e.refresh()}catch(w){u(xt(w))}finally{m(null)}}}async function te(c,h){let w=e.companyId,C=Pe(c);if(!w||!C||B(c)!=="issue"){u(ht());return}m(c.id);try{await n({companyId:w,taskId:c.taskId,issueId:C,body:h}),u(bt(M(c))),e.refresh()}catch(be){u(vt(be))}finally{m(null)}}async function U(c,h){let w=e.companyId,C=ce(c);if(!w||!C){u(je());return}m(c.id);try{await a({companyId:w,taskId:c.taskId,interactionId:c.interactionId,decision:h}),u(yt(M(c),h)),e.refresh()}catch(be){u(wt(be))}finally{m(null)}}async function ne(c){let h=e.companyId,w=ce(c);if(!h||!w){u(je());return}m(c.id);try{await a({companyId:h,taskId:c.taskId,interactionId:c.interactionId,decision:"request-revision"}),u(At(M(c))),e.refresh()}catch(C){u(kt(C))}finally{m(null)}}return{pendingId:d,statusOverride:p,holdNowFrame:he,acknowledgeFrame:ee,commentOnIssueFrame:te,submitApprovalDecision:U,requestApprovalRevision:ne}}import{Fragment as Ft,jsx as r,jsxs as l}from"react/jsx-runtime";function tn(e){let t=Me(e.frame,e.lane),n=e.pendingId===e.frame.id;return t==="none"?null:r("div",{className:S("flex items-center gap-2",e.compact&&"justify-end"),children:t==="approval"?l(Ft,{children:[R(e.frame)?r(N,{label:"Request revision",tone:"secondary",disabled:n,onClick:()=>{e.onRequestRevision(e.frame)}}):null,r(N,{label:n?"Submitting\u2026":"Approve",tone:"primary",disabled:n,onClick:()=>{e.onApprove(e.frame)}}),r(N,{label:"Reject",tone:"danger",disabled:n,onClick:()=>{e.onReject(e.frame)}})]}):r(N,{label:n?"Saving\u2026":"Acknowledge",tone:"accent",disabled:n,onClick:()=>{e.onAcknowledge(e.frame)}})})}function nn(e){let{frame:t,snapshotUpdatedAt:n}=e,o=Y(t,e.companyPrefix),i=R(t)?ct(e.companyPrefix):null,a=Ee(t,e.companyPrefix),s=me(t).length>0;return l("div",{className:"space-y-3 border-t border-border pt-4",children:[r(ut,{frame:t,lane:"now",detailOnly:!0}),l("div",{className:"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground",children:[r("span",{children:at(t)}),r("span",{children:de(t)}),r("span",{children:Z(_(t,n))}),Re(t)?l("span",{children:["Amount requested: ",Re(t)]}):null,Te(t)?r("span",{children:Te(t)}):null]}),s?l(Ft,{children:[r("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:"Context"}),r(lt,{frame:t})]}):null,l("div",{className:"flex flex-wrap items-center gap-3",children:[o?l("a",{href:o,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",children:[le(t),r(V,{})]}):null,a?l("a",{href:a,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",children:["Open activity",r(V,{})]}):null,i?r(v,{children:l("a",{href:i,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-xs font-medium underline underline-offset-2 hover:opacity-80",style:{color:"inherit"},children:["Review in Costs",r(V,{})]})}):null]})]})}function rn(){return l("div",{className:"flex min-h-20 items-center gap-3",children:[r(we,{size:"md"}),r("div",{className:"text-sm text-muted-foreground",children:"Nothing active right now."})]})}function on(e){let t=Me(e.frame,e.lane),n=e.pendingId===e.frame.id,o=t==="acknowledge"&&fe(e.frame);return l("div",{className:"space-y-3 rounded-md border border-border/60 bg-secondary/20 px-3 py-3",style:e.wide?{width:320,flexShrink:0,position:"sticky",top:16,alignSelf:"flex-start"}:void 0,children:[r("div",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:"Actions"}),t==="approval"?l("div",{className:"space-y-2",children:[l("div",{className:"grid grid-cols-2 gap-2",children:[r(N,{label:n?"Submitting\u2026":"Approve",tone:"primary",disabled:n,className:"w-full",onClick:()=>{e.onApprove(e.frame)}}),r(N,{label:"Reject",tone:"danger",disabled:n,className:"w-full",onClick:()=>{e.onReject(e.frame)}})]}),R(e.frame)?r(N,{label:"Request revision",tone:"secondary",disabled:n,className:"w-full",onClick:()=>{e.onRequestRevision(e.frame)}}):null]}):t==="acknowledge"?l("div",{className:"flex flex-wrap items-center gap-2",children:[o?r(qe,{frame:e.frame,pendingId:e.pendingId,onComment:e.onComment,triggerTone:"accent",triggerLabel:"Comment",onOpen:()=>e.onEngageFocus(e.frame,"comment_compose"),rows:4,panelClassName:"w-full space-y-2 rounded-md border border-border bg-background/80 p-3"}):null,r(N,{label:n?"Saving\u2026":"Acknowledge",tone:"accent",disabled:n,onClick:()=>{e.onAcknowledge(e.frame)}})]}):null,e.itemLink?l("a",{href:e.itemLink,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground",children:[le(e.frame),r(V,{})]}):null]})}function an(e){let t=e.snapshot.now,[n,o]=It(!1),i=Qe(1120),a=t?.id??null,s=!!t&&(e.snapshot.review?.unread.now??0)>0,d=t?Y(t,e.companyPrefix):null,m=t?X(t):null,p=t?dt(t,"now"):null,u=Le();return en(()=>{o(!1)},[a]),l("section",{className:"border border-border bg-card shadow-sm",style:t?{borderLeftWidth:3,borderLeftColor:A}:void 0,children:[l("div",{className:"flex items-center justify-between gap-3 border-b border-border bg-accent px-4 py-3 sm:px-6",children:[l("div",{className:"flex items-center gap-3",children:[r(v,{className:"text-sm leading-none",children:e.posture.glyph}),l("div",{className:"flex items-end gap-2",children:[r(v,{className:"text-base leading-none font-semibold tracking-[0.04em]",children:e.brand.wordmark}),r("span",{className:"text-[11px] leading-none text-muted-foreground",children:e.posture.label})]})]}),l("div",{className:"flex items-center gap-3 text-xs tabular-nums text-muted-foreground",children:[l("span",{className:e.counts.now>0?"font-semibold":"",children:["now ",e.counts.now>0?r(v,{children:e.counts.now}):e.counts.now]}),l("span",{className:e.counts.next>0?"font-semibold":"",children:["next ",e.counts.next>0?r(v,{children:e.counts.next}):e.counts.next]}),l("span",{className:e.counts.ambient>0?"font-semibold":"",children:["ambient ",e.counts.ambient>0?r(v,{children:e.counts.ambient}):e.counts.ambient]})]})]}),r("div",{className:"px-5 py-5 sm:px-6",children:t?l("div",{className:"space-y-5",children:[l("div",{className:i?"flex items-start gap-6":"space-y-4",children:[l("div",{className:"space-y-4",style:i?{minWidth:0,flex:1}:void 0,children:[l("div",{className:"flex items-center gap-2 text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground",children:[r("span",{children:"Now"}),r("span",{className:"normal-case tracking-normal text-muted-foreground/70",children:j(t)})]}),m?r("div",{className:"max-w-4xl text-3xl font-semibold leading-tight text-foreground",children:m}):t.summary?r("div",{className:"max-w-4xl text-3xl font-semibold leading-tight text-foreground",children:t.summary}):null,l("div",{className:"flex items-start gap-2 text-xl font-medium leading-snug text-foreground/80",children:[r(oe,{visible:s}),r("span",{children:ue(t)})]}),p?r("p",{className:"max-w-3xl text-sm leading-relaxed text-muted-foreground",children:p}):null,l("div",{className:"flex flex-wrap items-center gap-2",children:[r(q,{className:"border-border bg-secondary text-foreground/80",children:de(t)}),H(t)?r(q,{className:u.className,style:u.style,children:H(t)}):null]}),r(Ue,{frame:t,lane:"now",preferLaneReason:!!p}),r("div",{className:"flex items-center gap-4",children:r(v,{children:l("button",{type:"button",onClick:()=>{n||e.onEngageFocus(t,"show_context"),o(!n)},"aria-expanded":n,className:"flex items-center gap-1.5 text-xs font-medium transition-opacity hover:opacity-100",style:{color:"inherit"},children:[r("svg",{viewBox:"0 0 16 16",className:S("h-3 w-3 transition-transform",n&&"rotate-90"),fill:"currentColor","aria-hidden":"true",children:r("path",{d:"M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z"})}),n?"Hide context":"Show context"]})})})]}),r(on,{frame:t,lane:"now",wide:i,pendingId:e.pendingId,itemLink:d,onEngageFocus:e.onEngageFocus,onApprove:e.onApprove,onReject:e.onReject,onRequestRevision:e.onRequestRevision,onAcknowledge:e.onAcknowledge,onComment:e.onComment})]}),n?r(nn,{frame:t,snapshotUpdatedAt:e.snapshot.updatedAt,companyPrefix:e.companyPrefix}):null]}):r(rn,{})})]})}function sn(e){let{frame:t}=e,[n,o]=It(!1),i=_e(t,e.snapshot),a=Ee(t,e.companyPrefix),s=Y(t,e.companyPrefix),d=st(t),m=H(t),p=Le(),u=O(t,"next"),x=u.trim().length>0&&u!==d&&u!==Q,y=Math.max(.4,1-(e.rank-1)*.14);return l("div",{className:"border-b border-border/80 last:border-b-0",style:n?{borderLeftWidth:2,borderLeftColor:A}:void 0,children:[l("button",{type:"button",onClick:()=>o(!n),"aria-expanded":n,className:"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50 sm:px-6",children:[r(oe,{visible:i}),r("span",{className:"w-5 shrink-0",style:{opacity:y},children:r(v,{className:"text-xs font-medium tabular-nums",children:String(e.rank).padStart(2,"0")})}),l("div",{className:"min-w-0 flex-1",children:[r("div",{className:"text-sm font-medium leading-5 text-foreground",children:ue(t)}),d?r("div",{className:"mt-0.5 max-w-3xl text-xs leading-5 text-muted-foreground",children:d}):null]}),r(Je,{movement:e.movement}),m?r(q,{className:S("shrink-0",p.className),style:p.style,children:m}):null,r("span",{className:"shrink-0 text-xs text-muted-foreground",style:{opacity:.7},children:Z(_(t,e.snapshotUpdatedAt))}),r("svg",{viewBox:"0 0 16 16",className:S("h-3 w-3 shrink-0 text-muted-foreground transition-transform",n&&"rotate-90"),fill:"currentColor","aria-hidden":"true",children:r("path",{d:"M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z"})})]}),n?l("div",{className:"space-y-3 px-4 pb-4 pl-12 sm:px-6 sm:pl-14",children:[x?r("p",{className:"text-sm leading-relaxed text-muted-foreground",children:u}):null,r(mt,{frame:t,lane:"next"}),l("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground",children:[r("span",{children:de(t)}),m?r(v,{children:m}):null,e.movement?r(v,{children:e.movement==="up"?"rising in queue":"falling in queue"}):null,l("span",{children:["updated ",Z(_(t,e.snapshotUpdatedAt))]}),s?r("a",{href:s,className:"font-medium underline underline-offset-2 hover:text-foreground",children:le(t)}):null,a?r("a",{href:a,className:"font-medium underline underline-offset-2 hover:text-foreground",children:"Open activity"}):null]}),l("div",{className:"flex flex-wrap items-center gap-2 pt-1",children:[r(qe,{frame:t,pendingId:e.pendingId,onComment:e.onComment}),r(tn,{frame:t,lane:"next",pendingId:e.pendingId,onApprove:e.onApprove,onReject:e.onReject,onRequestRevision:e.onRequestRevision,onAcknowledge:e.onAcknowledge})]})]}):null]})}function dn(e){return l("section",{className:"border border-border/80 bg-card",children:[r("div",{className:"border-b border-border/80 px-4 py-2.5 sm:px-6",children:r("h2",{className:"text-xs font-medium uppercase tracking-wider text-muted-foreground",children:"Next"})}),e.rows.length===0?r("div",{className:"px-4 py-4 text-sm text-muted-foreground sm:px-6",style:{opacity:.7},children:"Nothing is staged behind the current focus."}):e.rows.map((t,n)=>r(sn,{rank:n+1,frame:t.frame,movement:e.movement[t.frame.interactionId],snapshot:e.snapshot,snapshotUpdatedAt:e.snapshotUpdatedAt,companyPrefix:e.companyPrefix,pendingId:e.pendingId,onApprove:e.onApprove,onReject:e.onReject,onRequestRevision:e.onRequestRevision,onAcknowledge:e.onAcknowledge,onComment:e.onComment},t.frame.id))]})}function cn(e){let{frame:t,snapshotUpdatedAt:n}=e,o=Y(t,e.companyPrefix),i=_e(t,e.snapshot),a=l("div",{className:"flex items-center gap-3 px-4 py-2.5 sm:px-6",children:[r(oe,{visible:i,tone:"muted",reserveSpace:!0}),r("span",{className:"min-w-0 flex-1 truncate text-[13px] text-muted-foreground",children:ue(t)}),r("span",{className:"shrink-0 text-xs text-muted-foreground/80",children:j(t)}),r("span",{className:"shrink-0 text-xs text-muted-foreground/70",children:Z(_(t,n))})]});return r("div",{style:{borderBottomWidth:1,borderBottomColor:"var(--border)"},className:"last:border-b-0",children:o?r("a",{href:o,className:"block transition-colors hover:bg-accent/40",children:a}):a})}function ln(e){return l("section",{style:{borderWidth:1,borderColor:"var(--border)"},className:"mt-2 bg-card","aria-label":"Ambient attention",children:[r("div",{style:{borderBottomWidth:1,borderBottomColor:"var(--border)"},className:"px-4 py-2.5 sm:px-6",children:r("h2",{className:"text-xs font-medium uppercase tracking-wider text-muted-foreground/80",children:"Ambient"})}),e.rows.length===0?l("div",{className:"flex items-center gap-3 px-4 py-4 text-sm text-muted-foreground sm:px-6",children:[r(we,{}),r("span",{children:"Nothing in peripheral view."})]}):e.rows.map(t=>r(cn,{frame:t.frame,snapshot:e.snapshot,snapshotUpdatedAt:e.snapshotUpdatedAt,companyPrefix:e.companyPrefix},t.frame.id))]})}function xr(e){let t=e.context.companyId,n=ie(),o=Se(e.context.companyPrefix),i=ae(t),a=i.snapshot,s=i.loading,d=i.error;if(!t)return r(D,{title:`Open ${n.wordmark}`,body:`Open a company to see ${n.wordmark}.`});if(s)return r(Ge,{label:n.loadingLabel});if(d)return r(D,{title:"Plugin error",body:d.message});if(!a)return r(D,{title:n.wordmark,body:n.headingEmptyState});let m=Ne(a),p=a.now?X(a.now)??a.now.title:null,u=Xe(a),x=m.label==="busy"?{borderLeftWidth:2,borderLeftColor:A}:m.label==="elevated"?{borderLeftWidth:2,borderLeftColor:F}:void 0;return l("a",{href:o,className:"block border border-border bg-card px-4 py-3 shadow-sm transition-colors hover:bg-accent/40",style:x,children:[l("div",{className:"flex items-start gap-2 text-xs",children:[r(v,{className:"pt-0.5 text-sm",children:m.glyph}),r("div",{className:"min-w-0",children:l("div",{className:"flex items-end gap-2",children:[r(v,{className:"font-semibold tracking-[0.04em]",children:n.wordmark}),r(v,{className:"text-[11px]",children:m.label}),u?r(v,{className:"text-[11px]",children:u}):null]})}),l("span",{className:"ml-auto tabular-nums text-muted-foreground",children:["now ",a.counts.now," \xB7 next ",a.counts.next," \xB7 ambient ",a.counts.ambient]})]}),p?r("div",{className:"mt-2 text-sm font-medium leading-5 text-foreground",style:{display:"-webkit-box",WebkitLineClamp:2,WebkitBoxOrient:"vertical",overflow:"hidden"},children:p}):r("div",{className:"mt-2 text-sm text-muted-foreground",children:"No active interruption right now."})]})}function hr({context:e}){let t=e.companyId,n=ie(),o=Se(e.companyPrefix),i=typeof window<"u"&&window.location.pathname===o,s=ae(t).snapshot,d=s?Ie(s):0,m=Ye(s);return l("a",{href:o,"aria-current":i?"page":void 0,className:S("flex items-center gap-2.5 px-3 py-2 text-[13px] font-medium transition-colors",i?"bg-accent text-foreground":"text-foreground/80 hover:bg-accent/50 hover:text-foreground"),children:[l("span",{className:"relative shrink-0","aria-hidden":"true",children:[l("svg",{viewBox:"0 0 24 24",className:"h-4 w-4",fill:"none",stroke:"currentColor",strokeWidth:"1.9",strokeLinecap:"round",strokeLinejoin:"round",children:[r("circle",{cx:"12",cy:"12",r:"9"}),r("path",{d:"m14.3 8 5.2 9"}),r("path",{d:"M9.7 8h10.4"}),r("path",{d:"m7.4 12 5.2-9"}),r("path",{d:"m9.7 16-5.2-9"}),r("path",{d:"M14.3 16H3.9"}),r("path",{d:"m16.6 12-5.2 9"})]}),m>0?r("span",{className:"absolute -right-1 -top-1 h-2 w-2 rounded-full",style:{backgroundColor:A}}):null]}),r("span",{className:"flex-1 truncate",children:n.wordmark}),d>0?r("span",{className:"inline-flex items-center justify-center rounded-full px-1.5 py-0.5 text-xs leading-none font-medium text-white",style:re,children:d}):m>0?r("span",{className:"inline-flex items-center justify-center rounded-full border px-1.5 py-0.5 text-xs leading-none font-medium",style:{color:A,borderColor:F,backgroundColor:"transparent"},children:m}):null]})}function br(e){let t=e.context.companyId,n=ie(),o=ae(t),i=o.snapshot,a=o.loading,s=o.error,d=i,m=d&&s?`Sync issue: ${s.message}`:null,p=He(()=>d?Ne(d):null,[d]),u=He(()=>d?d.next.map(c=>({frame:c,lane:"next"})):[],[d]),x=He(()=>d?d.ambient.map(c=>({frame:c,lane:"ambient"})):[],[d]),y=et(u.map(c=>c.frame)),{pendingId:P,statusOverride:z,holdNowFrame:he,acknowledgeFrame:ee,commentOnIssueFrame:te,submitApprovalDecision:U,requestApprovalRevision:ne}=Nt({companyId:t,displaySnapshot:d,refresh:o.refresh});return t?!d&&a?r(Ve,{label:"Loading attention center"}):!d&&s?r(D,{title:"Plugin error",body:s.message}):!d||!p?r(D,{title:`No ${n.wordmark} State Yet`,body:`No ${n.key==="focus"?"focus":"attention"} state has been captured for this company yet.`}):l("div",{className:"space-y-4",children:[r(an,{snapshot:d,posture:p,brand:n,companyPrefix:e.context.companyPrefix,counts:d.counts,pendingId:P,onEngageFocus:he,onApprove:c=>U(c,"approve"),onReject:c=>U(c,"reject"),onRequestRevision:ne,onAcknowledge:ee,onComment:te}),r(dn,{snapshot:d,rows:u,movement:y,snapshotUpdatedAt:d.updatedAt,companyPrefix:e.context.companyPrefix,pendingId:P,onApprove:c=>U(c,"approve"),onReject:c=>U(c,"reject"),onRequestRevision:ne,onAcknowledge:ee,onComment:te}),r(ln,{snapshot:d,rows:x,snapshotUpdatedAt:d.updatedAt,companyPrefix:e.context.companyPrefix}),r(Ze,{message:z??m})]}):r(D,{title:`Open ${n.wordmark}`,body:`Select a company to open ${n.wordmark}.`})}export{br as AttentionPage,hr as AttentionSidebarLink,xr as DashboardWidget};
|