@tomismeta/paperclip-aperture 0.2.2 → 0.3.1
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 +22 -1
- package/dist/manifest.js +1 -1
- package/dist/ui/index.js +522 -243
- package/dist/worker.js +20 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ and agents attention now? actually sees to th
|
|
|
64
64
|
|
|
65
65
|
- a Focus surface inside Paperclip
|
|
66
66
|
- ranked `now`, `next`, and `ambient` attention lanes
|
|
67
|
+
- embedded explainability in the Focus UI, including `Why now`, `Why next`, confidence, signals, thread context, and related activity
|
|
67
68
|
- approval handling, including budget-specific approval semantics
|
|
68
69
|
- issue-aware operator language such as `review required`, `blocked`, and targeted recommended moves
|
|
69
70
|
- agent-aware routing that distinguishes known company agents from human/operator roles when issue text references them
|
|
@@ -75,13 +76,31 @@ and agents attention now? actually sees to th
|
|
|
75
76
|
- durable acknowledge/suppression behavior backed by plugin state and ledger replay
|
|
76
77
|
- a sidebar entry, page, and dashboard widget
|
|
77
78
|
|
|
79
|
+
## Explainability
|
|
80
|
+
|
|
81
|
+
Focus is meant to be more legible than a smart inbox.
|
|
82
|
+
|
|
83
|
+
The current `0.3.x` explainability slice keeps reasoning attached to the cards you are already acting on:
|
|
84
|
+
|
|
85
|
+
- `Why now` on the active card explains why the current item outranks the rest of the queue
|
|
86
|
+
- `Why next` on queued rows explains why something is staged behind the current top item
|
|
87
|
+
- `Confidence` shows how strong the semantic signal is when the plugin has one
|
|
88
|
+
- `Signals` surfaces the specific factors that pushed the item into attention
|
|
89
|
+
- `Thread context` and `Related activity` help explain whether an item is part of a broader episode or continuation
|
|
90
|
+
|
|
91
|
+
The intent is not to expose every internal scoring detail. It is to help an operator answer:
|
|
92
|
+
|
|
93
|
+
- why did this surface?
|
|
94
|
+
- why is it in this lane?
|
|
95
|
+
- how much should I trust this judgment?
|
|
96
|
+
|
|
78
97
|
## Package Boundary
|
|
79
98
|
|
|
80
99
|
This plugin treats Paperclip as the host runtime and UI shell, while embedding [Aperture Core](https://github.com/tomismeta/aperture/tree/main/packages/core) through the npm package [`@tomismeta/aperture-core`](https://www.npmjs.com/package/@tomismeta/aperture-core).
|
|
81
100
|
|
|
82
101
|
It is a pure SDK integration: Aperture Core is used as-is inside a self-contained Paperclip plugin, with no changes to Aperture Core or Paperclip core.
|
|
83
102
|
|
|
84
|
-
For `0.
|
|
103
|
+
For `0.3.x`, the boundary works like this:
|
|
85
104
|
|
|
86
105
|
- the plugin worker owns Aperture ingestion, replay, review state, and display composition
|
|
87
106
|
- Paperclip remains the system of record for issue and approval writes
|
|
@@ -129,6 +148,8 @@ Then open `http://127.0.0.1:3100/APE/aperture` and verify:
|
|
|
129
148
|
- approval actions update Focus correctly
|
|
130
149
|
- resolved blocker comments downgrade stale `Now` items
|
|
131
150
|
- attached issue documents downgrade stale `share the memo/spec` review blockers into monitor-only follow-up
|
|
151
|
+
- the active card exposes `Why now`
|
|
152
|
+
- expanded queued rows expose `Why next`
|
|
132
153
|
|
|
133
154
|
## Links
|
|
134
155
|
|
package/dist/manifest.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const manifest = {
|
|
2
2
|
id: "tomismeta.paperclip-aperture",
|
|
3
3
|
apiVersion: 1,
|
|
4
|
-
version: "0.
|
|
4
|
+
version: "0.3.1",
|
|
5
5
|
displayName: "Paperclip Aperture",
|
|
6
6
|
description: "The live attention layer for Paperclip, powered by Aperture's deterministic attention engine.",
|
|
7
7
|
author: "@tomismeta",
|
package/dist/ui/index.js
CHANGED
|
@@ -123,6 +123,82 @@ function mergeStoredFrames(snapshot, companyId, candidates, review, nowIso = (/*
|
|
|
123
123
|
}, review);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// src/aperture/explainability.ts
|
|
127
|
+
function asRecord(value) {
|
|
128
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
129
|
+
}
|
|
130
|
+
function readStringArray(value) {
|
|
131
|
+
if (!Array.isArray(value)) return [];
|
|
132
|
+
return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
133
|
+
}
|
|
134
|
+
function readSemanticConfidence(frame) {
|
|
135
|
+
const semantic = asRecord(frame.metadata?.semantic);
|
|
136
|
+
const confidence = semantic?.confidence;
|
|
137
|
+
return confidence === "low" || confidence === "medium" || confidence === "high" ? confidence : null;
|
|
138
|
+
}
|
|
139
|
+
function readSemanticRelationHints(frame) {
|
|
140
|
+
const semantic = asRecord(frame.metadata?.semantic);
|
|
141
|
+
const relationHints = semantic?.relationHints;
|
|
142
|
+
if (!Array.isArray(relationHints)) return [];
|
|
143
|
+
return relationHints.filter((entry) => {
|
|
144
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
145
|
+
const hint = entry;
|
|
146
|
+
return hint.kind === "same_issue" || hint.kind === "resolves" || hint.kind === "supersedes" || hint.kind === "repeats" || hint.kind === "escalates";
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function relationHintLabel(hint) {
|
|
150
|
+
switch (hint.kind) {
|
|
151
|
+
case "same_issue":
|
|
152
|
+
return "Part of the same thread";
|
|
153
|
+
case "resolves":
|
|
154
|
+
return "Resolves an earlier blocker";
|
|
155
|
+
case "supersedes":
|
|
156
|
+
return "Moves the request forward";
|
|
157
|
+
case "repeats":
|
|
158
|
+
return "Repeats an earlier ask";
|
|
159
|
+
case "escalates":
|
|
160
|
+
return "Raises the urgency";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function laneReason(lane) {
|
|
164
|
+
switch (lane) {
|
|
165
|
+
case "active":
|
|
166
|
+
return "This is the most urgent item in the queue right now.";
|
|
167
|
+
case "queued":
|
|
168
|
+
return "This is queued behind the current top item.";
|
|
169
|
+
case "ambient":
|
|
170
|
+
return "This is visible for awareness without needing action yet.";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function continuitySummary(frame) {
|
|
174
|
+
const episode = asRecord(frame.metadata?.episode);
|
|
175
|
+
const size = typeof episode?.size === "number" ? episode.size : null;
|
|
176
|
+
const state = typeof episode?.state === "string" ? episode.state.replace(/_/g, " ") : null;
|
|
177
|
+
if (!size || size <= 1) return null;
|
|
178
|
+
if (state) return `Part of a ${state} thread with ${size} related interactions.`;
|
|
179
|
+
return `Part of a thread with ${size} related interactions.`;
|
|
180
|
+
}
|
|
181
|
+
function frameSignals(frame) {
|
|
182
|
+
const attention = asRecord(frame.metadata?.attention);
|
|
183
|
+
const rationale = readStringArray(attention?.rationale);
|
|
184
|
+
const factors = readStringArray(frame.provenance?.factors);
|
|
185
|
+
return [.../* @__PURE__ */ new Set([...rationale.length > 0 ? rationale : factors, ...factors])];
|
|
186
|
+
}
|
|
187
|
+
function signalStrengthLabel(confidence) {
|
|
188
|
+
return `${confidence} confidence`;
|
|
189
|
+
}
|
|
190
|
+
function explainFrame(frame, lane) {
|
|
191
|
+
const relationLabels = [...new Set(readSemanticRelationHints(frame).map(relationHintLabel))];
|
|
192
|
+
return {
|
|
193
|
+
whyNow: frame.provenance?.whyNow ?? frame.summary ?? null,
|
|
194
|
+
laneReason: laneReason(lane),
|
|
195
|
+
signalStrength: readSemanticConfidence(frame),
|
|
196
|
+
signals: frameSignals(frame),
|
|
197
|
+
relationLabels,
|
|
198
|
+
continuity: continuitySummary(frame)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
126
202
|
// src/aperture/attention-context.ts
|
|
127
203
|
var ATTENTION_CONTEXT_IDS = {
|
|
128
204
|
approvalType: "approval-type",
|
|
@@ -223,6 +299,10 @@ function approvalRecordToFrame(record) {
|
|
|
223
299
|
const decisionContext = typeof payload.decisionContext === "string" ? payload.decisionContext : void 0;
|
|
224
300
|
const summary = typeof payload.summary === "string" ? payload.summary : approvalBlockingSummary(budgetOverride);
|
|
225
301
|
const updatedAt = record.updatedAt ?? record.createdAt;
|
|
302
|
+
const provenance = {
|
|
303
|
+
whyNow: approvalBlockingWhyNow(budgetOverride),
|
|
304
|
+
factors: budgetOverride ? ["budget stop", "approval", "operator decision"] : ["approval", "operator decision"]
|
|
305
|
+
};
|
|
226
306
|
return {
|
|
227
307
|
id: `approval-bootstrap:${record.id}`,
|
|
228
308
|
taskId: `approval:${record.id}`,
|
|
@@ -256,17 +336,20 @@ function approvalRecordToFrame(record) {
|
|
|
256
336
|
...budgetOverride ? [{ id: "request-revision", label: "Request revision", kind: "cancel", emphasis: "secondary" }] : []
|
|
257
337
|
]
|
|
258
338
|
},
|
|
259
|
-
provenance
|
|
260
|
-
whyNow: approvalBlockingWhyNow(budgetOverride),
|
|
261
|
-
factors: budgetOverride ? ["budget stop", "approval", "operator decision"] : ["approval", "operator decision"]
|
|
262
|
-
},
|
|
339
|
+
provenance,
|
|
263
340
|
timing: {
|
|
264
341
|
createdAt: record.createdAt,
|
|
265
342
|
updatedAt
|
|
266
343
|
},
|
|
267
344
|
metadata: {
|
|
268
345
|
approvalStatus: record.status,
|
|
269
|
-
approvalType: record.type
|
|
346
|
+
approvalType: record.type,
|
|
347
|
+
attention: {
|
|
348
|
+
rationale: provenance.factors
|
|
349
|
+
},
|
|
350
|
+
semantic: {
|
|
351
|
+
confidence: "high"
|
|
352
|
+
}
|
|
270
353
|
}
|
|
271
354
|
};
|
|
272
355
|
}
|
|
@@ -325,12 +408,13 @@ function approvalRevisionFailureMessage(error) {
|
|
|
325
408
|
// src/ui/index.tsx
|
|
326
409
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
327
410
|
var ACCENT_COLOR = "#007ACC";
|
|
411
|
+
var ACCENT_BG = `${ACCENT_COLOR}14`;
|
|
412
|
+
var ACCENT_BORDER = `${ACCENT_COLOR}33`;
|
|
328
413
|
var ACCENT_BG_STYLE = { backgroundColor: ACCENT_COLOR };
|
|
329
414
|
function currentSurfaceBrand() {
|
|
330
415
|
return {
|
|
331
416
|
key: "focus",
|
|
332
417
|
wordmark: "Focus",
|
|
333
|
-
supportCopy: "Powered by Aperture",
|
|
334
418
|
headingEmptyState: "No focus state yet.",
|
|
335
419
|
loadingLabel: "Loading Focus\u2026"
|
|
336
420
|
};
|
|
@@ -346,6 +430,29 @@ function Accent({ children, className }) {
|
|
|
346
430
|
const ref = useAccentColor();
|
|
347
431
|
return /* @__PURE__ */ jsx("span", { ref, className, children });
|
|
348
432
|
}
|
|
433
|
+
function QuietMark({ size = "sm" }) {
|
|
434
|
+
const dimension = size === "md" ? 10 : 8;
|
|
435
|
+
return /* @__PURE__ */ jsx(
|
|
436
|
+
"span",
|
|
437
|
+
{
|
|
438
|
+
"aria-hidden": "true",
|
|
439
|
+
className: "inline-block shrink-0 rounded-full border",
|
|
440
|
+
style: { width: dimension, height: dimension, borderColor: ACCENT_BORDER }
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
function useWideLayout(minWidth = 1024) {
|
|
445
|
+
const [matches, setMatches] = useState(false);
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
448
|
+
const query = window.matchMedia(`(min-width: ${minWidth}px)`);
|
|
449
|
+
const update = () => setMatches(query.matches);
|
|
450
|
+
update();
|
|
451
|
+
query.addEventListener("change", update);
|
|
452
|
+
return () => query.removeEventListener("change", update);
|
|
453
|
+
}, [minWidth]);
|
|
454
|
+
return matches;
|
|
455
|
+
}
|
|
349
456
|
function cn(...parts) {
|
|
350
457
|
return parts.filter(Boolean).join(" ");
|
|
351
458
|
}
|
|
@@ -418,7 +525,7 @@ function driverLabel(frame) {
|
|
|
418
525
|
function driverBadgeStyle() {
|
|
419
526
|
return {
|
|
420
527
|
className: "border-transparent",
|
|
421
|
-
style: { color: ACCENT_COLOR, backgroundColor:
|
|
528
|
+
style: { color: ACCENT_COLOR, backgroundColor: ACCENT_BG, borderColor: ACCENT_BORDER }
|
|
422
529
|
};
|
|
423
530
|
}
|
|
424
531
|
function requestDescriptor(frame) {
|
|
@@ -459,6 +566,15 @@ function supportingLine(frame, lane) {
|
|
|
459
566
|
if (owner && lane === "active" && entityTypeFromFrame(frame) === "issue") {
|
|
460
567
|
return issueNeedsActionFromLine(owner);
|
|
461
568
|
}
|
|
569
|
+
if (lane === "active") {
|
|
570
|
+
const recommendedMove = recommendedMoveValue(frame)?.trim();
|
|
571
|
+
const summary = frame.summary?.trim();
|
|
572
|
+
const judgment2 = judgmentLine(frame, lane).trim();
|
|
573
|
+
if (recommendedMove && summary && summary !== recommendedMove && summary !== judgment2) {
|
|
574
|
+
return summary;
|
|
575
|
+
}
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
462
578
|
const judgment = judgmentLine(frame, lane);
|
|
463
579
|
return judgment.trim().length > 0 ? judgment : null;
|
|
464
580
|
}
|
|
@@ -552,9 +668,11 @@ function isFrameUnreadInSnapshot(frame, snapshot) {
|
|
|
552
668
|
if (!seenAt) return true;
|
|
553
669
|
return frameUpdatedAt(frame, snapshot.updatedAt).localeCompare(seenAt) > 0;
|
|
554
670
|
}
|
|
555
|
-
function UnreadDot(
|
|
556
|
-
|
|
557
|
-
|
|
671
|
+
function UnreadDot(props) {
|
|
672
|
+
const tone = props.tone ?? "default";
|
|
673
|
+
if (!props.visible && !props.reserveSpace) return null;
|
|
674
|
+
const style = props.visible ? tone === "muted" ? { backgroundColor: ACCENT_BORDER } : ACCENT_BG_STYLE : { backgroundColor: "transparent" };
|
|
675
|
+
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 });
|
|
558
676
|
}
|
|
559
677
|
function ExternalLinkIcon() {
|
|
560
678
|
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: [
|
|
@@ -619,7 +737,7 @@ function StatusToast({ message }) {
|
|
|
619
737
|
"div",
|
|
620
738
|
{
|
|
621
739
|
className: cn(
|
|
622
|
-
"pointer-events-none fixed bottom-6
|
|
740
|
+
"pointer-events-none fixed bottom-6 left-1/2 z-50 -translate-x-1/2 transition-all duration-200",
|
|
623
741
|
message ? "translate-y-0 opacity-100" : "translate-y-2 opacity-0"
|
|
624
742
|
),
|
|
625
743
|
"aria-live": "polite",
|
|
@@ -836,21 +954,26 @@ function Badge(props) {
|
|
|
836
954
|
);
|
|
837
955
|
}
|
|
838
956
|
function ActionButton(props) {
|
|
839
|
-
const
|
|
957
|
+
const [hovered, setHovered] = useState(false);
|
|
958
|
+
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";
|
|
840
959
|
return /* @__PURE__ */ jsx(
|
|
841
960
|
"button",
|
|
842
961
|
{
|
|
843
962
|
type: "button",
|
|
844
963
|
onClick: props.onClick,
|
|
964
|
+
onMouseEnter: () => setHovered(true),
|
|
965
|
+
onMouseLeave: () => setHovered(false),
|
|
845
966
|
disabled: props.disabled,
|
|
846
967
|
className: cn(
|
|
847
968
|
"inline-flex h-8 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium",
|
|
848
|
-
"transition-[color,background-color,border-color,box-shadow,opacity]",
|
|
969
|
+
"transition-[color,background-color,border-color,box-shadow,opacity,filter]",
|
|
849
970
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
850
971
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
972
|
+
props.disabled && /…|\.\.\./.test(props.label) && "animate-pulse",
|
|
851
973
|
toneClass,
|
|
852
974
|
props.className
|
|
853
975
|
),
|
|
976
|
+
style: props.tone === "accent" ? { ...ACCENT_BG_STYLE, filter: hovered && !props.disabled ? "brightness(1.08)" : void 0 } : void 0,
|
|
854
977
|
children: props.label
|
|
855
978
|
}
|
|
856
979
|
);
|
|
@@ -863,8 +986,8 @@ function QueueMovementBadge({ movement }) {
|
|
|
863
986
|
className: "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium",
|
|
864
987
|
style: {
|
|
865
988
|
color: ACCENT_COLOR,
|
|
866
|
-
backgroundColor:
|
|
867
|
-
borderColor:
|
|
989
|
+
backgroundColor: ACCENT_BG,
|
|
990
|
+
borderColor: ACCENT_BORDER
|
|
868
991
|
},
|
|
869
992
|
children: [
|
|
870
993
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: movement === "up" ? "\u2191" : "\u2193" }),
|
|
@@ -909,21 +1032,24 @@ function FrameActions(props) {
|
|
|
909
1032
|
ActionButton,
|
|
910
1033
|
{
|
|
911
1034
|
label: isPending ? "Saving\u2026" : "Acknowledge",
|
|
912
|
-
tone: "
|
|
1035
|
+
tone: "accent",
|
|
913
1036
|
disabled: isPending,
|
|
914
1037
|
onClick: () => void props.onAcknowledge(props.frame)
|
|
915
1038
|
}
|
|
916
1039
|
) });
|
|
917
1040
|
}
|
|
1041
|
+
function visibleContextItems(frame) {
|
|
1042
|
+
return (frame.context?.items ?? []).filter((item2) => item2.id !== ATTENTION_CONTEXT_IDS.recommendedMove);
|
|
1043
|
+
}
|
|
918
1044
|
function ContextItems({ frame }) {
|
|
919
|
-
const items = frame
|
|
1045
|
+
const items = visibleContextItems(frame);
|
|
920
1046
|
if (items.length === 0) return null;
|
|
921
1047
|
return /* @__PURE__ */ jsx("div", { className: "grid gap-2 md:grid-cols-2", children: items.map((item2) => /* @__PURE__ */ jsxs(
|
|
922
1048
|
"div",
|
|
923
1049
|
{
|
|
924
1050
|
className: cn(
|
|
925
1051
|
"border border-border bg-secondary/50 px-3 py-2",
|
|
926
|
-
|
|
1052
|
+
item2.id === ATTENTION_CONTEXT_IDS.latestComment && "md:col-span-2"
|
|
927
1053
|
),
|
|
928
1054
|
children: [
|
|
929
1055
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: item2.label }),
|
|
@@ -933,14 +1059,90 @@ function ContextItems({ frame }) {
|
|
|
933
1059
|
item2.id
|
|
934
1060
|
)) });
|
|
935
1061
|
}
|
|
1062
|
+
function ExplainabilityBadges(props) {
|
|
1063
|
+
if (props.values.length === 0) return null;
|
|
1064
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: props.values.map((value) => /* @__PURE__ */ jsx(
|
|
1065
|
+
Badge,
|
|
1066
|
+
{
|
|
1067
|
+
className: props.accent ? "border-transparent" : "border-border bg-background/70 text-foreground/80",
|
|
1068
|
+
style: props.accent ? { color: ACCENT_COLOR, backgroundColor: ACCENT_BG, borderColor: ACCENT_BORDER } : void 0,
|
|
1069
|
+
children: value
|
|
1070
|
+
},
|
|
1071
|
+
value
|
|
1072
|
+
)) });
|
|
1073
|
+
}
|
|
1074
|
+
function InlineExplainability(props) {
|
|
1075
|
+
const explanation = explainFrame(props.frame, props.lane);
|
|
1076
|
+
const chips = [
|
|
1077
|
+
...explanation.signalStrength ? [signalStrengthLabel(explanation.signalStrength)] : [],
|
|
1078
|
+
...explanation.signals.slice(0, 2),
|
|
1079
|
+
...explanation.relationLabels.slice(0, 1)
|
|
1080
|
+
];
|
|
1081
|
+
const label = props.lane === "queued" ? "Why next" : props.lane === "ambient" ? "Why ambient" : "Why now";
|
|
1082
|
+
const whyNow = explanation.whyNow ?? judgmentLine(props.frame, props.lane);
|
|
1083
|
+
const primaryLine = props.lane === "active" ? props.preferLaneReason ? explanation.laneReason : whyNow : whyNow;
|
|
1084
|
+
const secondaryLine = props.lane === "active" ? !props.preferLaneReason && whyNow !== explanation.laneReason ? explanation.laneReason : null : explanation.laneReason;
|
|
1085
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-l-2 pl-4", style: { borderColor: ACCENT_COLOR }, children: [
|
|
1086
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: label }),
|
|
1087
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-3xl text-sm leading-relaxed text-foreground/90", children: primaryLine }),
|
|
1088
|
+
secondaryLine ? /* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: secondaryLine }) : null,
|
|
1089
|
+
chips.length > 0 ? /* @__PURE__ */ jsx(ExplainabilityBadges, { values: chips, accent: true }) : null
|
|
1090
|
+
] });
|
|
1091
|
+
}
|
|
1092
|
+
function ExplainabilityPanel(props) {
|
|
1093
|
+
const explanation = explainFrame(props.frame, props.lane);
|
|
1094
|
+
const strength = !props.detailOnly && explanation.signalStrength ? signalStrengthLabel(explanation.signalStrength) : null;
|
|
1095
|
+
const signalValues = props.detailOnly ? explanation.signals.slice(2) : explanation.signals;
|
|
1096
|
+
const relationValues = props.detailOnly ? explanation.relationLabels.slice(1) : explanation.relationLabels;
|
|
1097
|
+
const reasoningLabel = props.lane === "active" ? "Reasoning" : props.lane === "queued" ? "Why it sits here" : "Why it stays quiet";
|
|
1098
|
+
if (props.detailOnly && signalValues.length === 0 && relationValues.length === 0 && !explanation.continuity) {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1102
|
+
!props.detailOnly ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem]", children: [
|
|
1103
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1104
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: reasoningLabel }),
|
|
1105
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm leading-relaxed text-muted-foreground", children: explanation.laneReason })
|
|
1106
|
+
] }),
|
|
1107
|
+
strength ? /* @__PURE__ */ jsxs("div", { children: [
|
|
1108
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Confidence" }),
|
|
1109
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-foreground/90", children: strength })
|
|
1110
|
+
] }) : null
|
|
1111
|
+
] }) : null,
|
|
1112
|
+
signalValues.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1113
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: props.detailOnly ? "More signals" : "Signals" }),
|
|
1114
|
+
/* @__PURE__ */ jsx(ExplainabilityBadges, { values: signalValues })
|
|
1115
|
+
] }) : null,
|
|
1116
|
+
relationValues.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1117
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: props.detailOnly ? "More thread context" : "Thread context" }),
|
|
1118
|
+
/* @__PURE__ */ jsx(ExplainabilityBadges, { values: relationValues, accent: true })
|
|
1119
|
+
] }) : null,
|
|
1120
|
+
explanation.continuity ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1121
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Related activity" }),
|
|
1122
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-muted-foreground", children: explanation.continuity })
|
|
1123
|
+
] }) : null
|
|
1124
|
+
] });
|
|
1125
|
+
}
|
|
1126
|
+
function ExplainabilityStrip(props) {
|
|
1127
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-2 border-t border-border/60 pt-3", children: /* @__PURE__ */ jsx(InlineExplainability, { frame: props.frame, lane: props.lane }) });
|
|
1128
|
+
}
|
|
936
1129
|
function isIssueFrame(frame) {
|
|
937
1130
|
return entityTypeFromFrame(frame) === "issue";
|
|
938
1131
|
}
|
|
939
1132
|
function IssueCommentComposer(props) {
|
|
940
1133
|
const [open, setOpen] = useState(false);
|
|
941
1134
|
const [body, setBody] = useState("");
|
|
942
|
-
|
|
1135
|
+
const textareaRef = useRef(null);
|
|
1136
|
+
const issueFrame = isIssueFrame(props.frame);
|
|
943
1137
|
const isPending = props.pendingId === props.frame.id;
|
|
1138
|
+
useEffect(() => {
|
|
1139
|
+
setOpen(false);
|
|
1140
|
+
setBody("");
|
|
1141
|
+
}, [props.frame.id]);
|
|
1142
|
+
useEffect(() => {
|
|
1143
|
+
if (open && issueFrame) textareaRef.current?.focus();
|
|
1144
|
+
}, [open, issueFrame]);
|
|
1145
|
+
if (!issueFrame) return null;
|
|
944
1146
|
async function submit() {
|
|
945
1147
|
const nextBody = body.trim();
|
|
946
1148
|
if (!nextBody) return;
|
|
@@ -967,30 +1169,42 @@ function IssueCommentComposer(props) {
|
|
|
967
1169
|
{
|
|
968
1170
|
label,
|
|
969
1171
|
tone: triggerTone,
|
|
970
|
-
className: "w-full",
|
|
1172
|
+
className: props.triggerFullWidth ? "w-full" : void 0,
|
|
971
1173
|
disabled: isPending,
|
|
972
1174
|
onClick: () => setOpen(true)
|
|
973
1175
|
}
|
|
974
1176
|
);
|
|
975
1177
|
}
|
|
976
|
-
return /* @__PURE__ */ jsxs("div", { className: "w-full space-y-2 border border-border bg-secondary/40 p-3", children: [
|
|
1178
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full space-y-2 rounded-md border border-border bg-secondary/40 p-3", children: [
|
|
1179
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Posts to the issue thread without leaving Focus." }),
|
|
977
1180
|
/* @__PURE__ */ jsx(
|
|
978
1181
|
"textarea",
|
|
979
1182
|
{
|
|
1183
|
+
ref: textareaRef,
|
|
980
1184
|
value: body,
|
|
981
1185
|
onChange: (event) => setBody(event.target.value),
|
|
1186
|
+
onKeyDown: (event) => {
|
|
1187
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
1188
|
+
event.preventDefault();
|
|
1189
|
+
void submit();
|
|
1190
|
+
}
|
|
1191
|
+
if (event.key === "Escape") {
|
|
1192
|
+
event.preventDefault();
|
|
1193
|
+
setOpen(false);
|
|
1194
|
+
setBody("");
|
|
1195
|
+
}
|
|
1196
|
+
},
|
|
982
1197
|
rows: 3,
|
|
983
1198
|
placeholder: "Add a short operator note back to the issue\u2026",
|
|
984
1199
|
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]"
|
|
985
1200
|
}
|
|
986
1201
|
),
|
|
987
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1202
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
|
|
988
1203
|
/* @__PURE__ */ jsx(
|
|
989
1204
|
ActionButton,
|
|
990
1205
|
{
|
|
991
1206
|
label: "Cancel",
|
|
992
1207
|
tone: "secondary",
|
|
993
|
-
className: "w-full",
|
|
994
1208
|
disabled: isPending,
|
|
995
1209
|
onClick: () => {
|
|
996
1210
|
setOpen(false);
|
|
@@ -1002,8 +1216,7 @@ function IssueCommentComposer(props) {
|
|
|
1002
1216
|
ActionButton,
|
|
1003
1217
|
{
|
|
1004
1218
|
label: isPending ? "Posting\u2026" : "Post comment",
|
|
1005
|
-
tone: "
|
|
1006
|
-
className: "w-full",
|
|
1219
|
+
tone: "accent",
|
|
1007
1220
|
disabled: isPending || body.trim().length === 0,
|
|
1008
1221
|
onClick: () => void submit()
|
|
1009
1222
|
}
|
|
@@ -1016,22 +1229,23 @@ function NowDetails(props) {
|
|
|
1016
1229
|
const href = itemHref(frame, props.companyPrefix);
|
|
1017
1230
|
const budgetHref = isBudgetOverride(frame) ? costsHref(props.companyPrefix) : null;
|
|
1018
1231
|
const activityLink = activityHref(frame, props.companyPrefix);
|
|
1232
|
+
const hasContext = visibleContextItems(frame).length > 0;
|
|
1019
1233
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-border pt-4", children: [
|
|
1020
|
-
/* @__PURE__ */
|
|
1021
|
-
|
|
1022
|
-
/* @__PURE__ */ jsx("
|
|
1023
|
-
/* @__PURE__ */
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
requestedAmount(frame)
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
] }) : null,
|
|
1031
|
-
budgetReason(frame) ? /* @__PURE__ */ jsx("span", { children: budgetReason(frame) }) : null
|
|
1032
|
-
] })
|
|
1234
|
+
/* @__PURE__ */ jsx(ExplainabilityPanel, { frame, lane: "active", detailOnly: true }),
|
|
1235
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
1236
|
+
/* @__PURE__ */ jsx("span", { children: requestDescriptor(frame) }),
|
|
1237
|
+
/* @__PURE__ */ jsx("span", { children: impactLabel(frame) }),
|
|
1238
|
+
/* @__PURE__ */ jsx("span", { children: formatRelativeTime(frameUpdatedAt(frame, snapshotUpdatedAt)) }),
|
|
1239
|
+
requestedAmount(frame) ? /* @__PURE__ */ jsxs("span", { children: [
|
|
1240
|
+
"Amount requested: ",
|
|
1241
|
+
requestedAmount(frame)
|
|
1242
|
+
] }) : null,
|
|
1243
|
+
budgetReason(frame) ? /* @__PURE__ */ jsx("span", { children: budgetReason(frame) }) : null
|
|
1033
1244
|
] }),
|
|
1034
|
-
/* @__PURE__ */
|
|
1245
|
+
hasContext ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1246
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Context" }),
|
|
1247
|
+
/* @__PURE__ */ jsx(ContextItems, { frame })
|
|
1248
|
+
] }) : null,
|
|
1035
1249
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
1036
1250
|
href ? /* @__PURE__ */ jsxs(
|
|
1037
1251
|
"a",
|
|
@@ -1077,41 +1291,41 @@ function NowDetails(props) {
|
|
|
1077
1291
|
] });
|
|
1078
1292
|
}
|
|
1079
1293
|
function QuietNow() {
|
|
1080
|
-
return /* @__PURE__ */
|
|
1294
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-20 items-center gap-3", children: [
|
|
1295
|
+
/* @__PURE__ */ jsx(QuietMark, { size: "md" }),
|
|
1296
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Nothing active right now." })
|
|
1297
|
+
] });
|
|
1081
1298
|
}
|
|
1082
1299
|
function NowActionRail(props) {
|
|
1083
1300
|
const actionMode = responseKind(props.frame, props.lane);
|
|
1084
1301
|
const isPending = props.pendingId === props.frame.id;
|
|
1085
1302
|
const commentEnabled = actionMode === "acknowledge" && isIssueFrame(props.frame);
|
|
1303
|
+
const [commentOpen, setCommentOpen] = useState(false);
|
|
1304
|
+
const [commentBody, setCommentBody] = useState("");
|
|
1305
|
+
const textareaRef = useRef(null);
|
|
1306
|
+
useEffect(() => {
|
|
1307
|
+
setCommentOpen(false);
|
|
1308
|
+
setCommentBody("");
|
|
1309
|
+
}, [props.frame.id]);
|
|
1310
|
+
useEffect(() => {
|
|
1311
|
+
if (commentOpen) textareaRef.current?.focus();
|
|
1312
|
+
}, [commentOpen]);
|
|
1313
|
+
async function submitComment() {
|
|
1314
|
+
const nextBody = commentBody.trim();
|
|
1315
|
+
if (!nextBody) return;
|
|
1316
|
+
await props.onComment(props.frame, nextBody);
|
|
1317
|
+
setCommentBody("");
|
|
1318
|
+
setCommentOpen(false);
|
|
1319
|
+
}
|
|
1086
1320
|
return /* @__PURE__ */ jsxs(
|
|
1087
|
-
"
|
|
1321
|
+
"div",
|
|
1088
1322
|
{
|
|
1089
|
-
className: "space-y-
|
|
1090
|
-
style: {
|
|
1323
|
+
className: "space-y-3 rounded-md border border-border/60 bg-secondary/20 px-3 py-3",
|
|
1324
|
+
style: props.wide ? { width: 320, flexShrink: 0, position: "sticky", top: 16, alignSelf: "flex-start" } : void 0,
|
|
1091
1325
|
children: [
|
|
1092
|
-
/* @__PURE__ */ jsx("div", { className: "text-
|
|
1093
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1094
|
-
|
|
1095
|
-
IssueCommentComposer,
|
|
1096
|
-
{
|
|
1097
|
-
frame: props.frame,
|
|
1098
|
-
pendingId: props.pendingId,
|
|
1099
|
-
onComment: props.onComment,
|
|
1100
|
-
triggerTone: "primary",
|
|
1101
|
-
triggerLabel: "Comment"
|
|
1102
|
-
}
|
|
1103
|
-
) : null,
|
|
1104
|
-
actionMode === "approval" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1105
|
-
isBudgetOverride(props.frame) ? /* @__PURE__ */ jsx(
|
|
1106
|
-
ActionButton,
|
|
1107
|
-
{
|
|
1108
|
-
label: "Request revision",
|
|
1109
|
-
tone: "secondary",
|
|
1110
|
-
disabled: isPending,
|
|
1111
|
-
className: "w-full",
|
|
1112
|
-
onClick: () => void props.onRequestRevision(props.frame)
|
|
1113
|
-
}
|
|
1114
|
-
) : null,
|
|
1326
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Actions" }),
|
|
1327
|
+
actionMode === "approval" ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1328
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1115
1329
|
/* @__PURE__ */ jsx(
|
|
1116
1330
|
ActionButton,
|
|
1117
1331
|
{
|
|
@@ -1132,17 +1346,37 @@ function NowActionRail(props) {
|
|
|
1132
1346
|
onClick: () => void props.onReject(props.frame)
|
|
1133
1347
|
}
|
|
1134
1348
|
)
|
|
1135
|
-
] })
|
|
1349
|
+
] }),
|
|
1350
|
+
isBudgetOverride(props.frame) ? /* @__PURE__ */ jsx(
|
|
1136
1351
|
ActionButton,
|
|
1137
1352
|
{
|
|
1138
|
-
label:
|
|
1139
|
-
tone:
|
|
1353
|
+
label: "Request revision",
|
|
1354
|
+
tone: "secondary",
|
|
1140
1355
|
disabled: isPending,
|
|
1141
1356
|
className: "w-full",
|
|
1142
|
-
onClick: () => void props.
|
|
1357
|
+
onClick: () => void props.onRequestRevision(props.frame)
|
|
1143
1358
|
}
|
|
1144
1359
|
) : null
|
|
1145
|
-
] }),
|
|
1360
|
+
] }) : actionMode === "acknowledge" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1361
|
+
commentEnabled ? /* @__PURE__ */ jsx(
|
|
1362
|
+
ActionButton,
|
|
1363
|
+
{
|
|
1364
|
+
label: "Comment",
|
|
1365
|
+
tone: "accent",
|
|
1366
|
+
disabled: isPending,
|
|
1367
|
+
onClick: () => setCommentOpen(true)
|
|
1368
|
+
}
|
|
1369
|
+
) : null,
|
|
1370
|
+
/* @__PURE__ */ jsx(
|
|
1371
|
+
ActionButton,
|
|
1372
|
+
{
|
|
1373
|
+
label: isPending ? "Saving\u2026" : "Acknowledge",
|
|
1374
|
+
tone: "accent",
|
|
1375
|
+
disabled: isPending,
|
|
1376
|
+
onClick: () => void props.onAcknowledge(props.frame)
|
|
1377
|
+
}
|
|
1378
|
+
)
|
|
1379
|
+
] }) : null,
|
|
1146
1380
|
props.itemLink ? /* @__PURE__ */ jsxs(
|
|
1147
1381
|
"a",
|
|
1148
1382
|
{
|
|
@@ -1155,7 +1389,59 @@ function NowActionRail(props) {
|
|
|
1155
1389
|
/* @__PURE__ */ jsx(ExternalLinkIcon, {})
|
|
1156
1390
|
]
|
|
1157
1391
|
}
|
|
1158
|
-
) : null
|
|
1392
|
+
) : null,
|
|
1393
|
+
commentOpen ? /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-border bg-background/80 p-3", children: [
|
|
1394
|
+
/* @__PURE__ */ jsxs("div", { className: "text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: [
|
|
1395
|
+
"Commenting on ",
|
|
1396
|
+
compactTitle(props.frame)
|
|
1397
|
+
] }),
|
|
1398
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: "Posts to the issue thread without leaving Focus." }),
|
|
1399
|
+
/* @__PURE__ */ jsx(
|
|
1400
|
+
"textarea",
|
|
1401
|
+
{
|
|
1402
|
+
ref: textareaRef,
|
|
1403
|
+
value: commentBody,
|
|
1404
|
+
onChange: (event) => setCommentBody(event.target.value),
|
|
1405
|
+
onKeyDown: (event) => {
|
|
1406
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
1407
|
+
event.preventDefault();
|
|
1408
|
+
void submitComment();
|
|
1409
|
+
}
|
|
1410
|
+
if (event.key === "Escape") {
|
|
1411
|
+
event.preventDefault();
|
|
1412
|
+
setCommentOpen(false);
|
|
1413
|
+
setCommentBody("");
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
rows: 4,
|
|
1417
|
+
placeholder: "Add a short operator note back to the issue\u2026",
|
|
1418
|
+
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]"
|
|
1419
|
+
}
|
|
1420
|
+
),
|
|
1421
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
|
|
1422
|
+
/* @__PURE__ */ jsx(
|
|
1423
|
+
ActionButton,
|
|
1424
|
+
{
|
|
1425
|
+
label: "Cancel",
|
|
1426
|
+
tone: "secondary",
|
|
1427
|
+
disabled: isPending,
|
|
1428
|
+
onClick: () => {
|
|
1429
|
+
setCommentOpen(false);
|
|
1430
|
+
setCommentBody("");
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
),
|
|
1434
|
+
/* @__PURE__ */ jsx(
|
|
1435
|
+
ActionButton,
|
|
1436
|
+
{
|
|
1437
|
+
label: isPending ? "Posting\u2026" : "Post comment",
|
|
1438
|
+
tone: "accent",
|
|
1439
|
+
disabled: isPending || commentBody.trim().length === 0,
|
|
1440
|
+
onClick: () => void submitComment()
|
|
1441
|
+
}
|
|
1442
|
+
)
|
|
1443
|
+
] })
|
|
1444
|
+
] }) : null
|
|
1159
1445
|
]
|
|
1160
1446
|
}
|
|
1161
1447
|
);
|
|
@@ -1163,6 +1449,7 @@ function NowActionRail(props) {
|
|
|
1163
1449
|
function NowPane(props) {
|
|
1164
1450
|
const frame = props.snapshot.active;
|
|
1165
1451
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
1452
|
+
const wideLayout = useWideLayout(1120);
|
|
1166
1453
|
const activeFrameId = frame?.id ?? null;
|
|
1167
1454
|
const activeUnread = !!frame && (props.snapshot.review?.unread.active ?? 0) > 0;
|
|
1168
1455
|
const itemLink = frame ? itemHref(frame, props.companyPrefix) : null;
|
|
@@ -1172,100 +1459,111 @@ function NowPane(props) {
|
|
|
1172
1459
|
useEffect(() => {
|
|
1173
1460
|
setDetailsOpen(false);
|
|
1174
1461
|
}, [activeFrameId]);
|
|
1175
|
-
return /* @__PURE__ */ jsxs(
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1462
|
+
return /* @__PURE__ */ jsxs(
|
|
1463
|
+
"section",
|
|
1464
|
+
{
|
|
1465
|
+
className: "border border-border bg-card shadow-sm",
|
|
1466
|
+
style: frame ? { borderLeftWidth: 3, borderLeftColor: ACCENT_COLOR } : void 0,
|
|
1467
|
+
children: [
|
|
1468
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-border bg-accent px-4 py-3 sm:px-6", children: [
|
|
1469
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1470
|
+
/* @__PURE__ */ jsx(Accent, { className: "text-sm leading-none", children: props.posture.glyph }),
|
|
1471
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
1472
|
+
/* @__PURE__ */ jsx(Accent, { className: "text-base leading-none font-semibold tracking-[0.04em]", children: props.brand.wordmark }),
|
|
1473
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] leading-none text-muted-foreground", children: props.posture.label })
|
|
1474
|
+
] })
|
|
1183
1475
|
] }),
|
|
1184
|
-
/* @__PURE__ */
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1476
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs tabular-nums text-muted-foreground", children: [
|
|
1477
|
+
/* @__PURE__ */ jsxs("span", { className: props.counts.active > 0 ? "font-semibold" : "", children: [
|
|
1478
|
+
"now ",
|
|
1479
|
+
props.counts.active > 0 ? /* @__PURE__ */ jsx(Accent, { children: props.counts.active }) : props.counts.active
|
|
1480
|
+
] }),
|
|
1481
|
+
/* @__PURE__ */ jsxs("span", { className: props.counts.queued > 0 ? "font-semibold" : "", children: [
|
|
1482
|
+
"next ",
|
|
1483
|
+
props.counts.queued > 0 ? /* @__PURE__ */ jsx(Accent, { children: props.counts.queued }) : props.counts.queued
|
|
1484
|
+
] }),
|
|
1485
|
+
/* @__PURE__ */ jsxs("span", { className: props.counts.ambient > 0 ? "font-semibold" : "", children: [
|
|
1486
|
+
"ambient ",
|
|
1487
|
+
props.counts.ambient > 0 ? /* @__PURE__ */ jsx(Accent, { children: props.counts.ambient }) : props.counts.ambient
|
|
1488
|
+
] })
|
|
1489
|
+
] })
|
|
1195
1490
|
] }),
|
|
1196
|
-
/* @__PURE__ */
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
] })
|
|
1200
|
-
] })
|
|
1201
|
-
] }),
|
|
1202
|
-
/* @__PURE__ */ jsx("div", { style: { height: "0.75rem" }, "aria-hidden": "true" }),
|
|
1203
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 pb-4 sm:px-6", children: !frame ? /* @__PURE__ */ jsx(QuietNow, {}) : /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1204
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-6", children: [
|
|
1205
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-4", style: { flex: "1 1 40rem" }, children: [
|
|
1206
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1207
|
-
/* @__PURE__ */ jsx("span", { children: "Now" }),
|
|
1208
|
-
/* @__PURE__ */ jsx("span", { className: "normal-case tracking-normal", style: { opacity: 0.6 }, children: sourceLabel(frame) })
|
|
1209
|
-
] }),
|
|
1210
|
-
recommendedMove ? /* @__PURE__ */ jsx("div", { className: "max-w-4xl text-[2rem] font-semibold leading-tight text-foreground", children: recommendedMove }) : frame.summary ? /* @__PURE__ */ jsx("div", { className: "max-w-4xl text-[2rem] font-semibold leading-tight text-foreground", children: frame.summary }) : null,
|
|
1211
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[1.75rem] font-medium leading-snug text-foreground/90", children: [
|
|
1212
|
-
/* @__PURE__ */ jsx(UnreadDot, { visible: activeUnread }),
|
|
1213
|
-
/* @__PURE__ */ jsx("span", { children: renderTitle(frame) })
|
|
1214
|
-
] }),
|
|
1215
|
-
helperLine ? /* @__PURE__ */ jsx("p", { className: "max-w-3xl text-[0.95rem] leading-relaxed text-muted-foreground", children: helperLine }) : null,
|
|
1216
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1217
|
-
/* @__PURE__ */ jsx(Badge, { className: "border-border bg-secondary text-foreground/80", children: impactLabel(frame) }),
|
|
1218
|
-
driverLabel(frame) ? /* @__PURE__ */ jsx(Badge, { className: driverBadge.className, style: driverBadge.style, children: driverLabel(frame) }) : null
|
|
1219
|
-
] }),
|
|
1220
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 pt-1", children: /* @__PURE__ */ jsx(Accent, { children: /* @__PURE__ */ jsxs(
|
|
1221
|
-
"button",
|
|
1491
|
+
/* @__PURE__ */ jsx("div", { className: "px-5 py-5 sm:px-6", children: !frame ? /* @__PURE__ */ jsx(QuietNow, {}) : /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
1492
|
+
/* @__PURE__ */ jsxs(
|
|
1493
|
+
"div",
|
|
1222
1494
|
{
|
|
1223
|
-
|
|
1224
|
-
onClick: () => setDetailsOpen(!detailsOpen),
|
|
1225
|
-
"aria-expanded": detailsOpen,
|
|
1226
|
-
className: "flex items-center gap-1.5 text-xs font-medium transition-opacity hover:opacity-100",
|
|
1227
|
-
style: { color: "inherit" },
|
|
1495
|
+
className: wideLayout ? "flex items-start gap-6" : "space-y-4",
|
|
1228
1496
|
children: [
|
|
1497
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", style: wideLayout ? { minWidth: 0, flex: 1 } : void 0, children: [
|
|
1498
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[11px] font-medium uppercase tracking-[0.16em] text-muted-foreground", children: [
|
|
1499
|
+
/* @__PURE__ */ jsx("span", { children: "Now" }),
|
|
1500
|
+
/* @__PURE__ */ jsx("span", { className: "normal-case tracking-normal text-muted-foreground/70", children: sourceLabel(frame) })
|
|
1501
|
+
] }),
|
|
1502
|
+
recommendedMove ? /* @__PURE__ */ jsx("div", { className: "max-w-4xl text-3xl font-semibold leading-tight text-foreground", children: recommendedMove }) : frame.summary ? /* @__PURE__ */ jsx("div", { className: "max-w-4xl text-3xl font-semibold leading-tight text-foreground", children: frame.summary }) : null,
|
|
1503
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xl font-medium leading-snug text-foreground/80", children: [
|
|
1504
|
+
/* @__PURE__ */ jsx(UnreadDot, { visible: activeUnread }),
|
|
1505
|
+
/* @__PURE__ */ jsx("span", { children: renderTitle(frame) })
|
|
1506
|
+
] }),
|
|
1507
|
+
helperLine ? /* @__PURE__ */ jsx("p", { className: "max-w-3xl text-sm leading-relaxed text-muted-foreground", children: helperLine }) : null,
|
|
1508
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1509
|
+
/* @__PURE__ */ jsx(Badge, { className: "border-border bg-secondary text-foreground/80", children: impactLabel(frame) }),
|
|
1510
|
+
driverLabel(frame) ? /* @__PURE__ */ jsx(Badge, { className: driverBadge.className, style: driverBadge.style, children: driverLabel(frame) }) : null
|
|
1511
|
+
] }),
|
|
1512
|
+
/* @__PURE__ */ jsx(InlineExplainability, { frame, lane: "active", preferLaneReason: !!helperLine }),
|
|
1513
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx(Accent, { children: /* @__PURE__ */ jsxs(
|
|
1514
|
+
"button",
|
|
1515
|
+
{
|
|
1516
|
+
type: "button",
|
|
1517
|
+
onClick: () => setDetailsOpen(!detailsOpen),
|
|
1518
|
+
"aria-expanded": detailsOpen,
|
|
1519
|
+
className: "flex items-center gap-1.5 text-xs font-medium transition-opacity hover:opacity-100",
|
|
1520
|
+
style: { color: "inherit" },
|
|
1521
|
+
children: [
|
|
1522
|
+
/* @__PURE__ */ jsx(
|
|
1523
|
+
"svg",
|
|
1524
|
+
{
|
|
1525
|
+
viewBox: "0 0 16 16",
|
|
1526
|
+
className: cn("h-3 w-3 transition-transform", detailsOpen && "rotate-90"),
|
|
1527
|
+
fill: "currentColor",
|
|
1528
|
+
"aria-hidden": "true",
|
|
1529
|
+
children: /* @__PURE__ */ jsx("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" })
|
|
1530
|
+
}
|
|
1531
|
+
),
|
|
1532
|
+
detailsOpen ? "Hide context" : "Show context"
|
|
1533
|
+
]
|
|
1534
|
+
}
|
|
1535
|
+
) }) })
|
|
1536
|
+
] }),
|
|
1229
1537
|
/* @__PURE__ */ jsx(
|
|
1230
|
-
|
|
1538
|
+
NowActionRail,
|
|
1231
1539
|
{
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1540
|
+
frame,
|
|
1541
|
+
lane: "active",
|
|
1542
|
+
wide: wideLayout,
|
|
1543
|
+
pendingId: props.pendingId,
|
|
1544
|
+
itemLink,
|
|
1545
|
+
onApprove: props.onApprove,
|
|
1546
|
+
onReject: props.onReject,
|
|
1547
|
+
onRequestRevision: props.onRequestRevision,
|
|
1548
|
+
onAcknowledge: props.onAcknowledge,
|
|
1549
|
+
onComment: props.onComment
|
|
1237
1550
|
}
|
|
1238
|
-
)
|
|
1239
|
-
detailsOpen ? "Hide details" : "Show details"
|
|
1551
|
+
)
|
|
1240
1552
|
]
|
|
1241
1553
|
}
|
|
1242
|
-
)
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
onComment: props.onComment
|
|
1256
|
-
}
|
|
1257
|
-
)
|
|
1258
|
-
] }),
|
|
1259
|
-
detailsOpen ? /* @__PURE__ */ jsx(
|
|
1260
|
-
NowDetails,
|
|
1261
|
-
{
|
|
1262
|
-
frame,
|
|
1263
|
-
snapshotUpdatedAt: props.snapshot.updatedAt,
|
|
1264
|
-
companyPrefix: props.companyPrefix
|
|
1265
|
-
}
|
|
1266
|
-
) : null
|
|
1267
|
-
] }) })
|
|
1268
|
-
] });
|
|
1554
|
+
),
|
|
1555
|
+
detailsOpen ? /* @__PURE__ */ jsx(
|
|
1556
|
+
NowDetails,
|
|
1557
|
+
{
|
|
1558
|
+
frame,
|
|
1559
|
+
snapshotUpdatedAt: props.snapshot.updatedAt,
|
|
1560
|
+
companyPrefix: props.companyPrefix
|
|
1561
|
+
}
|
|
1562
|
+
) : null
|
|
1563
|
+
] }) })
|
|
1564
|
+
]
|
|
1565
|
+
}
|
|
1566
|
+
);
|
|
1269
1567
|
}
|
|
1270
1568
|
function NextRow(props) {
|
|
1271
1569
|
const { frame } = props;
|
|
@@ -1278,6 +1576,7 @@ function NextRow(props) {
|
|
|
1278
1576
|
const driverBadge = driverBadgeStyle();
|
|
1279
1577
|
const detailText = judgmentLine(frame, "queued");
|
|
1280
1578
|
const showDetailText = detailText.trim().length > 0 && detailText !== secondaryText && detailText !== GENERIC_QUEUED_JUDGMENT;
|
|
1579
|
+
const rankOpacity = Math.max(0.4, 1 - (props.rank - 1) * 0.14);
|
|
1281
1580
|
return /* @__PURE__ */ jsxs("div", { className: "border-b border-border/80 last:border-b-0", style: expanded ? { borderLeftWidth: 2, borderLeftColor: ACCENT_COLOR } : void 0, children: [
|
|
1282
1581
|
/* @__PURE__ */ jsxs(
|
|
1283
1582
|
"button",
|
|
@@ -1287,11 +1586,11 @@ function NextRow(props) {
|
|
|
1287
1586
|
"aria-expanded": expanded,
|
|
1288
1587
|
className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50 sm:px-6",
|
|
1289
1588
|
children: [
|
|
1290
|
-
/* @__PURE__ */ jsx(Accent, { className: "w-5 shrink-0 text-xs font-medium tabular-nums", children: String(props.rank).padStart(2, "0") }),
|
|
1291
1589
|
/* @__PURE__ */ jsx(UnreadDot, { visible: unread }),
|
|
1590
|
+
/* @__PURE__ */ jsx("span", { className: "w-5 shrink-0", style: { opacity: rankOpacity }, children: /* @__PURE__ */ jsx(Accent, { className: "text-xs font-medium tabular-nums", children: String(props.rank).padStart(2, "0") }) }),
|
|
1292
1591
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1293
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
1294
|
-
secondaryText ? /* @__PURE__ */ jsx("div", { className: "
|
|
1592
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium leading-5 text-foreground", children: renderTitle(frame) }),
|
|
1593
|
+
secondaryText ? /* @__PURE__ */ jsx("div", { className: "mt-0.5 max-w-3xl text-xs leading-5 text-muted-foreground", children: secondaryText }) : null
|
|
1295
1594
|
] }),
|
|
1296
1595
|
/* @__PURE__ */ jsx(QueueMovementBadge, { movement: props.movement }),
|
|
1297
1596
|
driver ? /* @__PURE__ */ jsx(Badge, { className: cn("shrink-0", driverBadge.className), style: driverBadge.style, children: driver }) : null,
|
|
@@ -1309,8 +1608,9 @@ function NextRow(props) {
|
|
|
1309
1608
|
]
|
|
1310
1609
|
}
|
|
1311
1610
|
),
|
|
1312
|
-
expanded ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-
|
|
1611
|
+
expanded ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-4 pl-12 sm:px-6 sm:pl-14", children: [
|
|
1313
1612
|
showDetailText ? /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-muted-foreground", children: detailText }) : null,
|
|
1613
|
+
/* @__PURE__ */ jsx(ExplainabilityStrip, { frame, lane: "queued" }),
|
|
1314
1614
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground", children: [
|
|
1315
1615
|
/* @__PURE__ */ jsx("span", { children: impactLabel(frame) }),
|
|
1316
1616
|
driver ? /* @__PURE__ */ jsx(Accent, { children: driver }) : null,
|
|
@@ -1336,19 +1636,21 @@ function NextRow(props) {
|
|
|
1336
1636
|
}
|
|
1337
1637
|
) : null
|
|
1338
1638
|
] }),
|
|
1339
|
-
/* @__PURE__ */
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1639
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
|
|
1640
|
+
/* @__PURE__ */ jsx(IssueCommentComposer, { frame, pendingId: props.pendingId, onComment: props.onComment }),
|
|
1641
|
+
/* @__PURE__ */ jsx(
|
|
1642
|
+
FrameActions,
|
|
1643
|
+
{
|
|
1644
|
+
frame,
|
|
1645
|
+
lane: "queued",
|
|
1646
|
+
pendingId: props.pendingId,
|
|
1647
|
+
onApprove: props.onApprove,
|
|
1648
|
+
onReject: props.onReject,
|
|
1649
|
+
onRequestRevision: props.onRequestRevision,
|
|
1650
|
+
onAcknowledge: props.onAcknowledge
|
|
1651
|
+
}
|
|
1652
|
+
)
|
|
1653
|
+
] })
|
|
1352
1654
|
] }) : null
|
|
1353
1655
|
] });
|
|
1354
1656
|
}
|
|
@@ -1377,65 +1679,23 @@ function NextLane(props) {
|
|
|
1377
1679
|
}
|
|
1378
1680
|
function AmbientRow(props) {
|
|
1379
1681
|
const { frame, snapshotUpdatedAt } = props;
|
|
1380
|
-
const [expanded, setExpanded] = useState(false);
|
|
1381
1682
|
const href = itemHref(frame, props.companyPrefix);
|
|
1382
1683
|
const unread = isFrameUnreadInSnapshot(frame, props.snapshot);
|
|
1383
|
-
const
|
|
1384
|
-
|
|
1385
|
-
/* @__PURE__ */
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
type: "button",
|
|
1389
|
-
onClick: () => setExpanded(!expanded),
|
|
1390
|
-
"aria-expanded": expanded,
|
|
1391
|
-
className: "flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-accent/50 sm:px-6",
|
|
1392
|
-
children: [
|
|
1393
|
-
/* @__PURE__ */ jsx(UnreadDot, { visible: unread }),
|
|
1394
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-muted-foreground/80", children: renderTitle(frame) }),
|
|
1395
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", style: { opacity: 0.6 }, children: sourceLabel(frame) }),
|
|
1396
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", style: { opacity: 0.55 }, children: formatRelativeTime(frameUpdatedAt(frame, snapshotUpdatedAt)) }),
|
|
1397
|
-
/* @__PURE__ */ jsx(
|
|
1398
|
-
"svg",
|
|
1399
|
-
{
|
|
1400
|
-
viewBox: "0 0 16 16",
|
|
1401
|
-
className: cn("h-3 w-3 shrink-0 text-muted-foreground transition-transform", expanded && "rotate-90"),
|
|
1402
|
-
fill: "currentColor",
|
|
1403
|
-
style: { opacity: 0.75 },
|
|
1404
|
-
"aria-hidden": "true",
|
|
1405
|
-
children: /* @__PURE__ */ jsx("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" })
|
|
1406
|
-
}
|
|
1407
|
-
)
|
|
1408
|
-
]
|
|
1409
|
-
}
|
|
1410
|
-
),
|
|
1411
|
-
expanded ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-3 sm:px-6", children: [
|
|
1412
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-muted-foreground", children: frame.summary ?? judgmentLine(frame, "ambient") }),
|
|
1413
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1414
|
-
href ? /* @__PURE__ */ jsx(
|
|
1415
|
-
"a",
|
|
1416
|
-
{
|
|
1417
|
-
href,
|
|
1418
|
-
className: "text-xs font-medium underline underline-offset-2 text-muted-foreground hover:text-foreground",
|
|
1419
|
-
children: primaryLinkLabel(frame)
|
|
1420
|
-
}
|
|
1421
|
-
) : null,
|
|
1422
|
-
activityLink ? /* @__PURE__ */ jsx(
|
|
1423
|
-
"a",
|
|
1424
|
-
{
|
|
1425
|
-
href: activityLink,
|
|
1426
|
-
className: "text-xs font-medium underline underline-offset-2 text-muted-foreground hover:text-foreground",
|
|
1427
|
-
children: "Open activity"
|
|
1428
|
-
}
|
|
1429
|
-
) : null,
|
|
1430
|
-
/* @__PURE__ */ jsx("span", { className: "ml-auto text-xs uppercase tracking-[0.16em] text-muted-foreground/60", children: "Awareness only" })
|
|
1431
|
-
] })
|
|
1432
|
-
] }) : null
|
|
1684
|
+
const content = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-2.5 sm:px-6", children: [
|
|
1685
|
+
/* @__PURE__ */ jsx(UnreadDot, { visible: unread, tone: "muted", reserveSpace: true }),
|
|
1686
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[13px] text-muted-foreground", children: renderTitle(frame) }),
|
|
1687
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground/80", children: sourceLabel(frame) }),
|
|
1688
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground/70", children: formatRelativeTime(frameUpdatedAt(frame, snapshotUpdatedAt)) })
|
|
1433
1689
|
] });
|
|
1690
|
+
return /* @__PURE__ */ jsx("div", { style: { borderBottomWidth: 1, borderBottomColor: "var(--border)" }, className: "last:border-b-0", children: href ? /* @__PURE__ */ jsx("a", { href, className: "block transition-colors hover:bg-accent/40", children: content }) : content });
|
|
1434
1691
|
}
|
|
1435
1692
|
function AmbientLane(props) {
|
|
1436
|
-
return /* @__PURE__ */ jsxs("section", { style: { borderWidth: 1, borderColor: "var(--border)" }, className: "bg-card", "aria-label": "Ambient attention", children: [
|
|
1437
|
-
/* @__PURE__ */ jsx("div", { style: { borderBottomWidth: 1, borderBottomColor: "var(--border)" }, className: "px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsx("h2", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground",
|
|
1438
|
-
props.rows.length === 0 ? /* @__PURE__ */
|
|
1693
|
+
return /* @__PURE__ */ jsxs("section", { style: { borderWidth: 1, borderColor: "var(--border)" }, className: "mt-2 bg-card", "aria-label": "Ambient attention", children: [
|
|
1694
|
+
/* @__PURE__ */ jsx("div", { style: { borderBottomWidth: 1, borderBottomColor: "var(--border)" }, className: "px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsx("h2", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground/80", children: "Ambient" }) }),
|
|
1695
|
+
props.rows.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-4 text-sm text-muted-foreground sm:px-6", children: [
|
|
1696
|
+
/* @__PURE__ */ jsx(QuietMark, {}),
|
|
1697
|
+
/* @__PURE__ */ jsx("span", { children: "Nothing in peripheral view." })
|
|
1698
|
+
] }) : props.rows.map((row) => /* @__PURE__ */ jsx(
|
|
1439
1699
|
AmbientRow,
|
|
1440
1700
|
{
|
|
1441
1701
|
frame: row.frame,
|
|
@@ -1450,6 +1710,7 @@ function AmbientLane(props) {
|
|
|
1450
1710
|
function DashboardWidget(props) {
|
|
1451
1711
|
const companyId = props.context.companyId;
|
|
1452
1712
|
const brand = currentSurfaceBrand();
|
|
1713
|
+
const href = pluginPagePath(props.context.companyPrefix);
|
|
1453
1714
|
const model = useAttentionModel(companyId);
|
|
1454
1715
|
const data = model.snapshot;
|
|
1455
1716
|
const loading = model.loading;
|
|
@@ -1459,20 +1720,19 @@ function DashboardWidget(props) {
|
|
|
1459
1720
|
if (error) return /* @__PURE__ */ jsx(MessageCard, { title: "Plugin error", body: error.message });
|
|
1460
1721
|
if (!data) return /* @__PURE__ */ jsx(MessageCard, { title: brand.wordmark, body: brand.headingEmptyState });
|
|
1461
1722
|
const posture = postureForSnapshot(data);
|
|
1462
|
-
|
|
1723
|
+
const leadingText = data.active ? recommendedMoveValue(data.active) ?? data.active.title : null;
|
|
1724
|
+
const borderStyle = posture.label === "busy" ? { borderLeftWidth: 2, borderLeftColor: ACCENT_COLOR } : posture.label === "elevated" ? { borderLeftWidth: 2, borderLeftColor: ACCENT_BORDER } : void 0;
|
|
1725
|
+
return /* @__PURE__ */ jsxs("a", { href, className: "block border border-border bg-card px-4 py-3 shadow-sm transition-colors hover:bg-accent/40", style: borderStyle, children: [
|
|
1463
1726
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs", children: [
|
|
1464
1727
|
/* @__PURE__ */ jsx(Accent, { className: "pt-0.5 text-sm", children: posture.glyph }),
|
|
1465
|
-
/* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
] }),
|
|
1474
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-[10px] leading-none text-muted-foreground/60", children: brand.supportCopy })
|
|
1475
|
-
] }),
|
|
1728
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
1729
|
+
/* @__PURE__ */ jsx(Accent, { className: "font-semibold tracking-[0.04em]", children: brand.wordmark }),
|
|
1730
|
+
/* @__PURE__ */ jsx(Accent, { className: "text-[11px]", children: posture.label }),
|
|
1731
|
+
unreadCount(data) > 0 ? /* @__PURE__ */ jsxs(Accent, { className: "text-[11px]", children: [
|
|
1732
|
+
"new ",
|
|
1733
|
+
unreadCount(data)
|
|
1734
|
+
] }) : null
|
|
1735
|
+
] }) }),
|
|
1476
1736
|
/* @__PURE__ */ jsxs("span", { className: "ml-auto tabular-nums text-muted-foreground", children: [
|
|
1477
1737
|
"now ",
|
|
1478
1738
|
data.counts.active,
|
|
@@ -1482,7 +1742,19 @@ function DashboardWidget(props) {
|
|
|
1482
1742
|
data.counts.ambient
|
|
1483
1743
|
] })
|
|
1484
1744
|
] }),
|
|
1485
|
-
|
|
1745
|
+
leadingText ? /* @__PURE__ */ jsx(
|
|
1746
|
+
"div",
|
|
1747
|
+
{
|
|
1748
|
+
className: "mt-2 text-sm font-medium leading-5 text-foreground",
|
|
1749
|
+
style: {
|
|
1750
|
+
display: "-webkit-box",
|
|
1751
|
+
WebkitLineClamp: 2,
|
|
1752
|
+
WebkitBoxOrient: "vertical",
|
|
1753
|
+
overflow: "hidden"
|
|
1754
|
+
},
|
|
1755
|
+
children: leadingText
|
|
1756
|
+
}
|
|
1757
|
+
) : /* @__PURE__ */ jsx("div", { className: "mt-2 text-sm text-muted-foreground", children: "No active interruption right now." })
|
|
1486
1758
|
] });
|
|
1487
1759
|
}
|
|
1488
1760
|
function AttentionSidebarLink({ context }) {
|
|
@@ -1526,7 +1798,14 @@ function AttentionSidebarLink({ context }) {
|
|
|
1526
1798
|
}
|
|
1527
1799
|
) }),
|
|
1528
1800
|
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: brand.wordmark }),
|
|
1529
|
-
unread > 0 || actionable > 0 ? /* @__PURE__ */ jsx("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: unread
|
|
1801
|
+
unread > 0 || actionable > 0 ? unread > 0 ? /* @__PURE__ */ jsx("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: unread }) : /* @__PURE__ */ jsx(
|
|
1802
|
+
"span",
|
|
1803
|
+
{
|
|
1804
|
+
className: "inline-flex items-center justify-center rounded-full border px-1.5 py-0.5 text-xs leading-none font-medium",
|
|
1805
|
+
style: { color: ACCENT_COLOR, borderColor: ACCENT_BORDER, backgroundColor: "transparent" },
|
|
1806
|
+
children: actionable
|
|
1807
|
+
}
|
|
1808
|
+
) : null
|
|
1530
1809
|
]
|
|
1531
1810
|
}
|
|
1532
1811
|
);
|
|
@@ -1721,7 +2000,7 @@ function AttentionPage(props) {
|
|
|
1721
2000
|
}
|
|
1722
2001
|
);
|
|
1723
2002
|
}
|
|
1724
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
2003
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1725
2004
|
/* @__PURE__ */ jsx(
|
|
1726
2005
|
NowPane,
|
|
1727
2006
|
{
|
package/dist/worker.js
CHANGED
|
@@ -12556,6 +12556,13 @@ function issueWhyNow(issue, analysis, documentSignal) {
|
|
|
12556
12556
|
|
|
12557
12557
|
// src/aperture/reconciliation.ts
|
|
12558
12558
|
var COMMENT_LOOKUP_CONCURRENCY = 6;
|
|
12559
|
+
function semanticMetadata(confidence, relationHints) {
|
|
12560
|
+
const semantic = {
|
|
12561
|
+
...confidence ? { confidence } : {},
|
|
12562
|
+
...relationHints.length > 0 ? { relationHints } : {}
|
|
12563
|
+
};
|
|
12564
|
+
return Object.keys(semantic).length > 0 ? semantic : void 0;
|
|
12565
|
+
}
|
|
12559
12566
|
function toIsoString2(value) {
|
|
12560
12567
|
if (!value) return void 0;
|
|
12561
12568
|
if (typeof value === "string") return value;
|
|
@@ -12623,6 +12630,7 @@ function issueFrame(issue, comment, documents, directory) {
|
|
|
12623
12630
|
const owner = hasIntent(analysis, "resolution") || documentSignal.resolvesArtifactRequest ? null : analysis.owner;
|
|
12624
12631
|
const move = issueRecommendedMove(issue, analysis, documentSignal);
|
|
12625
12632
|
const target = analysis.blockingTarget;
|
|
12633
|
+
const semantic = semanticMetadata(analysis.semanticConfidence, analysis.relationHints);
|
|
12626
12634
|
return {
|
|
12627
12635
|
lane,
|
|
12628
12636
|
frame: {
|
|
@@ -12661,7 +12669,11 @@ function issueFrame(issue, comment, documents, directory) {
|
|
|
12661
12669
|
issueStatus: issue.status,
|
|
12662
12670
|
issuePriority: issue.priority,
|
|
12663
12671
|
liveReconciled: true,
|
|
12664
|
-
activityPath: comment ? "activity" : void 0
|
|
12672
|
+
activityPath: comment ? "activity" : void 0,
|
|
12673
|
+
attention: {
|
|
12674
|
+
rationale: provenance.factors ?? []
|
|
12675
|
+
},
|
|
12676
|
+
...semantic ? { semantic } : {}
|
|
12665
12677
|
}
|
|
12666
12678
|
}
|
|
12667
12679
|
};
|
|
@@ -12719,7 +12731,13 @@ function agentFrame(agent) {
|
|
|
12719
12731
|
agentStatus: agent.status,
|
|
12720
12732
|
pauseReason: agent.pauseReason,
|
|
12721
12733
|
liveReconciled: true,
|
|
12722
|
-
activityPath: agent.status === "error" ? "activity" : void 0
|
|
12734
|
+
activityPath: agent.status === "error" ? "activity" : void 0,
|
|
12735
|
+
attention: {
|
|
12736
|
+
rationale: provenance.factors ?? []
|
|
12737
|
+
},
|
|
12738
|
+
semantic: {
|
|
12739
|
+
confidence: "high"
|
|
12740
|
+
}
|
|
12723
12741
|
}
|
|
12724
12742
|
}
|
|
12725
12743
|
};
|
package/package.json
CHANGED