@tomismeta/paperclip-aperture 0.3.0 → 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 CHANGED
@@ -80,7 +80,7 @@ and agents attention now? actually sees to th
80
80
 
81
81
  Focus is meant to be more legible than a smart inbox.
82
82
 
83
- The first `0.3.0` explainability slice keeps reasoning attached to the cards you are already acting on:
83
+ The current `0.3.x` explainability slice keeps reasoning attached to the cards you are already acting on:
84
84
 
85
85
  - `Why now` on the active card explains why the current item outranks the rest of the queue
86
86
  - `Why next` on queued rows explains why something is staged behind the current top item
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.3.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
@@ -408,12 +408,13 @@ function approvalRevisionFailureMessage(error) {
408
408
  // src/ui/index.tsx
409
409
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
410
410
  var ACCENT_COLOR = "#007ACC";
411
+ var ACCENT_BG = `${ACCENT_COLOR}14`;
412
+ var ACCENT_BORDER = `${ACCENT_COLOR}33`;
411
413
  var ACCENT_BG_STYLE = { backgroundColor: ACCENT_COLOR };
412
414
  function currentSurfaceBrand() {
413
415
  return {
414
416
  key: "focus",
415
417
  wordmark: "Focus",
416
- supportCopy: "Powered by Aperture",
417
418
  headingEmptyState: "No focus state yet.",
418
419
  loadingLabel: "Loading Focus\u2026"
419
420
  };
@@ -429,6 +430,29 @@ function Accent({ children, className }) {
429
430
  const ref = useAccentColor();
430
431
  return /* @__PURE__ */ jsx("span", { ref, className, children });
431
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
+ }
432
456
  function cn(...parts) {
433
457
  return parts.filter(Boolean).join(" ");
434
458
  }
@@ -501,7 +525,7 @@ function driverLabel(frame) {
501
525
  function driverBadgeStyle() {
502
526
  return {
503
527
  className: "border-transparent",
504
- style: { color: ACCENT_COLOR, backgroundColor: `${ACCENT_COLOR}14`, borderColor: `${ACCENT_COLOR}40` }
528
+ style: { color: ACCENT_COLOR, backgroundColor: ACCENT_BG, borderColor: ACCENT_BORDER }
505
529
  };
506
530
  }
507
531
  function requestDescriptor(frame) {
@@ -542,6 +566,15 @@ function supportingLine(frame, lane) {
542
566
  if (owner && lane === "active" && entityTypeFromFrame(frame) === "issue") {
543
567
  return issueNeedsActionFromLine(owner);
544
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
+ }
545
578
  const judgment = judgmentLine(frame, lane);
546
579
  return judgment.trim().length > 0 ? judgment : null;
547
580
  }
@@ -635,9 +668,11 @@ function isFrameUnreadInSnapshot(frame, snapshot) {
635
668
  if (!seenAt) return true;
636
669
  return frameUpdatedAt(frame, snapshot.updatedAt).localeCompare(seenAt) > 0;
637
670
  }
638
- function UnreadDot({ visible }) {
639
- if (!visible) return null;
640
- return /* @__PURE__ */ jsx("span", { className: "inline-block h-2 w-2 rounded-full shrink-0", style: ACCENT_BG_STYLE, "aria-label": "Unread attention item" });
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 });
641
676
  }
642
677
  function ExternalLinkIcon() {
643
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: [
@@ -702,7 +737,7 @@ function StatusToast({ message }) {
702
737
  "div",
703
738
  {
704
739
  className: cn(
705
- "pointer-events-none fixed bottom-6 right-6 z-50 transition-all duration-200",
740
+ "pointer-events-none fixed bottom-6 left-1/2 z-50 -translate-x-1/2 transition-all duration-200",
706
741
  message ? "translate-y-0 opacity-100" : "translate-y-2 opacity-0"
707
742
  ),
708
743
  "aria-live": "polite",
@@ -919,21 +954,26 @@ function Badge(props) {
919
954
  );
920
955
  }
921
956
  function ActionButton(props) {
922
- const toneClass = props.tone === "primary" ? "bg-green-700 text-white hover:bg-green-600" : 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";
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";
923
959
  return /* @__PURE__ */ jsx(
924
960
  "button",
925
961
  {
926
962
  type: "button",
927
963
  onClick: props.onClick,
964
+ onMouseEnter: () => setHovered(true),
965
+ onMouseLeave: () => setHovered(false),
928
966
  disabled: props.disabled,
929
967
  className: cn(
930
968
  "inline-flex h-8 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium",
931
- "transition-[color,background-color,border-color,box-shadow,opacity]",
969
+ "transition-[color,background-color,border-color,box-shadow,opacity,filter]",
932
970
  "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
933
971
  "disabled:pointer-events-none disabled:opacity-50",
972
+ props.disabled && /…|\.\.\./.test(props.label) && "animate-pulse",
934
973
  toneClass,
935
974
  props.className
936
975
  ),
976
+ style: props.tone === "accent" ? { ...ACCENT_BG_STYLE, filter: hovered && !props.disabled ? "brightness(1.08)" : void 0 } : void 0,
937
977
  children: props.label
938
978
  }
939
979
  );
@@ -946,8 +986,8 @@ function QueueMovementBadge({ movement }) {
946
986
  className: "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium",
947
987
  style: {
948
988
  color: ACCENT_COLOR,
949
- backgroundColor: `${ACCENT_COLOR}12`,
950
- borderColor: `${ACCENT_COLOR}33`
989
+ backgroundColor: ACCENT_BG,
990
+ borderColor: ACCENT_BORDER
951
991
  },
952
992
  children: [
953
993
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: movement === "up" ? "\u2191" : "\u2193" }),
@@ -992,21 +1032,24 @@ function FrameActions(props) {
992
1032
  ActionButton,
993
1033
  {
994
1034
  label: isPending ? "Saving\u2026" : "Acknowledge",
995
- tone: "primary",
1035
+ tone: "accent",
996
1036
  disabled: isPending,
997
1037
  onClick: () => void props.onAcknowledge(props.frame)
998
1038
  }
999
1039
  ) });
1000
1040
  }
1041
+ function visibleContextItems(frame) {
1042
+ return (frame.context?.items ?? []).filter((item2) => item2.id !== ATTENTION_CONTEXT_IDS.recommendedMove);
1043
+ }
1001
1044
  function ContextItems({ frame }) {
1002
- const items = frame.context?.items ?? [];
1045
+ const items = visibleContextItems(frame);
1003
1046
  if (items.length === 0) return null;
1004
1047
  return /* @__PURE__ */ jsx("div", { className: "grid gap-2 md:grid-cols-2", children: items.map((item2) => /* @__PURE__ */ jsxs(
1005
1048
  "div",
1006
1049
  {
1007
1050
  className: cn(
1008
1051
  "border border-border bg-secondary/50 px-3 py-2",
1009
- (item2.id === ATTENTION_CONTEXT_IDS.latestComment || item2.id === ATTENTION_CONTEXT_IDS.recommendedMove) && "md:col-span-2"
1052
+ item2.id === ATTENTION_CONTEXT_IDS.latestComment && "md:col-span-2"
1010
1053
  ),
1011
1054
  children: [
1012
1055
  /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: item2.label }),
@@ -1016,60 +1059,90 @@ function ContextItems({ frame }) {
1016
1059
  item2.id
1017
1060
  )) });
1018
1061
  }
1019
- function DetailCard(props) {
1020
- return /* @__PURE__ */ jsxs("div", { className: cn("border border-border bg-secondary/50 px-3 py-2", props.className), children: [
1021
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: props.label }),
1022
- /* @__PURE__ */ jsx("div", { className: "mt-1", children: props.children })
1023
- ] });
1024
- }
1025
1062
  function ExplainabilityBadges(props) {
1026
1063
  if (props.values.length === 0) return null;
1027
1064
  return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: props.values.map((value) => /* @__PURE__ */ jsx(
1028
1065
  Badge,
1029
1066
  {
1030
1067
  className: props.accent ? "border-transparent" : "border-border bg-background/70 text-foreground/80",
1031
- style: props.accent ? { color: ACCENT_COLOR, backgroundColor: `${ACCENT_COLOR}14`, borderColor: `${ACCENT_COLOR}33` } : void 0,
1068
+ style: props.accent ? { color: ACCENT_COLOR, backgroundColor: ACCENT_BG, borderColor: ACCENT_BORDER } : void 0,
1032
1069
  children: value
1033
1070
  },
1034
1071
  value
1035
1072
  )) });
1036
1073
  }
