@opengeni/react 6.1.0 → 7.0.0-canary.35625901699001
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 +83 -2
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +1 -1
- package/dist/artifacts-presentation.js +1 -1
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +3 -3
- package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-LGH4IP47.js} +3 -3
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/{chunk-NOSYBS2D.js → chunk-2VHARTBL.js} +2 -1
- package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
- package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
- package/dist/chunk-5UONOQ3Y.js +635 -0
- package/dist/chunk-5UONOQ3Y.js.map +1 -0
- package/dist/{chunk-NYESIADM.js → chunk-AKOMC3VO.js} +3 -3
- package/dist/chunk-AKOMC3VO.js.map +1 -0
- package/dist/chunk-BRM6FVZA.js +302 -0
- package/dist/chunk-BRM6FVZA.js.map +1 -0
- package/dist/{chunk-KANFU34B.js → chunk-DBPVUGQT.js} +11 -2
- package/dist/chunk-DBPVUGQT.js.map +1 -0
- package/dist/chunk-FHIGTPME.js +1589 -0
- package/dist/chunk-FHIGTPME.js.map +1 -0
- package/dist/{chunk-5M6VTFJ5.js → chunk-GBPCZH3Y.js} +2 -2
- package/dist/{chunk-YBM6CQRU.js → chunk-GEN54DU5.js} +4 -4
- package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
- package/dist/chunk-HJ5VJ4JB.js.map +1 -0
- package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
- package/dist/{chunk-NLTS7JHI.js → chunk-JPHDWKZE.js} +5 -5
- package/dist/{chunk-TDSEILUH.js → chunk-MI6ALLLD.js} +1297 -1535
- package/dist/chunk-MI6ALLLD.js.map +1 -0
- package/dist/{chunk-ICA6R6TW.js → chunk-OWHPKAOV.js} +28 -20
- package/dist/chunk-OWHPKAOV.js.map +1 -0
- package/dist/{chunk-24M4YBCL.js → chunk-PNAVEK2U.js} +4 -4
- package/dist/{chunk-L2NBTKRX.js → chunk-TXCBE32L.js} +203 -19
- package/dist/chunk-TXCBE32L.js.map +1 -0
- package/dist/{chunk-45XEUVTK.js → chunk-UXE5SSNN.js} +293 -463
- package/dist/chunk-UXE5SSNN.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-VR4Z73EU.js} +203 -9
- package/dist/chunk-VR4Z73EU.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-XMXRI5JK.js} +65 -18
- package/dist/chunk-XMXRI5JK.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/chat-composer.d.ts +13 -3
- package/dist/components/markdown.d.ts +6 -3
- package/dist/components/message-timeline.d.ts +7 -2
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/session-capability-frame.d.ts +24 -0
- package/dist/components/session-chrome.d.ts +1 -2
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-conversation.d.ts +3 -0
- package/dist/components/session-mcp-capability-card.d.ts +22 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/composer.js +7 -7
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +5 -0
- package/dist/connect-chooser.d.ts +6 -0
- package/dist/connect-panel.d.ts +9 -2
- package/dist/connect-setup.d.ts +5 -1
- package/dist/connect.d.ts +5 -21
- package/dist/connect.js +1268 -584
- package/dist/connect.js.map +1 -1
- package/dist/connection-catalog.d.ts +6 -1
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +1 -0
- package/dist/connection-logo.d.ts +5 -2
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/hooks/use-codex-accounts.d.ts +11 -5
- package/dist/hooks/use-connect.d.ts +22 -0
- package/dist/hooks/use-session-events.d.ts +5 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-workspace-sessions.d.ts +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +771 -793
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
- package/dist/model-policy.d.ts +6 -2
- package/dist/model-policy.js +1 -1
- package/dist/{platform-activity-row-DUE2W74J.js → platform-activity-row-HLOH7FW4.js} +9 -4
- package/dist/platform-activity-row-HLOH7FW4.js.map +1 -0
- package/dist/plugin-discovery.d.ts +6 -0
- package/dist/{queue-surface-implementation-LK3S4BIW.js → queue-surface-implementation-3RIFK2BS.js} +4 -4
- package/dist/realtime.js +22 -22
- package/dist/realtime.js.map +1 -1
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +12 -0
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +14 -12
- package/dist/session.d.ts +1 -0
- package/dist/session.js +6 -4
- package/dist/skill-discovery.d.ts +11 -0
- package/dist/timeline/compaction-copy.d.ts +10 -0
- package/dist/timeline/projection.d.ts +3 -1
- package/dist/timeline/tool-display-name.d.ts +2 -1
- package/dist/timeline/turn-summary.d.ts +2 -2
- package/dist/timeline/types.d.ts +24 -2
- package/dist/{timeline-annotation-markers-DRSTLEM6.js → timeline-annotation-markers-OY6OWR3V.js} +2 -2
- package/dist/{timeline-annotation-selection-IHLUGVND.js → timeline-annotation-selection-BOXA2TIN.js} +2 -2
- package/dist/{timeline-annotations-dialog-6Q53N5FG.js → timeline-annotations-dialog-RGY3MHL3.js} +3 -3
- package/package.json +3 -3
- package/src/approvals.ts +8 -1
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +0 -9
- package/src/components/approval-surface.tsx +12 -2
- package/src/components/artifacts/document-editor.tsx +63 -6
- package/src/components/artifacts/presentation-editor.tsx +214 -9
- package/src/components/artifacts/spreadsheet-canvas.ts +3 -8
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +10 -11
- package/src/components/chat-composer.tsx +26 -3
- package/src/components/composer.tsx +10 -4
- package/src/components/human-input-form.tsx +12 -2
- package/src/components/markdown.tsx +133 -9
- package/src/components/message-timeline.tsx +104 -36
- package/src/components/model-picker.tsx +11 -3
- package/src/components/model-policy-picker-menu.tsx +10 -3
- package/src/components/preview-loading.tsx +154 -0
- package/src/components/sandbox-workspace.tsx +20 -36
- package/src/components/session-capability-frame.tsx +163 -0
- package/src/components/session-chrome.tsx +46 -77
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +10 -5
- package/src/components/session-mcp-capability-card.tsx +532 -0
- package/src/components/session-status.tsx +2 -2
- package/src/components/timeline-anchor.tsx +9 -8
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/user-message-body.tsx +7 -0
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +114 -52
- package/src/connect-chooser.tsx +171 -39
- package/src/connect-panel.tsx +73 -9
- package/src/connect-setup.tsx +72 -25
- package/src/connect.ts +13 -22
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +61 -39
- package/src/connection-discovery.tsx +235 -0
- package/src/connection-installed.tsx +2 -0
- package/src/connection-logo.tsx +18 -10
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/conversation-timeline.ts +1 -0
- package/src/discovery-cache.ts +42 -0
- package/src/hooks/use-codex-accounts.ts +33 -14
- package/src/hooks/use-composer.ts +4 -5
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-session-events.ts +114 -1
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-workspace-sessions.ts +10 -0
- package/src/index.ts +22 -2
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-policy.ts +21 -0
- package/src/plugin-details.tsx +8 -5
- package/src/plugin-discovery.tsx +92 -72
- package/src/realtime/realtime-control.tsx +6 -8
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +83 -0
- package/src/session-ui.ts +2 -0
- package/src/session.ts +1 -0
- package/src/skill-discovery.tsx +101 -56
- package/src/timeline/compaction-copy.ts +40 -0
- package/src/timeline/platform-activity-row.tsx +8 -3
- package/src/timeline/projection.ts +78 -2
- package/src/timeline/rolling-activity.tsx +4 -2
- package/src/timeline/shared.tsx +6 -1
- package/src/timeline/startup-timings.tsx +1 -1
- package/src/timeline/tool-display-name.ts +8 -6
- package/src/timeline/tool-renderers.tsx +5 -3
- package/src/timeline/turn-summary.tsx +17 -3
- package/src/timeline/types.ts +24 -2
- package/styles/compiled.css +325 -27
- package/styles/connect.css +90 -15
- package/styles/index.css +46 -0
- package/styles/session-capability.css +42 -0
- package/dist/chunk-45XEUVTK.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-IHQII5WO.js +0 -886
- package/dist/chunk-IHQII5WO.js.map +0 -1
- package/dist/chunk-KANFU34B.js.map +0 -1
- package/dist/chunk-L2NBTKRX.js.map +0 -1
- package/dist/chunk-NYESIADM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-TDSEILUH.js.map +0 -1
- package/dist/chunk-UMECRJRV.js.map +0 -1
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-DUE2W74J.js.map +0 -1
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-LGH4IP47.js.map} +0 -0
- /package/dist/{chunk-NOSYBS2D.js.map → chunk-2VHARTBL.js.map} +0 -0
- /package/dist/{chunk-5M6VTFJ5.js.map → chunk-GBPCZH3Y.js.map} +0 -0
- /package/dist/{chunk-YBM6CQRU.js.map → chunk-GEN54DU5.js.map} +0 -0
- /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
- /package/dist/{chunk-NLTS7JHI.js.map → chunk-JPHDWKZE.js.map} +0 -0
- /package/dist/{chunk-24M4YBCL.js.map → chunk-PNAVEK2U.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
- /package/dist/{queue-surface-implementation-LK3S4BIW.js.map → queue-surface-implementation-3RIFK2BS.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-DRSTLEM6.js.map → timeline-annotation-markers-OY6OWR3V.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-IHLUGVND.js.map → timeline-annotation-selection-BOXA2TIN.js.map} +0 -0
- /package/dist/{timeline-annotations-dialog-6Q53N5FG.js.map → timeline-annotations-dialog-RGY3MHL3.js.map} +0 -0
|
@@ -44,6 +44,9 @@ const MAX_ZOOM = 4;
|
|
|
44
44
|
const RAIL_ITEM_HEIGHT = 116;
|
|
45
45
|
const RAIL_OVERSCAN = 3;
|
|
46
46
|
const DEFAULT_RAIL_HEIGHT = 640;
|
|
47
|
+
const MOBILE_RAIL_ITEM_HEIGHT = 44;
|
|
48
|
+
const MOBILE_RAIL_OVERSCAN = 3;
|
|
49
|
+
const DEFAULT_MOBILE_LIST_HEIGHT = 264;
|
|
47
50
|
const DEFAULT_VIEWPORT = { width: 960, height: 640 };
|
|
48
51
|
const SPATIAL_TILE = 256;
|
|
49
52
|
const MAX_SPATIAL_BUCKETS_PER_OBJECT = 1_024;
|
|
@@ -450,6 +453,15 @@ export function PresentationProjectionEditor({
|
|
|
450
453
|
);
|
|
451
454
|
}
|
|
452
455
|
|
|
456
|
+
function nextSlideIndexFromKey(key: string, activeIndex: number, length: number): number | null {
|
|
457
|
+
if (length === 0) return null;
|
|
458
|
+
if (key === "ArrowDown") return Math.min(length - 1, activeIndex + 1);
|
|
459
|
+
if (key === "ArrowUp") return Math.max(0, activeIndex - 1);
|
|
460
|
+
if (key === "Home") return 0;
|
|
461
|
+
if (key === "End") return length - 1;
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
|
|
453
465
|
function PresentationEditorCore({
|
|
454
466
|
presentation,
|
|
455
467
|
revision,
|
|
@@ -492,6 +504,11 @@ function PresentationEditorCore({
|
|
|
492
504
|
scrollTop: 0,
|
|
493
505
|
height: DEFAULT_RAIL_HEIGHT,
|
|
494
506
|
});
|
|
507
|
+
const [mobileSelectorOpen, setMobileSelectorOpen] = useState(false);
|
|
508
|
+
const [mobileRail, setMobileRail] = useState({
|
|
509
|
+
scrollTop: 0,
|
|
510
|
+
height: DEFAULT_MOBILE_LIST_HEIGHT,
|
|
511
|
+
});
|
|
495
512
|
const [viewport, setViewport] = useState<ViewportMetrics>({
|
|
496
513
|
scrollLeft: 0,
|
|
497
514
|
scrollTop: 0,
|
|
@@ -504,6 +521,9 @@ function PresentationEditorCore({
|
|
|
504
521
|
const [textDraft, setTextDraft] = useState("");
|
|
505
522
|
const [paintEpoch, setPaintEpoch] = useState(0);
|
|
506
523
|
const railRef = useRef<HTMLDivElement>(null);
|
|
524
|
+
const mobileSelectorTriggerRef = useRef<HTMLButtonElement>(null);
|
|
525
|
+
const mobileSelectorPanelRef = useRef<HTMLDivElement>(null);
|
|
526
|
+
const mobileRailRef = useRef<HTMLDivElement>(null);
|
|
507
527
|
const viewportRef = useRef<HTMLDivElement>(null);
|
|
508
528
|
const stageRef = useRef<HTMLDivElement>(null);
|
|
509
529
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
@@ -517,6 +537,7 @@ function PresentationEditorCore({
|
|
|
517
537
|
const commandVersionsRef = useRef(new Map<string, number>());
|
|
518
538
|
const pendingSlideIdRef = useRef<string | null>(null);
|
|
519
539
|
const pendingObjectIdRef = useRef<string | null>(null);
|
|
540
|
+
const mobileRailScrollOnOpenRef = useRef(0);
|
|
520
541
|
const domId = useId().replaceAll(":", "");
|
|
521
542
|
const selectionProxyId = `${domId}-presentation-selection`;
|
|
522
543
|
const instructionsId = `${domId}-presentation-instructions`;
|
|
@@ -596,6 +617,47 @@ function PresentationEditorCore({
|
|
|
596
617
|
return () => observer.disconnect();
|
|
597
618
|
}, []);
|
|
598
619
|
|
|
620
|
+
useLayoutEffect(() => {
|
|
621
|
+
if (!mobileSelectorOpen) return;
|
|
622
|
+
const element = mobileRailRef.current;
|
|
623
|
+
if (!element) return;
|
|
624
|
+
element.scrollTop = mobileRailScrollOnOpenRef.current;
|
|
625
|
+
element.focus();
|
|
626
|
+
const measure = () =>
|
|
627
|
+
setMobileRail((current) => ({
|
|
628
|
+
scrollTop: element.scrollTop,
|
|
629
|
+
height: element.clientHeight || current.height,
|
|
630
|
+
}));
|
|
631
|
+
measure();
|
|
632
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
633
|
+
const observer = new ResizeObserver(measure);
|
|
634
|
+
observer.observe(element);
|
|
635
|
+
return () => observer.disconnect();
|
|
636
|
+
}, [mobileSelectorOpen]);
|
|
637
|
+
|
|
638
|
+
useEffect(() => {
|
|
639
|
+
if (!mobileSelectorOpen) return;
|
|
640
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
641
|
+
const target = event.target;
|
|
642
|
+
if (!(target instanceof Node)) return;
|
|
643
|
+
if (mobileSelectorTriggerRef.current?.contains(target)) return;
|
|
644
|
+
if (mobileSelectorPanelRef.current?.contains(target)) return;
|
|
645
|
+
setMobileSelectorOpen(false);
|
|
646
|
+
};
|
|
647
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
648
|
+
if (event.key !== "Escape") return;
|
|
649
|
+
event.preventDefault();
|
|
650
|
+
setMobileSelectorOpen(false);
|
|
651
|
+
mobileSelectorTriggerRef.current?.focus();
|
|
652
|
+
};
|
|
653
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
654
|
+
document.addEventListener("keydown", onKeyDown);
|
|
655
|
+
return () => {
|
|
656
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
657
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
658
|
+
};
|
|
659
|
+
}, [mobileSelectorOpen]);
|
|
660
|
+
|
|
599
661
|
useLayoutEffect(() => {
|
|
600
662
|
const element = viewportRef.current;
|
|
601
663
|
if (!element) return;
|
|
@@ -1058,6 +1120,34 @@ function PresentationEditorCore({
|
|
|
1058
1120
|
[activeIndex, activeSlide, selectSlide, slides],
|
|
1059
1121
|
);
|
|
1060
1122
|
|
|
1123
|
+
const toggleMobileSlideSelector = useCallback(() => {
|
|
1124
|
+
setMobileSelectorOpen((open) => {
|
|
1125
|
+
if (open) return false;
|
|
1126
|
+
const index = Math.max(0, activeIndex);
|
|
1127
|
+
const height = DEFAULT_MOBILE_LIST_HEIGHT;
|
|
1128
|
+
const maxScroll = Math.max(0, slides.length * MOBILE_RAIL_ITEM_HEIGHT - height);
|
|
1129
|
+
const scrollTop = Math.max(
|
|
1130
|
+
0,
|
|
1131
|
+
Math.min(
|
|
1132
|
+
maxScroll,
|
|
1133
|
+
index * MOBILE_RAIL_ITEM_HEIGHT - height / 2 + MOBILE_RAIL_ITEM_HEIGHT / 2,
|
|
1134
|
+
),
|
|
1135
|
+
);
|
|
1136
|
+
mobileRailScrollOnOpenRef.current = scrollTop;
|
|
1137
|
+
setMobileRail({ scrollTop, height });
|
|
1138
|
+
return true;
|
|
1139
|
+
});
|
|
1140
|
+
}, [activeIndex, slides.length]);
|
|
1141
|
+
|
|
1142
|
+
const chooseMobileSlide = useCallback(
|
|
1143
|
+
(slide: Slide, index: number) => {
|
|
1144
|
+
if (slide !== activeSlide) selectSlide(slide, index);
|
|
1145
|
+
setMobileSelectorOpen(false);
|
|
1146
|
+
mobileSelectorTriggerRef.current?.focus();
|
|
1147
|
+
},
|
|
1148
|
+
[activeSlide, selectSlide],
|
|
1149
|
+
);
|
|
1150
|
+
|
|
1061
1151
|
const handleCanvasKeyDown = useCallback(
|
|
1062
1152
|
(event: ReactKeyboardEvent<SVGSVGElement>) => {
|
|
1063
1153
|
if (event.metaKey || event.ctrlKey) {
|
|
@@ -1229,6 +1319,17 @@ function PresentationEditorCore({
|
|
|
1229
1319
|
slides.length,
|
|
1230
1320
|
Math.ceil((rail.scrollTop + rail.height) / RAIL_ITEM_HEIGHT) + RAIL_OVERSCAN,
|
|
1231
1321
|
);
|
|
1322
|
+
const mobileRailStart = Math.max(
|
|
1323
|
+
0,
|
|
1324
|
+
Math.floor(mobileRail.scrollTop / MOBILE_RAIL_ITEM_HEIGHT) - MOBILE_RAIL_OVERSCAN,
|
|
1325
|
+
);
|
|
1326
|
+
const mobileRailEnd = Math.min(
|
|
1327
|
+
slides.length,
|
|
1328
|
+
Math.ceil((mobileRail.scrollTop + mobileRail.height) / MOBILE_RAIL_ITEM_HEIGHT) +
|
|
1329
|
+
MOBILE_RAIL_OVERSCAN,
|
|
1330
|
+
);
|
|
1331
|
+
const slideCountLabel =
|
|
1332
|
+
activeIndex >= 0 ? `${activeIndex + 1} / ${slides.length}` : `0 / ${slides.length}`;
|
|
1232
1333
|
const stageWidth = presentation.slideSize.width * zoom;
|
|
1233
1334
|
const stageHeight = presentation.slideSize.height * zoom;
|
|
1234
1335
|
|
|
@@ -1256,8 +1357,22 @@ function PresentationEditorCore({
|
|
|
1256
1357
|
>
|
|
1257
1358
|
<ChevronLeftIcon />
|
|
1258
1359
|
</button>
|
|
1259
|
-
<
|
|
1260
|
-
{
|
|
1360
|
+
<button
|
|
1361
|
+
ref={mobileSelectorTriggerRef}
|
|
1362
|
+
type="button"
|
|
1363
|
+
aria-label="Choose slide"
|
|
1364
|
+
aria-haspopup="listbox"
|
|
1365
|
+
aria-expanded={mobileSelectorOpen}
|
|
1366
|
+
aria-controls={mobileSelectorOpen ? `${domId}-mobile-slides` : undefined}
|
|
1367
|
+
disabled={slides.length === 0}
|
|
1368
|
+
data-og-mobile-slide-selector
|
|
1369
|
+
onClick={toggleMobileSlideSelector}
|
|
1370
|
+
className="inline-flex min-h-11 min-w-16 shrink-0 items-center justify-center rounded-og-sm px-2 text-og-xs tabular-nums text-og-fg-muted hover:bg-og-surface-3 hover:text-og-fg disabled:opacity-35 sm:hidden"
|
|
1371
|
+
>
|
|
1372
|
+
<span>{slideCountLabel}</span>
|
|
1373
|
+
</button>
|
|
1374
|
+
<span className="hidden min-w-16 shrink-0 text-center text-og-xs tabular-nums text-og-fg-muted sm:inline">
|
|
1375
|
+
{slideCountLabel}
|
|
1261
1376
|
</span>
|
|
1262
1377
|
<button
|
|
1263
1378
|
type="button"
|
|
@@ -1364,7 +1479,7 @@ function PresentationEditorCore({
|
|
|
1364
1479
|
{commandFailure?.message ?? (pendingCommands > 0 ? "Saving presentation changes" : "")}
|
|
1365
1480
|
</div>
|
|
1366
1481
|
|
|
1367
|
-
<div className="flex min-h-0 min-w-0 flex-1">
|
|
1482
|
+
<div className="relative flex min-h-0 min-w-0 flex-1">
|
|
1368
1483
|
<div
|
|
1369
1484
|
ref={railRef}
|
|
1370
1485
|
role="listbox"
|
|
@@ -1389,12 +1504,7 @@ function PresentationEditorCore({
|
|
|
1389
1504
|
}));
|
|
1390
1505
|
}}
|
|
1391
1506
|
onKeyDown={(event) => {
|
|
1392
|
-
|
|
1393
|
-
let nextIndex: number | null = null;
|
|
1394
|
-
if (event.key === "ArrowDown") nextIndex = Math.min(slides.length - 1, activeIndex + 1);
|
|
1395
|
-
else if (event.key === "ArrowUp") nextIndex = Math.max(0, activeIndex - 1);
|
|
1396
|
-
else if (event.key === "Home") nextIndex = 0;
|
|
1397
|
-
else if (event.key === "End") nextIndex = slides.length - 1;
|
|
1507
|
+
const nextIndex = nextSlideIndexFromKey(event.key, activeIndex, slides.length);
|
|
1398
1508
|
if (nextIndex === null) return;
|
|
1399
1509
|
event.preventDefault();
|
|
1400
1510
|
const next = slides[nextIndex];
|
|
@@ -1447,6 +1557,101 @@ function PresentationEditorCore({
|
|
|
1447
1557
|
</div>
|
|
1448
1558
|
</div>
|
|
1449
1559
|
|
|
1560
|
+
{mobileSelectorOpen ? (
|
|
1561
|
+
<div
|
|
1562
|
+
ref={mobileSelectorPanelRef}
|
|
1563
|
+
className="absolute left-2 top-2 z-40 max-w-56 overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 shadow-og-md sm:hidden"
|
|
1564
|
+
style={{ width: "calc(100% - 1rem)" }}
|
|
1565
|
+
>
|
|
1566
|
+
<div
|
|
1567
|
+
ref={mobileRailRef}
|
|
1568
|
+
id={`${domId}-mobile-slides`}
|
|
1569
|
+
role="listbox"
|
|
1570
|
+
tabIndex={0}
|
|
1571
|
+
aria-label="Slides"
|
|
1572
|
+
aria-orientation="vertical"
|
|
1573
|
+
aria-activedescendant={
|
|
1574
|
+
activeSlide && activeIndex >= mobileRailStart && activeIndex < mobileRailEnd
|
|
1575
|
+
? `${domId}-mobile-slide-${safeDomId(activeSlide.id)}`
|
|
1576
|
+
: undefined
|
|
1577
|
+
}
|
|
1578
|
+
className="max-h-72 overflow-y-auto outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent"
|
|
1579
|
+
data-og-mobile-slide-list
|
|
1580
|
+
data-og-window-start={mobileRailStart}
|
|
1581
|
+
data-og-window-end={mobileRailEnd}
|
|
1582
|
+
onScroll={(event) => {
|
|
1583
|
+
const scrollTop = event.currentTarget.scrollTop;
|
|
1584
|
+
const height = event.currentTarget.clientHeight;
|
|
1585
|
+
setMobileRail((current) => ({
|
|
1586
|
+
scrollTop,
|
|
1587
|
+
height: height || current.height,
|
|
1588
|
+
}));
|
|
1589
|
+
}}
|
|
1590
|
+
onKeyDown={(event) => {
|
|
1591
|
+
if (event.key === "Escape") {
|
|
1592
|
+
event.preventDefault();
|
|
1593
|
+
setMobileSelectorOpen(false);
|
|
1594
|
+
mobileSelectorTriggerRef.current?.focus();
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
const nextIndex = nextSlideIndexFromKey(event.key, activeIndex, slides.length);
|
|
1598
|
+
if (nextIndex === null) return;
|
|
1599
|
+
event.preventDefault();
|
|
1600
|
+
const next = slides[nextIndex];
|
|
1601
|
+
if (!next) return;
|
|
1602
|
+
if (next !== activeSlide) selectSlide(next, nextIndex);
|
|
1603
|
+
const list = event.currentTarget;
|
|
1604
|
+
const top = nextIndex * MOBILE_RAIL_ITEM_HEIGHT;
|
|
1605
|
+
const bottom = top + MOBILE_RAIL_ITEM_HEIGHT;
|
|
1606
|
+
if (top < list.scrollTop) list.scrollTop = top;
|
|
1607
|
+
else if (bottom > list.scrollTop + list.clientHeight) {
|
|
1608
|
+
list.scrollTop = bottom - list.clientHeight;
|
|
1609
|
+
}
|
|
1610
|
+
}}
|
|
1611
|
+
>
|
|
1612
|
+
<div
|
|
1613
|
+
className="relative w-full"
|
|
1614
|
+
style={{ height: slides.length * MOBILE_RAIL_ITEM_HEIGHT }}
|
|
1615
|
+
>
|
|
1616
|
+
{slides.slice(mobileRailStart, mobileRailEnd).map((slide, offset) => {
|
|
1617
|
+
const index = mobileRailStart + offset;
|
|
1618
|
+
const active = slide === activeSlide;
|
|
1619
|
+
return (
|
|
1620
|
+
<button
|
|
1621
|
+
key={slide.id}
|
|
1622
|
+
id={`${domId}-mobile-slide-${safeDomId(slide.id)}`}
|
|
1623
|
+
type="button"
|
|
1624
|
+
role="option"
|
|
1625
|
+
tabIndex={-1}
|
|
1626
|
+
aria-selected={active}
|
|
1627
|
+
aria-label={`Slide ${index + 1}${slide.title ? `: ${slide.title}` : ""}`}
|
|
1628
|
+
onClick={() => chooseMobileSlide(slide, index)}
|
|
1629
|
+
className={cn(
|
|
1630
|
+
"absolute left-0 flex min-h-11 w-full items-center gap-2 px-2 text-left",
|
|
1631
|
+
active
|
|
1632
|
+
? "bg-og-surface-3 text-og-fg"
|
|
1633
|
+
: "text-og-fg-muted hover:bg-og-surface-3/70",
|
|
1634
|
+
)}
|
|
1635
|
+
style={{
|
|
1636
|
+
top: index * MOBILE_RAIL_ITEM_HEIGHT,
|
|
1637
|
+
height: MOBILE_RAIL_ITEM_HEIGHT,
|
|
1638
|
+
}}
|
|
1639
|
+
data-og-mobile-slide-index={index}
|
|
1640
|
+
>
|
|
1641
|
+
<span className="w-7 shrink-0 text-right text-og-xs tabular-nums text-og-fg-subtle">
|
|
1642
|
+
{index + 1}
|
|
1643
|
+
</span>
|
|
1644
|
+
<span className="min-w-0 flex-1 truncate text-og-sm">
|
|
1645
|
+
{slide.title || `Slide ${index + 1}`}
|
|
1646
|
+
</span>
|
|
1647
|
+
</button>
|
|
1648
|
+
);
|
|
1649
|
+
})}
|
|
1650
|
+
</div>
|
|
1651
|
+
</div>
|
|
1652
|
+
</div>
|
|
1653
|
+
) : null}
|
|
1654
|
+
|
|
1450
1655
|
<div
|
|
1451
1656
|
ref={viewportRef}
|
|
1452
1657
|
className="relative min-h-0 min-w-0 flex-1 overflow-auto bg-og-surface-3/60 p-6"
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { formatSpreadsheetGeneralDisplay } from "./spreadsheet-general-display";
|
|
2
|
+
|
|
1
3
|
const TILE_SIZE = 256;
|
|
2
4
|
const MAX_CACHE_PHYSICAL_PIXELS = 32_000_000;
|
|
3
5
|
const MAX_CACHE_TILES = 96;
|
|
@@ -430,7 +432,7 @@ export class SpreadsheetCanvasRenderer {
|
|
|
430
432
|
|
|
431
433
|
for (const cell of cells) {
|
|
432
434
|
const value = input.projection.valueAt(cell);
|
|
433
|
-
const text =
|
|
435
|
+
const text = formatSpreadsheetGeneralDisplay(value);
|
|
434
436
|
if (!text) continue;
|
|
435
437
|
paintCellText(context, input, cell, text, tileLeft, tileTop);
|
|
436
438
|
}
|
|
@@ -776,13 +778,6 @@ function finitePositive(value: number | undefined, fallback: number): number {
|
|
|
776
778
|
return value !== undefined && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
777
779
|
}
|
|
778
780
|
|
|
779
|
-
function displayValue(value: unknown): string {
|
|
780
|
-
if (value === null || value === undefined) return "";
|
|
781
|
-
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : value.toLocaleDateString();
|
|
782
|
-
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
783
|
-
return String(value);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
781
|
function columnName(index: number): string {
|
|
787
782
|
let value = index + 1;
|
|
788
783
|
let result = "";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DISPLAY-ONLY General number formatting for spreadsheet canvas and a11y
|
|
3
|
+
* labels. This is not numeric arithmetic and must never feed formula-bar /
|
|
4
|
+
* cell-edit `input`, stored cells, formula results, or serialized exports.
|
|
5
|
+
*
|
|
6
|
+
* Policy for independent review:
|
|
7
|
+
* IEEE-754 cannot represent many decimal fractions exactly, so values such as
|
|
8
|
+
* `110.00000000000001` and `0.1 + 0.2` leak binary residue through `String(n)`.
|
|
9
|
+
* General display therefore collapses a finite number to 15 significant decimal
|
|
10
|
+
* digits — the same bound spreadsheet General uses — via
|
|
11
|
+
* `String(Number(value.toPrecision(15)))`.
|
|
12
|
+
*
|
|
13
|
+
* That mapping yields `110` / `220` / `0.3` for the noisy cases while keeping
|
|
14
|
+
* exponent-scale values (`1e-20`, `1e21`) and ~15-digit legitimate precision.
|
|
15
|
+
* Integers stringify without a decimal; `-0` displays as `0`; non-finite
|
|
16
|
+
* numbers, dates, booleans, errors, and strings keep their existing labels.
|
|
17
|
+
*
|
|
18
|
+
* Do not reuse `Intl.NumberFormat({ maximumFractionDigits: 12 })` from the
|
|
19
|
+
* artifact-tool renderer: 12 fraction digits rounds `1e-20` to `0` and truncates
|
|
20
|
+
* high-precision values that General should still show.
|
|
21
|
+
*/
|
|
22
|
+
export function formatSpreadsheetGeneralDisplay(value: unknown): string {
|
|
23
|
+
if (value === null || value === undefined) return "";
|
|
24
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : value.toLocaleDateString();
|
|
25
|
+
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
26
|
+
if (typeof value === "number") {
|
|
27
|
+
if (Object.is(value, -0) || value === 0) return "0";
|
|
28
|
+
if (!Number.isFinite(value)) return String(value);
|
|
29
|
+
const rounded = Number(value.toPrecision(15));
|
|
30
|
+
// Rounding at the finite IEEE-754 boundary can overflow. A display policy
|
|
31
|
+
// must not turn a finite source into an infinity label.
|
|
32
|
+
return String(Number.isFinite(rounded) ? rounded : value);
|
|
33
|
+
}
|
|
34
|
+
return String(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Exact edit/formula-bar source. Numbers stay `String(value)` with no General collapse. */
|
|
38
|
+
export function spreadsheetCellEditSource(value: unknown): string {
|
|
39
|
+
if (value === null || value === undefined) return "";
|
|
40
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : value.toLocaleDateString();
|
|
41
|
+
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
42
|
+
return String(value);
|
|
43
|
+
}
|
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
type SpreadsheetCanvasProjection,
|
|
27
27
|
type SpreadsheetCanvasTheme,
|
|
28
28
|
} from "./spreadsheet-canvas";
|
|
29
|
+
import {
|
|
30
|
+
formatSpreadsheetGeneralDisplay,
|
|
31
|
+
spreadsheetCellEditSource,
|
|
32
|
+
} from "./spreadsheet-general-display";
|
|
29
33
|
|
|
30
34
|
const MAX_ROWS = 1_048_576;
|
|
31
35
|
const MAX_COLUMNS = 16_384;
|
|
@@ -494,13 +498,6 @@ function isCellSelected(selection: SpreadsheetSelection, row: number, col: numbe
|
|
|
494
498
|
);
|
|
495
499
|
}
|
|
496
500
|
|
|
497
|
-
function displayValue(value: unknown): string {
|
|
498
|
-
if (value === null || value === undefined) return "";
|
|
499
|
-
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : value.toLocaleDateString();
|
|
500
|
-
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
501
|
-
return String(value);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
501
|
function parseCellInput(input: string): string | number | boolean | null {
|
|
505
502
|
if (input === "") return null;
|
|
506
503
|
const normalized = input.trim();
|
|
@@ -906,7 +903,7 @@ export function SpreadsheetGrid({
|
|
|
906
903
|
const data = worksheet.cellData(row, col);
|
|
907
904
|
return {
|
|
908
905
|
value: data.formula ? workbook.valueAt(worksheet, { row, col }) : data.value,
|
|
909
|
-
input: data.formula ??
|
|
906
|
+
input: data.formula ?? spreadsheetCellEditSource(data.value),
|
|
910
907
|
format: data.format,
|
|
911
908
|
};
|
|
912
909
|
},
|
|
@@ -2059,7 +2056,7 @@ export function SpreadsheetProjectionGrid({
|
|
|
2059
2056
|
role="gridcell"
|
|
2060
2057
|
aria-colindex={col + 1}
|
|
2061
2058
|
aria-selected={selected}
|
|
2062
|
-
aria-label={`${address}${value === null ? "" : `, ${
|
|
2059
|
+
aria-label={`${address}${value === null ? "" : `, ${formatSpreadsheetGeneralDisplay(value)}`}`}
|
|
2063
2060
|
data-og-cell={address}
|
|
2064
2061
|
className={cn(
|
|
2065
2062
|
"pointer-events-none absolute top-0 flex h-full min-w-0 overflow-hidden border-b border-r border-og-border px-1.5 text-og-fg",
|
|
@@ -2099,7 +2096,7 @@ export function SpreadsheetProjectionGrid({
|
|
|
2099
2096
|
/>
|
|
2100
2097
|
) : (
|
|
2101
2098
|
<span className="block min-w-0 overflow-hidden text-ellipsis">
|
|
2102
|
-
{
|
|
2099
|
+
{formatSpreadsheetGeneralDisplay(value)}
|
|
2103
2100
|
</span>
|
|
2104
2101
|
)}
|
|
2105
2102
|
</div>
|
|
@@ -2117,7 +2114,9 @@ export function SpreadsheetProjectionGrid({
|
|
|
2117
2114
|
aria-colindex={selection.focus.col + 1}
|
|
2118
2115
|
aria-selected="true"
|
|
2119
2116
|
aria-label={`${cellName(selection.focus)}${
|
|
2120
|
-
hiddenActiveValue == null
|
|
2117
|
+
hiddenActiveValue == null
|
|
2118
|
+
? ""
|
|
2119
|
+
: `, ${formatSpreadsheetGeneralDisplay(hiddenActiveValue)}`
|
|
2121
2120
|
}`}
|
|
2122
2121
|
/>
|
|
2123
2122
|
</div>
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { ClientModel, EffectiveSessionControl } from "@opengeni/sdk";
|
|
2
2
|
import { LayoutGroup, motion } from "motion/react";
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
type ClipboardEvent,
|
|
6
|
+
type ComponentProps,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
type Ref,
|
|
9
|
+
} from "react";
|
|
4
10
|
import type { SlashCommand } from "../commands/types";
|
|
5
11
|
import type { ComposerState } from "../hooks/use-composer";
|
|
6
12
|
import type { UseFileAttachmentsResult } from "../hooks/use-file-attachments";
|
|
@@ -41,6 +47,14 @@ export { OPEN_WORKSTREAM_CONTROL_EVENT };
|
|
|
41
47
|
|
|
42
48
|
export type ChatComposerProps = {
|
|
43
49
|
composer: ComposerState;
|
|
50
|
+
/** Replace only the normal footer, inside the shared composer context.
|
|
51
|
+
* Confirmation, drafts, annotations and attachments remain native. */
|
|
52
|
+
footer?: ReactNode;
|
|
53
|
+
focusRef?: Ref<{ focusInput: () => void }>;
|
|
54
|
+
inputProps?: ComponentProps<typeof Input> & {
|
|
55
|
+
[attribute: `data-${string}`]: string | number | boolean | undefined;
|
|
56
|
+
};
|
|
57
|
+
surfaceClassName?: string;
|
|
44
58
|
/**
|
|
45
59
|
* Measurement surface for responsive chrome. Defaults to the historical
|
|
46
60
|
* viewport breakpoints; opt into `container` for narrow embedded panels.
|
|
@@ -125,6 +139,10 @@ export function ChatComposer({
|
|
|
125
139
|
commandContext,
|
|
126
140
|
onClearView,
|
|
127
141
|
messages,
|
|
142
|
+
footer,
|
|
143
|
+
focusRef,
|
|
144
|
+
inputProps,
|
|
145
|
+
surfaceClassName,
|
|
128
146
|
}: ChatComposerProps) {
|
|
129
147
|
const controller = useChatComposerController({
|
|
130
148
|
delivery: composer,
|
|
@@ -146,13 +164,16 @@ export function ChatComposer({
|
|
|
146
164
|
attachments || models || controlsLeading || controlsStart || transcription,
|
|
147
165
|
);
|
|
148
166
|
const stackActions = hasControls && Boolean(actionsStart);
|
|
167
|
+
useImperativeHandle(focusRef, () => ({ focusInput: controller.focusInput }), [
|
|
168
|
+
controller.focusInput,
|
|
169
|
+
]);
|
|
149
170
|
|
|
150
171
|
return (
|
|
151
172
|
<>
|
|
152
173
|
<Root controller={controller} responsiveBasis={responsiveBasis} className={className}>
|
|
153
174
|
<Frame>
|
|
154
175
|
<CommandPalette />
|
|
155
|
-
<Surface>
|
|
176
|
+
<Surface className={surfaceClassName}>
|
|
156
177
|
<PausedState />
|
|
157
178
|
<RestoredResources />
|
|
158
179
|
<Attachments />
|
|
@@ -172,9 +193,11 @@ export function ChatComposer({
|
|
|
172
193
|
/>
|
|
173
194
|
</div>
|
|
174
195
|
) : null}
|
|
175
|
-
<Input placeholder={placeholder} autoFocus={autoFocus} />
|
|
196
|
+
<Input {...inputProps} placeholder={placeholder} autoFocus={autoFocus} />
|
|
176
197
|
{controller.confirmState ? (
|
|
177
198
|
<Confirmation />
|
|
199
|
+
) : footer !== undefined ? (
|
|
200
|
+
footer
|
|
178
201
|
) : (
|
|
179
202
|
<Footer
|
|
180
203
|
data-og-stack-actions={stackActions ? "" : undefined}
|
|
@@ -449,7 +449,6 @@ export function useChatComposerController({
|
|
|
449
449
|
}, [id, paused]);
|
|
450
450
|
|
|
451
451
|
const blockedByAttachment = attachments?.hasUnresolved === true;
|
|
452
|
-
const hasReadyAttachment = (attachments?.readyResources.length ?? 0) > 0;
|
|
453
452
|
|
|
454
453
|
const [dragging, setDragging] = useState(false);
|
|
455
454
|
const dragCarriesFiles = (event: { dataTransfer: DataTransfer | null }): boolean =>
|
|
@@ -573,7 +572,7 @@ export function useChatComposerController({
|
|
|
573
572
|
? "command"
|
|
574
573
|
: annotationsIncomplete
|
|
575
574
|
? "annotations"
|
|
576
|
-
: delivery.canSend
|
|
575
|
+
: delivery.canSend
|
|
577
576
|
? null
|
|
578
577
|
: "empty";
|
|
579
578
|
const canSubmit = submitBlocker === null;
|
|
@@ -593,7 +592,7 @@ export function useChatComposerController({
|
|
|
593
592
|
delivery.requestAnnotationReview?.();
|
|
594
593
|
return false;
|
|
595
594
|
}
|
|
596
|
-
if (!delivery.canSend
|
|
595
|
+
if (!delivery.canSend) return false;
|
|
597
596
|
submittingRef.current = true;
|
|
598
597
|
setSubmitting(true);
|
|
599
598
|
try {
|
|
@@ -609,7 +608,6 @@ export function useChatComposerController({
|
|
|
609
608
|
commandDraftBlocked,
|
|
610
609
|
delivery,
|
|
611
610
|
disabled,
|
|
612
|
-
hasReadyAttachment,
|
|
613
611
|
messages.annotationNotesRequired,
|
|
614
612
|
messages.slashCommandBlocked,
|
|
615
613
|
],
|
|
@@ -1015,6 +1013,14 @@ export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(functio
|
|
|
1015
1013
|
const textarea = controller.textareaRef.current;
|
|
1016
1014
|
if (!textarea || textarea.disabled) return;
|
|
1017
1015
|
autoFocusedRef.current = true;
|
|
1016
|
+
// Draft hydration may finish after the user has started editing another
|
|
1017
|
+
// field. Initial focus is a convenience, not authority to take their caret.
|
|
1018
|
+
const active = textarea.ownerDocument.activeElement;
|
|
1019
|
+
if (
|
|
1020
|
+
active !== textarea &&
|
|
1021
|
+
active?.closest('input, textarea, select, [contenteditable="true"], [role="textbox"]')
|
|
1022
|
+
)
|
|
1023
|
+
return;
|
|
1018
1024
|
textarea.focus();
|
|
1019
1025
|
});
|
|
1020
1026
|
return () => window.cancelAnimationFrame(frame);
|
|
@@ -159,6 +159,9 @@ function HumanInputRequestForm({
|
|
|
159
159
|
revisionId: question.skillReview!.revisionId,
|
|
160
160
|
expectedRevisionId: question.skillReview!.expectedRevisionId,
|
|
161
161
|
expectedScopeVersion: question.skillReview!.expectedScopeVersion,
|
|
162
|
+
...(question.skillReview!.removalOperationId
|
|
163
|
+
? { removalOperationId: question.skillReview!.removalOperationId }
|
|
164
|
+
: {}),
|
|
162
165
|
},
|
|
163
166
|
})),
|
|
164
167
|
);
|
|
@@ -188,6 +191,7 @@ function HumanInputRequestForm({
|
|
|
188
191
|
if (
|
|
189
192
|
record.id !== reference.skillId ||
|
|
190
193
|
record.revisionId !== reference.revisionId ||
|
|
194
|
+
(record.removalOperationId ?? undefined) !== reference.removalOperationId ||
|
|
191
195
|
!record.files.some((file) => file.path === "SKILL.md")
|
|
192
196
|
) {
|
|
193
197
|
throw new Error("The requested Skill revision could not be verified.");
|
|
@@ -230,7 +234,9 @@ function HumanInputRequestForm({
|
|
|
230
234
|
{record.title ?? "Skill"} · {record.scope} · {record.files.length} files
|
|
231
235
|
</p>
|
|
232
236
|
<p className="text-og-xs text-og-fg-muted">
|
|
233
|
-
|
|
237
|
+
{question.skillReview.removalOperationId
|
|
238
|
+
? "Permanently deletes this Skill and all stored revisions. This cannot be undone; conversations remain unchanged."
|
|
239
|
+
: "Saving activates these exact files. No additional review is required."}
|
|
234
240
|
</p>
|
|
235
241
|
{record.files.map((file) => (
|
|
236
242
|
<details key={file.path} open={file.path === "SKILL.md"}>
|
|
@@ -357,7 +363,11 @@ function HumanInputRequestForm({
|
|
|
357
363
|
),
|
|
358
364
|
);
|
|
359
365
|
if (unreviewedSave) {
|
|
360
|
-
setValidationErrors({
|
|
366
|
+
setValidationErrors({
|
|
367
|
+
[unreviewedSave.id]: unreviewedSave.skillReview?.removalOperationId
|
|
368
|
+
? "Load the exact Skill proposal before approving permanent deletion."
|
|
369
|
+
: "Load the exact Skill files before saving.",
|
|
370
|
+
});
|
|
361
371
|
return;
|
|
362
372
|
}
|
|
363
373
|
await submitResponse({ outcome: "answered", answers: result.answers });
|