1037
- function ExplainabilityPanel(props) {
1038
- const explanation = explainFrame(props.frame, props.lane);
1039
- const strength = explanation.signalStrength ? signalStrengthLabel(explanation.signalStrength) : null;
1040
- return /* @__PURE__ */ jsxs("div", { className: "grid gap-3 lg:grid-cols-2", children: [
1041
- /* @__PURE__ */ jsxs(DetailCard, { label: "Why now", className: "lg:col-span-2", children: [
1042
- /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-foreground/90", children: explanation.whyNow ?? judgmentLine(props.frame, props.lane) }),
1043
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: explanation.laneReason })
1044
- ] }),
1045
- strength ? /* @__PURE__ */ jsx(DetailCard, { label: "Confidence", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-foreground/90", children: strength }) }) : null,
1046
- explanation.continuity ? /* @__PURE__ */ jsx(DetailCard, { label: "Related activity", children: /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-foreground/90", children: explanation.continuity }) }) : null,
1047
- explanation.signals.length > 0 ? /* @__PURE__ */ jsx(DetailCard, { label: "Signals", className: explanation.relationLabels.length > 0 ? "lg:col-span-1" : "lg:col-span-2", children: /* @__PURE__ */ jsx(ExplainabilityBadges, { values: explanation.signals }) }) : null,
1048
- explanation.relationLabels.length > 0 ? /* @__PURE__ */ jsx(DetailCard, { label: "Thread context", className: explanation.signals.length > 0 ? "lg:col-span-1" : "lg:col-span-2", children: /* @__PURE__ */ jsx(ExplainabilityBadges, { values: explanation.relationLabels, accent: true }) }) : null
1049
- ] });
1050
- }
1051
- function ExplainabilityStrip(props) {
1074
+ function InlineExplainability(props) {
1052
1075
  const explanation = explainFrame(props.frame, props.lane);
1053
1076
  const chips = [
1054
1077
  ...explanation.signalStrength ? [signalStrengthLabel(explanation.signalStrength)] : [],
1055
1078
  ...explanation.signals.slice(0, 2),
1056
1079
  ...explanation.relationLabels.slice(0, 1)
1057
1080
  ];
1058
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-border/70 bg-secondary/30 px-3 py-2", children: [
1059
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: props.lane === "queued" ? "Why next" : props.lane === "ambient" ? "Why ambient" : "Why now" }),
1060
- /* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: explanation.whyNow ?? judgmentLine(props.frame, props.lane) }),
1061
- /* @__PURE__ */ jsx("div", { className: "text-[11px] leading-relaxed text-muted-foreground", children: explanation.laneReason }),
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,
1062
1089
  chips.length > 0 ? /* @__PURE__ */ jsx(ExplainabilityBadges, { values: chips, accent: true }) : null
1063
1090
  ] });
1064
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
+ }
1065
1129
  function isIssueFrame(frame) {
1066
1130
  return entityTypeFromFrame(frame) === "issue";
1067
1131
  }
1068
1132
  function IssueCommentComposer(props) {
1069
1133
  const [open, setOpen] = useState(false);
1070
1134
  const [body, setBody] = useState("");
1071
- if (!isIssueFrame(props.frame)) return null;
1135
+ const textareaRef = useRef(null);
1136
+ const issueFrame = isIssueFrame(props.frame);
1072
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;
1073
1146
  async function submit() {
1074
1147
  const nextBody = body.trim();
1075
1148
  if (!nextBody) return;
@@ -1096,30 +1169,42 @@ function IssueCommentComposer(props) {
1096
1169
  {
1097
1170
  label,
1098
1171
  tone: triggerTone,
1099
- className: "w-full",
1172
+ className: props.triggerFullWidth ? "w-full" : void 0,
1100
1173
  disabled: isPending,
1101
1174
  onClick: () => setOpen(true)
1102
1175
  }
1103
1176
  );
1104
1177
  }
1105
- 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." }),
1106
1180
  /* @__PURE__ */ jsx(
1107
1181
  "textarea",
1108
1182
  {
1183
+ ref: textareaRef,
1109
1184
  value: body,
1110
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
+ },
1111
1197
  rows: 3,
1112
1198
  placeholder: "Add a short operator note back to the issue\u2026",
1113
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]"
1114
1200
  }
1115
1201
  ),
1116
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
1202
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
1117
1203
  /* @__PURE__ */ jsx(
1118
1204
  ActionButton,
1119
1205
  {
1120
1206
  label: "Cancel",
1121
1207
  tone: "secondary",
1122
- className: "w-full",
1123
1208
  disabled: isPending,
1124
1209
  onClick: () => {
1125
1210
  setOpen(false);
@@ -1131,8 +1216,7 @@ function IssueCommentComposer(props) {
1131
1216
  ActionButton,
1132
1217
  {
1133
1218
  label: isPending ? "Posting\u2026" : "Post comment",
1134
- tone: "primary",
1135
- className: "w-full",
1219
+ tone: "accent",
1136
1220
  disabled: isPending || body.trim().length === 0,
1137
1221
  onClick: () => void submit()
1138
1222
  }
@@ -1145,8 +1229,9 @@ function NowDetails(props) {
1145
1229
  const href = itemHref(frame, props.companyPrefix);
1146
1230
  const budgetHref = isBudgetOverride(frame) ? costsHref(props.companyPrefix) : null;
1147
1231
  const activityLink = activityHref(frame, props.companyPrefix);
1232
+ const hasContext = visibleContextItems(frame).length > 0;
1148
1233
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-border pt-4", children: [
1149
- /* @__PURE__ */ jsx(ExplainabilityPanel, { frame, lane: "active" }),
1234
+ /* @__PURE__ */ jsx(ExplainabilityPanel, { frame, lane: "active", detailOnly: true }),
1150
1235
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
1151
1236
  /* @__PURE__ */ jsx("span", { children: requestDescriptor(frame) }),
1152
1237
  /* @__PURE__ */ jsx("span", { children: impactLabel(frame) }),
@@ -1157,7 +1242,10 @@ function NowDetails(props) {
1157
1242
  ] }) : null,
1158
1243
  budgetReason(frame) ? /* @__PURE__ */ jsx("span", { children: budgetReason(frame) }) : null
1159
1244
  ] }),
1160
- /* @__PURE__ */ jsx(ContextItems, { frame }),
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,
1161
1249
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
1162
1250
  href ? /* @__PURE__ */ jsxs(
1163
1251
  "a",
@@ -1203,41 +1291,41 @@ function NowDetails(props) {
1203
1291
  ] });
1204
1292
  }
1205
1293
  function QuietNow() {
1206
- return /* @__PURE__ */ jsx("div", { className: "flex min-h-16 items-center", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Nothing active right now." }) });
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
+ ] });
1207
1298
  }
1208
1299
  function NowActionRail(props) {
1209
1300
  const actionMode = responseKind(props.frame, props.lane);
1210
1301
  const isPending = props.pendingId === props.frame.id;
1211
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
+ }
1212
1320
  return /* @__PURE__ */ jsxs(
1213
- "aside",
1321
+ "div",
1214
1322
  {
1215
- className: "space-y-4 border border-border bg-secondary/20 p-4",
1216
- style: { flex: "0 1 20rem", minWidth: "18rem" },
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,
1217
1325
  children: [
1218
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground", children: "Actions" }),
1219
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1220
- commentEnabled ? /* @__PURE__ */ jsx(
1221
- IssueCommentComposer,
1222
- {
1223
- frame: props.frame,
1224
- pendingId: props.pendingId,
1225
- onComment: props.onComment,
1226
- triggerTone: "primary",
1227
- triggerLabel: "Comment"
1228
- }
1229
- ) : null,
1230
- actionMode === "approval" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1231
- isBudgetOverride(props.frame) ? /* @__PURE__ */ jsx(
1232
- ActionButton,
1233
- {
1234
- label: "Request revision",
1235
- tone: "secondary",
1236
- disabled: isPending,
1237
- className: "w-full",
1238
- onClick: () => void props.onRequestRevision(props.frame)
1239
- }
1240
- ) : 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: [
1241
1329
  /* @__PURE__ */ jsx(
1242
1330
  ActionButton,
1243
1331
  {
@@ -1258,17 +1346,37 @@ function NowActionRail(props) {
1258
1346
  onClick: () => void props.onReject(props.frame)
1259
1347
  }
1260
1348
  )
1261
- ] }) : actionMode === "acknowledge" ? /* @__PURE__ */ jsx(
1349
+ ] }),
1350
+ isBudgetOverride(props.frame) ? /* @__PURE__ */ jsx(
1262
1351
  ActionButton,
1263
1352
  {
1264
- label: isPending ? "Saving\u2026" : "Acknowledge",
1265
- tone: commentEnabled ? "secondary" : "primary",
1353
+ label: "Request revision",
1354
+ tone: "secondary",
1266
1355
  disabled: isPending,
1267
1356
  className: "w-full",
1268
- onClick: () => void props.onAcknowledge(props.frame)
1357
+ onClick: () => void props.onRequestRevision(props.frame)
1269
1358
  }
1270
1359
  ) : null
1271
- ] }),
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,
1272
1380
  props.itemLink ? /* @__PURE__ */ jsxs(
1273
1381
  "a",
1274
1382
  {
@@ -1281,7 +1389,59 @@ function NowActionRail(props) {
1281
1389
  /* @__PURE__ */ jsx(ExternalLinkIcon, {})
1282
1390
  ]
1283
1391
  }
1284
- ) : 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
1285
1445
  ]
1286
1446
  }
1287
1447
  );
@@ -1289,6 +1449,7 @@ function NowActionRail(props) {
1289
1449
  function NowPane(props) {
1290
1450
  const frame = props.snapshot.active;
1291
1451
  const [detailsOpen, setDetailsOpen] = useState(false);
1452
+ const wideLayout = useWideLayout(1120);
1292
1453
  const activeFrameId = frame?.id ?? null;
1293
1454
  const activeUnread = !!frame && (props.snapshot.review?.unread.active ?? 0) > 0;
1294
1455
  const itemLink = frame ? itemHref(frame, props.companyPrefix) : null;
@@ -1298,100 +1459,111 @@ function NowPane(props) {
1298
1459
  useEffect(() => {
1299
1460
  setDetailsOpen(false);
1300
1461
  }, [activeFrameId]);
1301
- return /* @__PURE__ */ jsxs("section", { className: "border border-border bg-card shadow-sm", children: [
1302
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-border bg-accent px-4 py-2.5 sm:px-6", children: [
1303
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1304
- /* @__PURE__ */ jsx(Accent, { className: "text-base leading-none", children: props.posture.glyph }),
1305
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
1306
- /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
1307
- /* @__PURE__ */ jsx(Accent, { className: "text-[18px] leading-none font-bold tracking-[0.04em]", children: props.brand.wordmark }),
1308
- /* @__PURE__ */ jsx(Accent, { className: "text-xs leading-none", children: props.posture.label })
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
+ ] })
1309
1475
  ] }),
1310
- /* @__PURE__ */ jsx("div", { className: "text-[10px] leading-none text-muted-foreground/60", children: props.brand.supportCopy })
1311
- ] })
1312
- ] }),
1313
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs tabular-nums text-muted-foreground", children: [
1314
- /* @__PURE__ */ jsxs("span", { className: props.counts.active > 0 ? "font-semibold" : "", children: [
1315
- "now ",
1316
- props.counts.active > 0 ? /* @__PURE__ */ jsx(Accent, { children: props.counts.active }) : props.counts.active
1317
- ] }),
1318
- /* @__PURE__ */ jsxs("span", { className: props.counts.queued > 0 ? "font-semibold" : "", children: [
1319
- "next ",
1320
- props.counts.queued > 0 ? /* @__PURE__ */ jsx(Accent, { children: props.counts.queued }) : props.counts.queued
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
+ ] })
1321
1490
  ] }),
1322
- /* @__PURE__ */ jsxs("span", { className: props.counts.ambient > 0 ? "font-semibold" : "", children: [
1323
- "ambient ",
1324
- props.counts.ambient > 0 ? /* @__PURE__ */ jsx(Accent, { children: props.counts.ambient }) : props.counts.ambient
1325
- ] })
1326
- ] })
1327
- ] }),
1328
- /* @__PURE__ */ jsx("div", { style: { height: "0.75rem" }, "aria-hidden": "true" }),
1329
- /* @__PURE__ */ jsx("div", { className: "px-4 pb-4 sm:px-6", children: !frame ? /* @__PURE__ */ jsx(QuietNow, {}) : /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1330
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-6", children: [
1331
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-4", style: { flex: "1 1 40rem" }, children: [
1332
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-muted-foreground", children: [
1333
- /* @__PURE__ */ jsx("span", { children: "Now" }),
1334
- /* @__PURE__ */ jsx("span", { className: "normal-case tracking-normal", style: { opacity: 0.6 }, children: sourceLabel(frame) })
1335
- ] }),
1336
- 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,
1337
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[1.75rem] font-medium leading-snug text-foreground/90", children: [
1338
- /* @__PURE__ */ jsx(UnreadDot, { visible: activeUnread }),
1339
- /* @__PURE__ */ jsx("span", { children: renderTitle(frame) })
1340
- ] }),
1341
- helperLine ? /* @__PURE__ */ jsx("p", { className: "max-w-3xl text-[0.95rem] leading-relaxed text-muted-foreground", children: helperLine }) : null,
1342
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
1343
- /* @__PURE__ */ jsx(Badge, { className: "border-border bg-secondary text-foreground/80", children: impactLabel(frame) }),
1344
- driverLabel(frame) ? /* @__PURE__ */ jsx(Badge, { className: driverBadge.className, style: driverBadge.style, children: driverLabel(frame) }) : null
1345
- ] }),
1346
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 pt-1", children: /* @__PURE__ */ jsx(Accent, { children: /* @__PURE__ */ jsxs(
1347
- "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",
1348
1494
  {
1349
- type: "button",
1350
- onClick: () => setDetailsOpen(!detailsOpen),
1351
- "aria-expanded": detailsOpen,
1352
- className: "flex items-center gap-1.5 text-xs font-medium transition-opacity hover:opacity-100",
1353
- style: { color: "inherit" },
1495
+ className: wideLayout ? "flex items-start gap-6" : "space-y-4",
1354
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
+ ] }),
1355
1537
  /* @__PURE__ */ jsx(
1356
- "svg",
1538
+ NowActionRail,
1357
1539
  {
1358
- viewBox: "0 0 16 16",
1359
- className: cn("h-3 w-3 transition-transform", detailsOpen && "rotate-90"),
1360
- fill: "currentColor",
1361
- "aria-hidden": "true",
1362
- 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" })
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
1363
1550
  }
1364
- ),
1365
- detailsOpen ? "Hide details" : "Show details"
1551
+ )
1366
1552
  ]
1367
1553
  }
1368
- ) }) })
1369
- ] }),
1370
- /* @__PURE__ */ jsx(
1371
- NowActionRail,
1372
- {
1373
- frame,
1374
- lane: "active",
1375
- pendingId: props.pendingId,
1376
- itemLink,
1377
- onApprove: props.onApprove,
1378
- onReject: props.onReject,
1379
- onRequestRevision: props.onRequestRevision,
1380
- onAcknowledge: props.onAcknowledge,
1381
- onComment: props.onComment
1382
- }
1383
- )
1384
- ] }),
1385
- detailsOpen ? /* @__PURE__ */ jsx(
1386
- NowDetails,
1387
- {
1388
- frame,
1389
- snapshotUpdatedAt: props.snapshot.updatedAt,
1390
- companyPrefix: props.companyPrefix
1391
- }
1392
- ) : null
1393
- ] }) })
1394
- ] });
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
+ );
1395
1567
  }
1396
1568
  function NextRow(props) {
1397
1569
  const { frame } = props;
@@ -1404,6 +1576,7 @@ function NextRow(props) {
1404
1576
  const driverBadge = driverBadgeStyle();
1405
1577
  const detailText = judgmentLine(frame, "queued");
1406
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);
1407
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: [
1408
1581
  /* @__PURE__ */ jsxs(
1409
1582
  "button",
@@ -1413,11 +1586,11 @@ function NextRow(props) {
1413
1586
  "aria-expanded": expanded,
1414
1587
  className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50 sm:px-6",
1415
1588
  children: [
1416
- /* @__PURE__ */ jsx(Accent, { className: "w-5 shrink-0 text-xs font-medium tabular-nums", children: String(props.rank).padStart(2, "0") }),
1417
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") }) }),
1418
1591
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1419
- /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: renderTitle(frame) }),
1420
- secondaryText ? /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-muted-foreground", children: secondaryText }) : null
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
1421
1594
  ] }),
1422
1595
  /* @__PURE__ */ jsx(QueueMovementBadge, { movement: props.movement }),
1423
1596
  driver ? /* @__PURE__ */ jsx(Badge, { className: cn("shrink-0", driverBadge.className), style: driverBadge.style, children: driver }) : null,
@@ -1435,7 +1608,7 @@ function NextRow(props) {
1435
1608
  ]
1436
1609
  }
1437
1610
  ),
1438
- expanded ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-3 pl-12 sm:px-6 sm:pl-14", children: [
1611
+ expanded ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-4 pl-12 sm:px-6 sm:pl-14", children: [
1439
1612
  showDetailText ? /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-muted-foreground", children: detailText }) : null,
1440
1613
  /* @__PURE__ */ jsx(ExplainabilityStrip, { frame, lane: "queued" }),
1441
1614
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground", children: [
@@ -1463,19 +1636,21 @@ function NextRow(props) {
1463
1636
  }
1464
1637
  ) : null
1465
1638
  ] }),
1466
- /* @__PURE__ */ jsx(IssueCommentComposer, { frame, pendingId: props.pendingId, onComment: props.onComment }),
1467
- /* @__PURE__ */ jsx(
1468
- FrameActions,
1469
- {
1470
- frame,
1471
- lane: "queued",
1472
- pendingId: props.pendingId,
1473
- onApprove: props.onApprove,
1474
- onReject: props.onReject,
1475
- onRequestRevision: props.onRequestRevision,
1476
- onAcknowledge: props.onAcknowledge
1477
- }
1478
- )
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
+ ] })
1479
1654
  ] }) : null
1480
1655
  ] });
1481
1656
  }
@@ -1504,65 +1679,23 @@ function NextLane(props) {
1504
1679
  }
1505
1680
  function AmbientRow(props) {
1506
1681
  const { frame, snapshotUpdatedAt } = props;
1507
- const [expanded, setExpanded] = useState(false);
1508
1682
  const href = itemHref(frame, props.companyPrefix);
1509
1683
  const unread = isFrameUnreadInSnapshot(frame, props.snapshot);
1510
- const activityLink = activityHref(frame, props.companyPrefix);
1511
- return /* @__PURE__ */ jsxs("div", { style: { borderBottomWidth: 1, borderBottomColor: "var(--border)" }, className: "last:border-b-0", children: [
1512
- /* @__PURE__ */ jsxs(
1513
- "button",
1514
- {
1515
- type: "button",
1516
- onClick: () => setExpanded(!expanded),
1517
- "aria-expanded": expanded,
1518
- className: "flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-accent/50 sm:px-6",
1519
- children: [
1520
- /* @__PURE__ */ jsx(UnreadDot, { visible: unread }),
1521
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-muted-foreground/80", children: renderTitle(frame) }),
1522
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", style: { opacity: 0.6 }, children: sourceLabel(frame) }),
1523
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", style: { opacity: 0.55 }, children: formatRelativeTime(frameUpdatedAt(frame, snapshotUpdatedAt)) }),
1524
- /* @__PURE__ */ jsx(
1525
- "svg",
1526
- {
1527
- viewBox: "0 0 16 16",
1528
- className: cn("h-3 w-3 shrink-0 text-muted-foreground transition-transform", expanded && "rotate-90"),
1529
- fill: "currentColor",
1530
- style: { opacity: 0.75 },
1531
- "aria-hidden": "true",
1532
- 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" })
1533
- }
1534
- )
1535
- ]
1536
- }
1537
- ),
1538
- expanded ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-4 pb-3 sm:px-6", children: [
1539
- /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-muted-foreground", children: frame.summary ?? judgmentLine(frame, "ambient") }),
1540
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1541
- href ? /* @__PURE__ */ jsx(
1542
- "a",
1543
- {
1544
- href,
1545
- className: "text-xs font-medium underline underline-offset-2 text-muted-foreground hover:text-foreground",
1546
- children: primaryLinkLabel(frame)
1547
- }
1548
- ) : null,
1549
- activityLink ? /* @__PURE__ */ jsx(
1550
- "a",
1551
- {
1552
- href: activityLink,
1553
- className: "text-xs font-medium underline underline-offset-2 text-muted-foreground hover:text-foreground",
1554
- children: "Open activity"
1555
- }
1556
- ) : null,
1557
- /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs uppercase tracking-[0.16em] text-muted-foreground/60", children: "Awareness only" })
1558
- ] })
1559
- ] }) : 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)) })
1560
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 });
1561
1691
  }
1562
1692
  function AmbientLane(props) {
1563
- return /* @__PURE__ */ jsxs("section", { style: { borderWidth: 1, borderColor: "var(--border)" }, className: "bg-card", "aria-label": "Ambient attention", children: [
1564
- /* @__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", style: { opacity: 0.6 }, children: "Ambient" }) }),
1565
- props.rows.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-4 text-sm text-muted-foreground sm:px-6", style: { opacity: 0.4 }, children: "Nothing in peripheral view." }) : props.rows.map((row) => /* @__PURE__ */ jsx(
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(
1566
1699
  AmbientRow,
1567
1700
  {
1568
1701
  frame: row.frame,
@@ -1577,6 +1710,7 @@ function AmbientLane(props) {
1577
1710
  function DashboardWidget(props) {
1578
1711
  const companyId = props.context.companyId;
1579
1712
  const brand = currentSurfaceBrand();
1713
+ const href = pluginPagePath(props.context.companyPrefix);
1580
1714
  const model = useAttentionModel(companyId);
1581
1715
  const data = model.snapshot;
1582
1716
  const loading = model.loading;
@@ -1586,20 +1720,19 @@ function DashboardWidget(props) {
1586
1720
  if (error) return /* @__PURE__ */ jsx(MessageCard, { title: "Plugin error", body: error.message });
1587
1721
  if (!data) return /* @__PURE__ */ jsx(MessageCard, { title: brand.wordmark, body: brand.headingEmptyState });
1588
1722
  const posture = postureForSnapshot(data);
1589
- return /* @__PURE__ */ jsxs("div", { className: "border border-border bg-card px-4 py-2.5 shadow-sm", children: [
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: [
1590
1726
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs", children: [
1591
1727
  /* @__PURE__ */ jsx(Accent, { className: "pt-0.5 text-sm", children: posture.glyph }),
1592
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1593
- /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
1594
- /* @__PURE__ */ jsx(Accent, { className: "font-semibold tracking-[0.04em]", children: brand.wordmark }),
1595
- /* @__PURE__ */ jsx(Accent, { className: "text-[11px]", children: posture.label }),
1596
- unreadCount(data) > 0 ? /* @__PURE__ */ jsxs(Accent, { className: "text-[11px]", children: [
1597
- "new ",
1598
- unreadCount(data)
1599
- ] }) : null
1600
- ] }),
1601
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-[10px] leading-none text-muted-foreground/60", children: brand.supportCopy })
1602
- ] }),
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
+ ] }) }),
1603
1736
  /* @__PURE__ */ jsxs("span", { className: "ml-auto tabular-nums text-muted-foreground", children: [
1604
1737
  "now ",
1605
1738
  data.counts.active,
@@ -1609,7 +1742,19 @@ function DashboardWidget(props) {
1609
1742
  data.counts.ambient
1610
1743
  ] })
1611
1744
  ] }),
1612
- data.active ? /* @__PURE__ */ jsx("div", { className: "mt-2 truncate text-sm font-medium text-foreground", children: data.active.title }) : /* @__PURE__ */ jsx("div", { className: "mt-2 text-sm text-muted-foreground", children: "No active interruption right now." })
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." })
1613
1758
  ] });
1614
1759
  }
1615
1760
  function AttentionSidebarLink({ context }) {
@@ -1653,7 +1798,14 @@ function AttentionSidebarLink({ context }) {
1653
1798
  }
1654
1799
  ) }),
1655
1800
  /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: brand.wordmark }),
1656
- 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 > 0 ? unread : actionable }) : null
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
1657
1809
  ]
1658
1810
  }
1659
1811
  );
@@ -1848,7 +2000,7 @@ function AttentionPage(props) {
1848
2000
  }
1849
2001
  );
1850
2002
  }
1851
- return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
2003
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1852
2004
  /* @__PURE__ */ jsx(
1853
2005
  NowPane,
1854
2006
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomismeta/paperclip-aperture",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "The live attention layer for Paperclip, powered by Aperture's deterministic attention engine.",