@puredesktop/puredesktop-ui-bridge 1.0.0-beta.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/package.json +96 -0
- package/src/agents/runtime/agentLiveReducer.test.ts +72 -0
- package/src/agents/runtime/agentLiveReducer.ts +139 -0
- package/src/agents/runtime/agentRunEventTypes.ts +115 -0
- package/src/agents/runtime/contextWindowUsage.test.ts +38 -0
- package/src/agents/runtime/contextWindowUsage.ts +25 -0
- package/src/agents/runtime/index.ts +24 -0
- package/src/agents/runtime/mapAgentMessages.ts +54 -0
- package/src/agents/runtime/mapAgentSnapshot.ts +71 -0
- package/src/agents/runtime/mergeUsageTurn.ts +23 -0
- package/src/agents/runtime/pendingToolCalls.test.ts +85 -0
- package/src/agents/runtime/pendingToolCalls.ts +14 -0
- package/src/agents/runtime/toolRequiresApproval.ts +20 -0
- package/src/agents/runtime/unresolvedToolCalls.ts +42 -0
- package/src/bridge/agentModelTiers.ts +50 -0
- package/src/bridge/agentToolHandlers.ts +23 -0
- package/src/bridge/agentToolHelpers.test.ts +27 -0
- package/src/bridge/agentToolHelpers.ts +20 -0
- package/src/bridge/agentTypes.ts +294 -0
- package/src/bridge/agents.d.mts +113 -0
- package/src/bridge/agents.mjs +233 -0
- package/src/bridge/agents.ts +62 -0
- package/src/bridge/appSettings.d.mts +9 -0
- package/src/bridge/appSettings.mjs +18 -0
- package/src/bridge/appSettings.ts +4 -0
- package/src/bridge/applyThemeFromHost.d.mts +7 -0
- package/src/bridge/applyThemeFromHost.mjs +33 -0
- package/src/bridge/assets.d.mts +66 -0
- package/src/bridge/assets.mjs +121 -0
- package/src/bridge/catalog.d.mts +5 -0
- package/src/bridge/catalog.mjs +24 -0
- package/src/bridge/catalog.ts +1 -0
- package/src/bridge/client.d.mts +19 -0
- package/src/bridge/client.mjs +147 -0
- package/src/bridge/collectionAssetAdapter.ts +63 -0
- package/src/bridge/collectionAssets.d.mts +1 -0
- package/src/bridge/collectionAssets.mjs +2 -0
- package/src/bridge/collectionAssets.ts +1 -0
- package/src/bridge/collectionDocumentHtml.test.ts +71 -0
- package/src/bridge/collectionDocumentHtml.ts +118 -0
- package/src/bridge/collectionImagePaste.test.ts +32 -0
- package/src/bridge/collectionImagePaste.ts +130 -0
- package/src/bridge/context.mjs +12 -0
- package/src/bridge/credentials.mjs +22 -0
- package/src/bridge/dialog.d.mts +5 -0
- package/src/bridge/dialog.mjs +13 -0
- package/src/bridge/events.d.mts +14 -0
- package/src/bridge/events.mjs +12 -0
- package/src/bridge/filePreviewKind.test.ts +43 -0
- package/src/bridge/filePreviewKind.ts +76 -0
- package/src/bridge/fs.d.mts +41 -0
- package/src/bridge/fs.mjs +82 -0
- package/src/bridge/fs.test.ts +16 -0
- package/src/bridge/fs.ts +10 -0
- package/src/bridge/index.ts +12 -0
- package/src/bridge/inlinePreview.d.mts +35 -0
- package/src/bridge/inlinePreview.mjs +99 -0
- package/src/bridge/inlinePreview.test.ts +56 -0
- package/src/bridge/inlinePreview.ts +13 -0
- package/src/bridge/methods.d.mts +115 -0
- package/src/bridge/methods.mjs +132 -0
- package/src/bridge/os.d.mts +11 -0
- package/src/bridge/os.mjs +26 -0
- package/src/bridge/plugins.d.mts +9 -0
- package/src/bridge/plugins.mjs +31 -0
- package/src/bridge/plugins.ts +5 -0
- package/src/bridge/preferences.d.mts +7 -0
- package/src/bridge/preferences.mjs +17 -0
- package/src/bridge/preferences.ts +13 -0
- package/src/bridge/printDocument.test.ts +47 -0
- package/src/bridge/printDocument.ts +75 -0
- package/src/bridge/pureRender/adapters.test.ts +144 -0
- package/src/bridge/pureRender/adapters.ts +175 -0
- package/src/bridge/pureRender/assets.test.ts +44 -0
- package/src/bridge/pureRender/base.css +76 -0
- package/src/bridge/pureRender/compiler.test.ts +279 -0
- package/src/bridge/pureRender/compiler.ts +660 -0
- package/src/bridge/pureRender/document.test.ts +283 -0
- package/src/bridge/pureRender/document.ts +35 -0
- package/src/bridge/pureRender/extraction.test.ts +432 -0
- package/src/bridge/pureRender/extraction.ts +1023 -0
- package/src/bridge/pureRender/flow.css +3 -0
- package/src/bridge/pureRender/index.ts +66 -0
- package/src/bridge/pureRender/normalize.test.ts +442 -0
- package/src/bridge/pureRender/normalize.ts +682 -0
- package/src/bridge/pureRender/presets.ts +365 -0
- package/src/bridge/pureRender/profiles/book.css +19 -0
- package/src/bridge/pureRender/profiles/manuscript.css +11 -0
- package/src/bridge/pureRender/profiles/writer.css +10 -0
- package/src/bridge/pureRender/reset.css +5 -0
- package/src/bridge/pureRender/samples.ts +94 -0
- package/src/bridge/pureRender/styles.ts +164 -0
- package/src/bridge/pureRender/theme.test.ts +53 -0
- package/src/bridge/pureRender/theme.ts +258 -0
- package/src/bridge/pureRender/tokens/default.css +36 -0
- package/src/bridge/pureRender/tokens/default.json +32 -0
- package/src/bridge/pureRender/types.ts +264 -0
- package/src/bridge/pureRender/visualLayoutInspection.test.ts +57 -0
- package/src/bridge/pureRender/visualLayoutInspection.ts +161 -0
- package/src/bridge/react/useDebouncedEffect.ts +21 -0
- package/src/bridge/react/useIpc.tsx +75 -0
- package/src/bridge/react/usePlatformAgentRunLiveState.tsx +78 -0
- package/src/bridge/react/usePlatformAgentSessionDrive.tsx +729 -0
- package/src/bridge/react/usePlatformAgentSessions.tsx +60 -0
- package/src/bridge/react/usePlatformAgentTools.tsx +82 -0
- package/src/bridge/react/usePlatformAppManifest.tsx +32 -0
- package/src/bridge/react/usePlatformBridge.tsx +56 -0
- package/src/bridge/react/usePlatformDeepLink.tsx +57 -0
- package/src/bridge/react/usePlatformFileBinaryDataUrl.tsx +54 -0
- package/src/bridge/react/usePlatformFilePreview.tsx +92 -0
- package/src/bridge/react/usePlatformPreferences.tsx +77 -0
- package/src/bridge/react/usePlatformThemeDocumentAttrs.ts +35 -0
- package/src/bridge/react/usePlatformThemeFromBridge.tsx +16 -0
- package/src/bridge/react/usePlatformViewportResource.test.tsx +106 -0
- package/src/bridge/react/usePlatformViewportResource.tsx +72 -0
- package/src/bridge/react/useScheduledTask.tsx +59 -0
- package/src/bridge/render.d.mts +31 -0
- package/src/bridge/render.mjs +62 -0
- package/src/bridge/render.ts +8 -0
- package/src/bridge/screenAnnotation.test.ts +103 -0
- package/src/bridge/screenAnnotation.ts +269 -0
- package/src/bridge/theme.d.mts +3 -0
- package/src/bridge/theme.mjs +9 -0
- package/src/bridge/types.ts +439 -0
- package/src/bridge/watch.d.mts +9 -0
- package/src/bridge/watch.mjs +34 -0
- package/src/bridge/watch.ts +1 -0
- package/src/bridge/window.d.mts +44 -0
- package/src/bridge/window.mjs +33 -0
- package/src/chrome/index.ts +1 -0
- package/src/components/accessibility/AccessibilityReviewDialog.test.tsx +104 -0
- package/src/components/accessibility/AccessibilityReviewDialog.tsx +350 -0
- package/src/components/accessibility/accessibilityReview.test.ts +101 -0
- package/src/components/accessibility/accessibilityReview.ts +639 -0
- package/src/components/accessibility/index.ts +16 -0
- package/src/components/agent-feedback/ScreenAnnotationAgentDialog.tsx +509 -0
- package/src/components/agent-feedback/index.ts +5 -0
- package/src/components/agents/AgentComposer.tsx +34 -0
- package/src/components/agents/AgentContextUsageBar.tsx +127 -0
- package/src/components/agents/AgentDrawerPanel.tsx +82 -0
- package/src/components/agents/AgentMarkdown.tsx +12 -0
- package/src/components/agents/AgentMessageBubble.tsx +82 -0
- package/src/components/agents/AgentMessageList.tsx +47 -0
- package/src/components/agents/AgentModelMenu.tsx +55 -0
- package/src/components/agents/AgentThinkingBlock.tsx +66 -0
- package/src/components/agents/AgentToolApprovalActions.tsx +65 -0
- package/src/components/agents/AgentToolCallCard.tsx +26 -0
- package/src/components/agents/AgentToolPendingList.tsx +39 -0
- package/src/components/agents/AgentToolPendingReview.tsx +118 -0
- package/src/components/agents/agentPanelStyles.ts +79 -0
- package/src/components/agents/agentTypes.ts +96 -0
- package/src/components/agents/formatToolCallArguments.test.ts +16 -0
- package/src/components/agents/formatToolCallArguments.ts +9 -0
- package/src/components/agents/index.ts +40 -0
- package/src/components/assets/AssetLibraryDialog.tsx +5 -0
- package/src/components/assets/asset-library/AssetLibraryDialog.tsx +43 -0
- package/src/components/assets/asset-library/AssetLibraryWorkspace.tsx +54 -0
- package/src/components/assets/asset-library/detail/AssetLibraryDetailActions.tsx +68 -0
- package/src/components/assets/asset-library/detail/AssetLibraryDetailHeader.tsx +56 -0
- package/src/components/assets/asset-library/detail/AssetLibraryDetailPane.tsx +90 -0
- package/src/components/assets/asset-library/detail/AssetLibraryDetailPreview.tsx +67 -0
- package/src/components/assets/asset-library/detail/AssetLibraryInlinePreview.tsx +87 -0
- package/src/components/assets/asset-library/detail/AssetLibraryInlineSourceField.tsx +29 -0
- package/src/components/assets/asset-library/detail/AssetLibraryMetadataFields.tsx +34 -0
- package/src/components/assets/asset-library/detail/AssetLibraryUsagePanel.tsx +46 -0
- package/src/components/assets/asset-library/detail/AssetLibraryVersionsPanel.tsx +145 -0
- package/src/components/assets/asset-library/helpers/assetKind.ts +74 -0
- package/src/components/assets/asset-library/helpers.ts +41 -0
- package/src/components/assets/asset-library/hooks/useAssetDetailDraft.ts +81 -0
- package/src/components/assets/asset-library/hooks/useAssetLibraryDialog.ts +233 -0
- package/src/components/assets/asset-library/hooks/useAssetLibraryMutations.ts +266 -0
- package/src/components/assets/asset-library/hooks/useAssetLibrarySelectionSync.ts +24 -0
- package/src/components/assets/asset-library/hooks/useAssetLibraryVersions.ts +145 -0
- package/src/components/assets/asset-library/hooks/useInlineAssetPreview.ts +44 -0
- package/src/components/assets/asset-library/index.ts +19 -0
- package/src/components/assets/asset-library/layout/assetLibraryLayout.ts +201 -0
- package/src/components/assets/asset-library/shared/fieldStyles.ts +62 -0
- package/src/components/assets/asset-library/sidebar/AssetLibraryFilterPills.tsx +89 -0
- package/src/components/assets/asset-library/sidebar/AssetLibraryList.tsx +91 -0
- package/src/components/assets/asset-library/sidebar/AssetLibraryListRow.tsx +99 -0
- package/src/components/assets/asset-library/sidebar/AssetLibraryListStatus.tsx +24 -0
- package/src/components/assets/asset-library/sidebar/AssetLibrarySidebar.tsx +48 -0
- package/src/components/assets/asset-library/sidebar/AssetLibrarySidebarActions.tsx +38 -0
- package/src/components/assets/asset-library/sidebar/AssetLibrarySidebarToolbar.tsx +78 -0
- package/src/components/assets/asset-library/sidebar/AssetListThumbnail.tsx +86 -0
- package/src/components/assets/asset-library/types.ts +128 -0
- package/src/components/assets/asset-library/utils/assetLibraryUtils.ts +181 -0
- package/src/components/assets/asset-library/utils/pathBasename.ts +5 -0
- package/src/components/assets/asset-library/utils/sanitizeUploadFileName.ts +7 -0
- package/src/components/assets/asset-library/utils/uploadCollectionAsset.ts +52 -0
- package/src/components/chrome/OverlayActionCapsule.test.tsx +82 -0
- package/src/components/chrome/OverlayActionCapsule.tsx +90 -0
- package/src/components/chrome/PanelTabStrip.tsx +113 -0
- package/src/components/chrome/PlatformIcon.tsx +27 -0
- package/src/components/chrome/PureAppWordmark.tsx +89 -0
- package/src/components/chrome/ShellWindowControls.tsx +51 -0
- package/src/components/chrome/ThemeModeButton.tsx +16 -0
- package/src/components/chrome/Titlebar.tsx +79 -0
- package/src/components/chrome/WorkspaceTabStrip.tsx +300 -0
- package/src/components/chrome/index.ts +31 -0
- package/src/components/chrome/workspaceTabTypes.ts +29 -0
- package/src/components/common/buttons/Button.tsx +241 -0
- package/src/components/common/buttons/ChoiceChip.tsx +73 -0
- package/src/components/common/buttons/CleanButton.tsx +34 -0
- package/src/components/common/buttons/IconButton.tsx +89 -0
- package/src/components/common/buttons/QuietAction.tsx +29 -0
- package/src/components/common/buttons/SegmentedControl.tsx +67 -0
- package/src/components/common/buttons/TextActionButton.tsx +55 -0
- package/src/components/common/buttons/TextLink.tsx +38 -0
- package/src/components/common/canvas/CanvasElementFrame.test.tsx +79 -0
- package/src/components/common/canvas/CanvasElementFrame.tsx +182 -0
- package/src/components/common/canvas/canvasGeometry.test.ts +66 -0
- package/src/components/common/canvas/canvasGeometry.ts +133 -0
- package/src/components/common/canvas/index.ts +27 -0
- package/src/components/common/canvas/useCanvasDragResize.test.tsx +152 -0
- package/src/components/common/canvas/useCanvasDragResize.ts +161 -0
- package/src/components/common/chat/ChatAssistantMessage.tsx +81 -0
- package/src/components/common/chat/ChatBox.tsx +189 -0
- package/src/components/common/chat/ChatMarkdown.tsx +109 -0
- package/src/components/common/chat/ChatModelMenu.tsx +258 -0
- package/src/components/common/chat/ChatStatusRow.tsx +24 -0
- package/src/components/common/chat/ChatThinkingIndicator.tsx +54 -0
- package/src/components/common/chat/ChatThread.tsx +59 -0
- package/src/components/common/chat/ChatToolRow.tsx +141 -0
- package/src/components/common/chat/ChatUserBubble.tsx +45 -0
- package/src/components/common/chat/chatUtils.ts +7 -0
- package/src/components/common/chat/index.ts +24 -0
- package/src/components/common/connections/ConnectionSetup.tsx +529 -0
- package/src/components/common/connections/index.ts +26 -0
- package/src/components/common/containers/AppFrame.test.tsx +47 -0
- package/src/components/common/containers/AppFrame.tsx +65 -0
- package/src/components/common/containers/AppHeader.tsx +60 -0
- package/src/components/common/containers/AutoFillGrid.tsx +37 -0
- package/src/components/common/containers/Collapsible.tsx +43 -0
- package/src/components/common/containers/CollapsibleGrid.tsx +174 -0
- package/src/components/common/containers/MotionScope.tsx +32 -0
- package/src/components/common/containers/PageHeader.tsx +35 -0
- package/src/components/common/containers/PanelToolbarHeader.tsx +113 -0
- package/src/components/common/containers/QuietDisclosure.tsx +91 -0
- package/src/components/common/containers/QuietSurface.tsx +41 -0
- package/src/components/common/containers/ScrollPanel.tsx +33 -0
- package/src/components/common/containers/StatusBar.tsx +42 -0
- package/src/components/common/containers/SurfacePanel.tsx +66 -0
- package/src/components/common/containers/ToolbarStrip.tsx +80 -0
- package/src/components/common/containers/flex.ts +117 -0
- package/src/components/common/containers/useOpenTransition.ts +33 -0
- package/src/components/common/context/ContextSelector.tsx +269 -0
- package/src/components/common/context/index.ts +7 -0
- package/src/components/common/desk/ActionReader.tsx +270 -0
- package/src/components/common/desk/ActivityLedger.tsx +143 -0
- package/src/components/common/desk/CreationDesk.tsx +139 -0
- package/src/components/common/desk/DailyBrief.tsx +160 -0
- package/src/components/common/desk/DeskCard.tsx +157 -0
- package/src/components/common/desk/index.ts +30 -0
- package/src/components/common/dropdown/ContextMenu.tsx +87 -0
- package/src/components/common/dropdown/MenuButtonDropdown.tsx +121 -0
- package/src/components/common/dropdown/MenuDropdown.tsx +63 -0
- package/src/components/common/dropdown/MenuDropdownEntry.tsx +51 -0
- package/src/components/common/dropdown/MenuDropdownItem.tsx +82 -0
- package/src/components/common/dropdown/MenuDropdownSubmenu.tsx +145 -0
- package/src/components/common/dropdown/MenuPanel.tsx +69 -0
- package/src/components/common/dropdown/MenuPortal.tsx +162 -0
- package/src/components/common/dropdown/MenuPortalLayer.tsx +6 -0
- package/src/components/common/dropdown/SelectMenu.tsx +243 -0
- package/src/components/common/dropdown/SelectMenuControl.tsx +23 -0
- package/src/components/common/dropdown/SelectMenuOptions.tsx +38 -0
- package/src/components/common/dropdown/menuPortalConstants.ts +11 -0
- package/src/components/common/dropdown/menuTypes.ts +71 -0
- package/src/components/common/dropdown/menuViewportPosition.test.ts +139 -0
- package/src/components/common/dropdown/menuViewportPosition.ts +122 -0
- package/src/components/common/dropdown/selectMenuLayout.test.ts +42 -0
- package/src/components/common/dropdown/selectMenuLayout.ts +123 -0
- package/src/components/common/dropdown/useMenuAnchorPosition.ts +109 -0
- package/src/components/common/dropdown/useSelectMenuAnchor.ts +30 -0
- package/src/components/common/evidence/StructuredEvidenceView.test.tsx +77 -0
- package/src/components/common/evidence/StructuredEvidenceView.tsx +312 -0
- package/src/components/common/evidence/index.ts +13 -0
- package/src/components/common/feedback/Badge.tsx +33 -0
- package/src/components/common/feedback/CapabilityPills.tsx +36 -0
- package/src/components/common/feedback/EmptyState.tsx +132 -0
- package/src/components/common/feedback/InfoHint.tsx +67 -0
- package/src/components/common/feedback/InlineBanner.tsx +82 -0
- package/src/components/common/feedback/PlatformChip.tsx +104 -0
- package/src/components/common/feedback/QuietPill.tsx +75 -0
- package/src/components/common/feedback/WorkProgressPanel.tsx +156 -0
- package/src/components/common/feedback/index.ts +33 -0
- package/src/components/common/inputs/BareInput.tsx +31 -0
- package/src/components/common/inputs/BareTextarea.tsx +10 -0
- package/src/components/common/inputs/Checkbox.tsx +13 -0
- package/src/components/common/inputs/FieldControlShell.tsx +49 -0
- package/src/components/common/inputs/FormField.tsx +40 -0
- package/src/components/common/inputs/HybridSearchField.tsx +280 -0
- package/src/components/common/inputs/InlineComposer.tsx +197 -0
- package/src/components/common/inputs/PaletteCirclePicker.tsx +26 -0
- package/src/components/common/inputs/PaletteDropdown.tsx +413 -0
- package/src/components/common/inputs/Radio.tsx +19 -0
- package/src/components/common/inputs/SearchField.tsx +128 -0
- package/src/components/common/inputs/SelectField.tsx +169 -0
- package/src/components/common/inputs/SurfacePalettePicker.tsx +54 -0
- package/src/components/common/inputs/TextAreaField.tsx +22 -0
- package/src/components/common/inputs/TextField.tsx +51 -0
- package/src/components/common/inputs/ToggleSwitch.tsx +110 -0
- package/src/components/common/inputs/WorkspacePicker.tsx +349 -0
- package/src/components/common/inputs/fieldStyles.ts +137 -0
- package/src/components/common/layout/PanelGroup.tsx +479 -0
- package/src/components/common/layout/index.ts +31 -0
- package/src/components/common/layout/panelLayoutConstants.ts +7 -0
- package/src/components/common/layout/panelLayoutPersistence.ts +35 -0
- package/src/components/common/layout/panelLayoutState.test.ts +38 -0
- package/src/components/common/layout/panelLayoutState.ts +174 -0
- package/src/components/common/layout/panelLayoutTypes.ts +19 -0
- package/src/components/common/layout/tabs/Tabs.tsx +136 -0
- package/src/components/common/lists/listRowStyles.ts +32 -0
- package/src/components/common/lists/quiet-ledger/QuietActivityList.tsx +64 -0
- package/src/components/common/lists/quiet-ledger/QuietLedgerRow.tsx +119 -0
- package/src/components/common/lists/quiet-ledger/index.ts +9 -0
- package/src/components/common/lists/reorderable/DragHandle.tsx +65 -0
- package/src/components/common/lists/reorderable/ReorderableList.tsx +280 -0
- package/src/components/common/lists/reorderable/ReorderableListRow.tsx +102 -0
- package/src/components/common/lists/reorderable/ReorderableSections.tsx +344 -0
- package/src/components/common/lists/reorderable/ReorderableSectionsDemo.tsx +94 -0
- package/src/components/common/lists/reorderable/index.ts +15 -0
- package/src/components/common/lists/reorderable/reorderableUtils.test.ts +28 -0
- package/src/components/common/lists/reorderable/reorderableUtils.ts +23 -0
- package/src/components/common/lists/reorderable/types.ts +40 -0
- package/src/components/common/lists/reorderable/useReorderableDragState.ts +49 -0
- package/src/components/common/media/ImageReferencePicker.tsx +242 -0
- package/src/components/common/menubar/MenuBar.tsx +78 -0
- package/src/components/common/menubar/MenuBarTrigger.tsx +70 -0
- package/src/components/common/narrative/NarrativeMention.tsx +74 -0
- package/src/components/common/navigation/PathBreadcrumbs.tsx +78 -0
- package/src/components/common/navigation/UnderlineTabs.tsx +83 -0
- package/src/components/common/overlays/InstallAppPromptDialog.tsx +59 -0
- package/src/components/common/overlays/Modal.tsx +285 -0
- package/src/components/common/overlays/ModalChromeHeader.tsx +105 -0
- package/src/components/common/overlays/ObjectActionStrip.tsx +80 -0
- package/src/components/common/people/AutoUpdateReviewList.tsx +182 -0
- package/src/components/common/people/EntityList.tsx +122 -0
- package/src/components/common/people/EntityPortrait.tsx +64 -0
- package/src/components/common/people/LinkedDossierList.tsx +99 -0
- package/src/components/common/people/OpportunityList.tsx +94 -0
- package/src/components/common/people/PeopleSourceList.tsx +145 -0
- package/src/components/common/people/RelationshipDossier.tsx +168 -0
- package/src/components/common/people/RelationshipMap.tsx +362 -0
- package/src/components/common/people/index.ts +40 -0
- package/src/components/common/preview/FileAudioPreview.tsx +89 -0
- package/src/components/common/preview/FileImagePreview.tsx +88 -0
- package/src/components/common/preview/FilePdfPreview.tsx +87 -0
- package/src/components/common/preview/FileTextPreview.tsx +96 -0
- package/src/components/common/preview/FileVideoPreview.tsx +87 -0
- package/src/components/common/preview/PlatformFilePreview.tsx +166 -0
- package/src/components/common/preview/previewLayout.ts +27 -0
- package/src/components/common/render/Each.tsx +42 -0
- package/src/components/common/research/EvidenceDossier.tsx +894 -0
- package/src/components/common/research/index.ts +7 -0
- package/src/components/common/tiles/CatalogCard.tsx +116 -0
- package/src/components/common/tiles/Tile.tsx +45 -0
- package/src/components/common/tiles/TileGrid.tsx +13 -0
- package/src/components/common/typography/Heading.tsx +27 -0
- package/src/components/common/typography/Kicker.tsx +30 -0
- package/src/components/common/typography/Text.tsx +80 -0
- package/src/components/credentials/CredentialsPanel.tsx +169 -0
- package/src/components/credentials/credentialsTypes.ts +14 -0
- package/src/components/data/DataChart.tsx +913 -0
- package/src/components/data/dataTable.ts +99 -0
- package/src/components/editor/EditorCommentPromptDialog.tsx +124 -0
- package/src/components/editor/EditorLinkPromptDialog.tsx +65 -0
- package/src/components/print-design/PrintDesignPanel.test.tsx +1446 -0
- package/src/components/print-design/PrintDesignPanel.tsx +3043 -0
- package/src/components/print-design/index.ts +9 -0
- package/src/components/proofing/PagedProofDialog.tsx +453 -0
- package/src/components/proofing/index.ts +4 -0
- package/src/editor/DocumentEditor.tsx +28 -0
- package/src/editor/EditorViewModeToggle.tsx +35 -0
- package/src/editor/index.ts +97 -0
- package/src/editor/toolbarOverlays.ts +8 -0
- package/src/index.ts +209 -0
- package/src/theme/ThemeProvider.tsx +113 -0
- package/src/theme/appAccents.ts +87 -0
- package/src/theme/buildThemeBridgePayload.ts +23 -0
- package/src/theme/composePlatformTheme.test.ts +30 -0
- package/src/theme/composePlatformTheme.ts +23 -0
- package/src/theme/platformFont.d.mts +7 -0
- package/src/theme/platformFont.mjs +20 -0
- package/src/theme/platformFont.ts +6 -0
- package/src/theme/reportsShellChromeSelectors.ts +136 -0
- package/src/theme/styled.d.ts +5 -0
- package/src/theme/surfacePalettes.ts +178 -0
- package/src/theme/surfaceRoleSelectors.ts +113 -0
- package/src/theme/themeVars.ts +13 -0
- package/src/theme/themes/dark.ts +59 -0
- package/src/theme/themes/light.ts +60 -0
- package/src/theme/themes/palette.ts +44 -0
- package/src/theme/themes/shared.ts +68 -0
- package/src/theme/tokens.ts +114 -0
- package/src/theme/utils/GlobalStyles.ts +88 -0
- package/src/theme/utils/appFrameDocumentStyles.d.mts +5 -0
- package/src/theme/utils/appFrameDocumentStyles.mjs +89 -0
- package/src/theme/utils/appFrameDocumentStyles.ts +5 -0
- package/src/theme/utils/applyTheme.ts +50 -0
- package/src/theme/utils/cssVariables.ts +49 -0
- package/src/theme/utils/useInjectThemeIntoIframe.ts +40 -0
- package/src/utils/extensionIconMap.ts +123 -0
- package/src/utils/generalUtils.ts +157 -0
- package/src/writing/DraftAssistantDialog.tsx +279 -0
- package/src/writing/WritingStyleManager.tsx +416 -0
- package/src/writing/condenseWritingStyle.ts +103 -0
- package/src/writing/draftTextWithAgent.ts +134 -0
- package/src/writing/index.ts +39 -0
- package/src/writing/styleStore.ts +32 -0
- package/src/writing/writingStyle.test.ts +122 -0
- package/src/writing/writingStyle.ts +217 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import { bridge } from '../client.mjs'
|
|
3
|
+
import { PLATFORM_BRIDGE_EVENTS } from '../events.mjs'
|
|
4
|
+
import { PLATFORM_BRIDGE_METHODS } from '../methods.mjs'
|
|
5
|
+
import type { PlatformBridgeReadyMeta, ResourceOpenEvent } from '../types.js'
|
|
6
|
+
|
|
7
|
+
function isResourceOpenEvent(value: unknown): value is ResourceOpenEvent {
|
|
8
|
+
return (
|
|
9
|
+
typeof value === 'object' &&
|
|
10
|
+
value !== null &&
|
|
11
|
+
typeof (value as ResourceOpenEvent).path === 'string' &&
|
|
12
|
+
(value as ResourceOpenEvent).path.trim().length > 0
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UsePlatformViewportResourceResult {
|
|
17
|
+
/** Latest resource for this viewport (boot binding or warm `resource.open`). */
|
|
18
|
+
resource: ResourceOpenEvent | null
|
|
19
|
+
/** Clears `resource` after the app has applied it (avoids re-open loops). */
|
|
20
|
+
clearResource: () => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Per-tab resource binding from the shell workspace.
|
|
25
|
+
* Prefer `meta.viewport.resource` on bridge ready; also listens for `resource.open`.
|
|
26
|
+
*/
|
|
27
|
+
export function usePlatformViewportResource(
|
|
28
|
+
ready: boolean,
|
|
29
|
+
meta: PlatformBridgeReadyMeta | null,
|
|
30
|
+
): UsePlatformViewportResourceResult {
|
|
31
|
+
const [resource, setResource] = useState<ResourceOpenEvent | null>(null)
|
|
32
|
+
const bootTabIdRef = useRef<string | null>(null)
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
return bridge.onEvent(PLATFORM_BRIDGE_EVENTS.RESOURCE_OPEN, payload => {
|
|
36
|
+
if (!isResourceOpenEvent(payload)) return
|
|
37
|
+
setResource(payload)
|
|
38
|
+
})
|
|
39
|
+
}, [])
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!ready || !meta?.viewport) return
|
|
43
|
+
if (bootTabIdRef.current === meta.viewport.tabId) return
|
|
44
|
+
bootTabIdRef.current = meta.viewport.tabId
|
|
45
|
+
if (meta.viewport.resource) {
|
|
46
|
+
setResource(meta.viewport.resource)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let cancelled = false
|
|
51
|
+
bridge
|
|
52
|
+
.call<{ resource?: ResourceOpenEvent | null }>(
|
|
53
|
+
PLATFORM_BRIDGE_METHODS.CATALOG_CONSUME_PENDING_OPEN,
|
|
54
|
+
[],
|
|
55
|
+
)
|
|
56
|
+
.then(result => {
|
|
57
|
+
if (cancelled || !isResourceOpenEvent(result?.resource)) return
|
|
58
|
+
setResource(result.resource)
|
|
59
|
+
})
|
|
60
|
+
.catch(() => undefined)
|
|
61
|
+
|
|
62
|
+
return () => {
|
|
63
|
+
cancelled = true
|
|
64
|
+
}
|
|
65
|
+
}, [meta, ready])
|
|
66
|
+
|
|
67
|
+
const clearResource = useCallback(() => {
|
|
68
|
+
setResource(null)
|
|
69
|
+
}, [])
|
|
70
|
+
|
|
71
|
+
return { resource, clearResource }
|
|
72
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
import { bridge } from '../client.mjs'
|
|
3
|
+
import { PLATFORM_BRIDGE_EVENTS } from '../events.mjs'
|
|
4
|
+
import { PLATFORM_BRIDGE_METHODS } from '../methods.mjs'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Run `task` every `everyMinutes`, scheduled by the shell
|
|
8
|
+
* (`scheduler.register` + `schedule.fire` events) so all recurring work
|
|
9
|
+
* across the suite is declared in one place. Outside the shell
|
|
10
|
+
* (standalone dev) it falls back to a plain interval. `enabled: false`
|
|
11
|
+
* unregisters.
|
|
12
|
+
*/
|
|
13
|
+
export function useScheduledTask(
|
|
14
|
+
taskName: string,
|
|
15
|
+
everyMinutes: number,
|
|
16
|
+
task: () => void,
|
|
17
|
+
options?: { enabled?: boolean },
|
|
18
|
+
): void {
|
|
19
|
+
const taskRef = useRef(task)
|
|
20
|
+
taskRef.current = task
|
|
21
|
+
const enabled = options?.enabled ?? true
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!enabled) return undefined
|
|
25
|
+
|
|
26
|
+
const standalone = typeof window !== 'undefined' && window.parent === window
|
|
27
|
+
if (standalone) {
|
|
28
|
+
const timer = setInterval(
|
|
29
|
+
() => taskRef.current(),
|
|
30
|
+
Math.max(1, everyMinutes) * 60_000,
|
|
31
|
+
)
|
|
32
|
+
return () => clearInterval(timer)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const unsubscribe = bridge.onEvent(
|
|
36
|
+
PLATFORM_BRIDGE_EVENTS.SCHEDULE_FIRE,
|
|
37
|
+
payload => {
|
|
38
|
+
if ((payload as { taskName?: string } | null)?.taskName === taskName) {
|
|
39
|
+
taskRef.current()
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
void bridge
|
|
44
|
+
.waitForReady()
|
|
45
|
+
.then(() =>
|
|
46
|
+
bridge.call(PLATFORM_BRIDGE_METHODS.SCHEDULER_REGISTER, [
|
|
47
|
+
{ taskName, everyMinutes },
|
|
48
|
+
]),
|
|
49
|
+
)
|
|
50
|
+
.catch(() => undefined)
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
unsubscribe()
|
|
54
|
+
void bridge
|
|
55
|
+
.call(PLATFORM_BRIDGE_METHODS.SCHEDULER_UNREGISTER, [{ taskName }])
|
|
56
|
+
.catch(() => undefined)
|
|
57
|
+
}
|
|
58
|
+
}, [enabled, everyMinutes, taskName])
|
|
59
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PrintPageSize,
|
|
3
|
+
RenderPagedPreviewRequest,
|
|
4
|
+
RenderPagedPreviewResult,
|
|
5
|
+
RenderPagedSnapshotRequest,
|
|
6
|
+
RenderPagedSnapshotResult,
|
|
7
|
+
RenderPrintHtmlRequest,
|
|
8
|
+
} from './types.js'
|
|
9
|
+
|
|
10
|
+
export declare function renderPrintHtml(
|
|
11
|
+
request: RenderPrintHtmlRequest,
|
|
12
|
+
): Promise<string>
|
|
13
|
+
|
|
14
|
+
export declare function preparePagedPreview(
|
|
15
|
+
request: RenderPagedPreviewRequest,
|
|
16
|
+
): Promise<RenderPagedPreviewResult>
|
|
17
|
+
|
|
18
|
+
export declare function capturePagedSnapshot(
|
|
19
|
+
request: RenderPagedSnapshotRequest,
|
|
20
|
+
): Promise<RenderPagedSnapshotResult>
|
|
21
|
+
|
|
22
|
+
export declare function getDefaultPrintStylesheet(
|
|
23
|
+
pageSize?: PrintPageSize,
|
|
24
|
+
): Promise<string>
|
|
25
|
+
|
|
26
|
+
/** @returns unsubscribe */
|
|
27
|
+
export declare function onRenderProgress(
|
|
28
|
+
listener: (event: import('./types.js').RenderProgress) => void,
|
|
29
|
+
): () => void
|
|
30
|
+
|
|
31
|
+
export declare function renderCancel(): Promise<void>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { bridge } from './client.mjs'
|
|
2
|
+
import { PLATFORM_BRIDGE_EVENTS } from './events.mjs'
|
|
3
|
+
import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Print an app-prepared HTML file to PDF (shell: Paged.js + Electron printToPDF).
|
|
7
|
+
* Requires `render` permission in plugin.json.
|
|
8
|
+
*
|
|
9
|
+
* @param {import('./types.js').RenderPrintHtmlRequest} request
|
|
10
|
+
* @returns {Promise<string>} Absolute path to the output PDF
|
|
11
|
+
*/
|
|
12
|
+
export async function renderPrintHtml(request) {
|
|
13
|
+
return bridge.call(PLATFORM_BRIDGE_METHODS.RENDER_PRINT_HTML, [request])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Prepare an app print HTML file for preview (shell: Paged.js pagination).
|
|
18
|
+
* Requires `render` permission in plugin.json.
|
|
19
|
+
*
|
|
20
|
+
* @param {import('./types.js').RenderPagedPreviewRequest} request
|
|
21
|
+
* @returns {Promise<import('./types.js').RenderPagedPreviewResult>}
|
|
22
|
+
*/
|
|
23
|
+
export async function preparePagedPreview(request) {
|
|
24
|
+
return bridge.call(PLATFORM_BRIDGE_METHODS.RENDER_PREPARE_PAGED_PREVIEW, [
|
|
25
|
+
request,
|
|
26
|
+
])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Capture a paginated page image from an app print HTML file.
|
|
31
|
+
* Requires `render` permission.
|
|
32
|
+
*
|
|
33
|
+
* @param {import('./types.js').RenderPagedSnapshotRequest} request
|
|
34
|
+
* @returns {Promise<import('./types.js').RenderPagedSnapshotResult>}
|
|
35
|
+
*/
|
|
36
|
+
export async function capturePagedSnapshot(request) {
|
|
37
|
+
return bridge.call(PLATFORM_BRIDGE_METHODS.RENDER_CAPTURE_PAGED_SNAPSHOT, [
|
|
38
|
+
request,
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default Paged.js print CSS for a built-in page size (shell-owned).
|
|
44
|
+
* Requires `render` permission.
|
|
45
|
+
*
|
|
46
|
+
* @param {import('./types.js').PrintPageSize} [pageSize='A5']
|
|
47
|
+
* @returns {Promise<string>}
|
|
48
|
+
*/
|
|
49
|
+
export async function getDefaultPrintStylesheet(pageSize = 'A5') {
|
|
50
|
+
return bridge.call(PLATFORM_BRIDGE_METHODS.RENDER_GET_PRINT_STYLESHEET, [
|
|
51
|
+
pageSize,
|
|
52
|
+
])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @returns {() => void} unsubscribe */
|
|
56
|
+
export function onRenderProgress(listener) {
|
|
57
|
+
return bridge.onEvent(PLATFORM_BRIDGE_EVENTS.RENDER_PROGRESS, listener)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function renderCancel() {
|
|
61
|
+
return bridge.call(PLATFORM_BRIDGE_METHODS.RENDER_CANCEL, [])
|
|
62
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
defaultAnnotationMessage,
|
|
4
|
+
selectTextFromCandidates,
|
|
5
|
+
type TextCandidate,
|
|
6
|
+
} from './screenAnnotation.js'
|
|
7
|
+
|
|
8
|
+
const candidates: TextCandidate[] = [
|
|
9
|
+
{ text: 'First paragraph', rect: { x: 0, y: 0, width: 100, height: 20 } },
|
|
10
|
+
{ text: 'Second paragraph', rect: { x: 0, y: 30, width: 100, height: 20 } },
|
|
11
|
+
{ text: 'Far away', rect: { x: 0, y: 500, width: 100, height: 20 } },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
describe('selectTextFromCandidates', () => {
|
|
15
|
+
it('returns text of blocks overlapping the selection, in order', () => {
|
|
16
|
+
expect(
|
|
17
|
+
selectTextFromCandidates(candidates, {
|
|
18
|
+
x: 10,
|
|
19
|
+
y: 10,
|
|
20
|
+
width: 50,
|
|
21
|
+
height: 30,
|
|
22
|
+
}),
|
|
23
|
+
).toBe('First paragraph\n\nSecond paragraph')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('excludes blocks outside the selection', () => {
|
|
27
|
+
expect(
|
|
28
|
+
selectTextFromCandidates(candidates, {
|
|
29
|
+
x: 10,
|
|
30
|
+
y: 0,
|
|
31
|
+
width: 50,
|
|
32
|
+
height: 15,
|
|
33
|
+
}),
|
|
34
|
+
).toBe('First paragraph')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('dedupes and normalizes whitespace', () => {
|
|
38
|
+
expect(
|
|
39
|
+
selectTextFromCandidates(
|
|
40
|
+
[
|
|
41
|
+
{ text: ' Hello world ', rect: { x: 0, y: 0, width: 10, height: 10 } },
|
|
42
|
+
{ text: 'Hello world', rect: { x: 0, y: 0, width: 10, height: 10 } },
|
|
43
|
+
],
|
|
44
|
+
{ x: 0, y: 0, width: 10, height: 10 },
|
|
45
|
+
),
|
|
46
|
+
).toBe('Hello world')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('returns empty string when nothing overlaps', () => {
|
|
50
|
+
expect(
|
|
51
|
+
selectTextFromCandidates(candidates, {
|
|
52
|
+
x: 1000,
|
|
53
|
+
y: 1000,
|
|
54
|
+
width: 10,
|
|
55
|
+
height: 10,
|
|
56
|
+
}),
|
|
57
|
+
).toBe('')
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe('defaultAnnotationMessage', () => {
|
|
62
|
+
const baseCapture = {
|
|
63
|
+
imageDataUrl: 'data:image/png;base64,AAAA',
|
|
64
|
+
rect: { x: 0, y: 0, width: 10, height: 10 },
|
|
65
|
+
mode: 'content' as const,
|
|
66
|
+
note: 'tighten this',
|
|
67
|
+
hasMarks: false,
|
|
68
|
+
optionValues: {},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
it('includes the note and resolved text in content mode', () => {
|
|
72
|
+
const message = defaultAnnotationMessage({
|
|
73
|
+
capture: baseCapture,
|
|
74
|
+
resolvedText: 'The paragraph text',
|
|
75
|
+
})
|
|
76
|
+
expect(message).toContain('tighten this')
|
|
77
|
+
expect(message).toContain('The paragraph text')
|
|
78
|
+
expect(message).toContain('annotated screenshot')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('omits the resolved-text block in vibe mode', () => {
|
|
82
|
+
const message = defaultAnnotationMessage({
|
|
83
|
+
capture: { ...baseCapture, mode: 'ui', note: '' },
|
|
84
|
+
resolvedText: '',
|
|
85
|
+
})
|
|
86
|
+
expect(message).toContain('vibe mode')
|
|
87
|
+
expect(message).not.toContain('captured region')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('adds pen-focus guidance only when marks were drawn', () => {
|
|
91
|
+
const withoutMarks = defaultAnnotationMessage({
|
|
92
|
+
capture: baseCapture,
|
|
93
|
+
resolvedText: 'text',
|
|
94
|
+
})
|
|
95
|
+
expect(withoutMarks).not.toContain('focus on what they marked')
|
|
96
|
+
|
|
97
|
+
const withMarks = defaultAnnotationMessage({
|
|
98
|
+
capture: { ...baseCapture, hasMarks: true },
|
|
99
|
+
resolvedText: 'text',
|
|
100
|
+
})
|
|
101
|
+
expect(withMarks).toContain('focus on what they marked')
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callAgentRunModel,
|
|
3
|
+
createAgentSession,
|
|
4
|
+
resolveAgentRunToolCalls,
|
|
5
|
+
startAgentRun,
|
|
6
|
+
} from './agents.js'
|
|
7
|
+
import { startScreenCapture } from './window.mjs'
|
|
8
|
+
import type {
|
|
9
|
+
PlatformScreenCaptureOption,
|
|
10
|
+
PlatformScreenCaptureRect,
|
|
11
|
+
PlatformScreenCaptureResult,
|
|
12
|
+
} from './window.mjs'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Shared screen-annotation capture → agent flow. Any app can call
|
|
16
|
+
* {@link captureAndSendToAgent}: it opens the shell's chromeless capture
|
|
17
|
+
* overlay, resolves the text under the selection (content mode), and starts an
|
|
18
|
+
* agent run with the annotated image attached. App-specific wording and tools
|
|
19
|
+
* are supplied via `buildMessage`.
|
|
20
|
+
*
|
|
21
|
+
* Requires the `agents` permission in plugin.json.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const DEFAULT_MAX_STEPS = 8
|
|
25
|
+
|
|
26
|
+
export interface TextCandidate {
|
|
27
|
+
text: string
|
|
28
|
+
rect: PlatformScreenCaptureRect
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function rectsIntersect(
|
|
32
|
+
a: PlatformScreenCaptureRect,
|
|
33
|
+
b: PlatformScreenCaptureRect,
|
|
34
|
+
): boolean {
|
|
35
|
+
return (
|
|
36
|
+
a.x < b.x + b.width &&
|
|
37
|
+
a.x + a.width > b.x &&
|
|
38
|
+
a.y < b.y + b.height &&
|
|
39
|
+
a.y + a.height > b.y
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Pure core: pick the text of candidates whose rect overlaps the selection, in
|
|
45
|
+
* order, deduped + whitespace-normalized. Separated from the DOM so it is
|
|
46
|
+
* unit-testable (jsdom has no layout).
|
|
47
|
+
*/
|
|
48
|
+
export function selectTextFromCandidates(
|
|
49
|
+
candidates: TextCandidate[],
|
|
50
|
+
selection: PlatformScreenCaptureRect,
|
|
51
|
+
): string {
|
|
52
|
+
const seen = new Set<string>()
|
|
53
|
+
const parts: string[] = []
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
if (!rectsIntersect(candidate.rect, selection)) continue
|
|
56
|
+
const text = candidate.text.replace(/\s+/g, ' ').trim()
|
|
57
|
+
if (!text || seen.has(text)) continue
|
|
58
|
+
seen.add(text)
|
|
59
|
+
parts.push(text)
|
|
60
|
+
}
|
|
61
|
+
return parts.join('\n\n')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const DEFAULT_BLOCK_SELECTOR =
|
|
65
|
+
'p, li, h1, h2, h3, h4, h5, h6, blockquote, figcaption, td, th, pre, dt, dd'
|
|
66
|
+
|
|
67
|
+
export interface ResolveSelectionTextOptions {
|
|
68
|
+
/** Root to search within. Defaults to the active editor / contenteditable. */
|
|
69
|
+
root?: ParentNode | null
|
|
70
|
+
/** Block-level selector to collect. */
|
|
71
|
+
selector?: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the text that intersects an app-local selection rect (CSS px relative
|
|
76
|
+
* to the app's own document viewport). Reads block elements inside the editor
|
|
77
|
+
* surface; returns '' when nothing meaningful overlaps.
|
|
78
|
+
*/
|
|
79
|
+
export function resolveSelectionText(
|
|
80
|
+
selection: PlatformScreenCaptureRect,
|
|
81
|
+
options: ResolveSelectionTextOptions = {},
|
|
82
|
+
): string {
|
|
83
|
+
const root =
|
|
84
|
+
options.root ??
|
|
85
|
+
document.querySelector('.ProseMirror, [contenteditable="true"]')
|
|
86
|
+
if (!root) return ''
|
|
87
|
+
const selector = options.selector ?? DEFAULT_BLOCK_SELECTOR
|
|
88
|
+
const blocks = Array.from(root.querySelectorAll<HTMLElement>(selector))
|
|
89
|
+
const candidates: TextCandidate[] = blocks.map(element => {
|
|
90
|
+
const r = element.getBoundingClientRect()
|
|
91
|
+
return {
|
|
92
|
+
text: element.textContent ?? '',
|
|
93
|
+
rect: { x: r.left, y: r.top, width: r.width, height: r.height },
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
return selectTextFromCandidates(candidates, selection)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ScreenAnnotationResult {
|
|
100
|
+
capture: PlatformScreenCaptureResult
|
|
101
|
+
/** Text resolved under the selection (content mode); '' in vibe mode. */
|
|
102
|
+
resolvedText: string
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** In-progress + terminal states a caller can reflect with quiet UI. */
|
|
106
|
+
export type CaptureAgentStatus =
|
|
107
|
+
| 'working'
|
|
108
|
+
| 'applied'
|
|
109
|
+
| 'no-changes'
|
|
110
|
+
| 'needs-approval'
|
|
111
|
+
| 'error'
|
|
112
|
+
|
|
113
|
+
export interface CaptureAgentOutcome {
|
|
114
|
+
sessionId: string | null
|
|
115
|
+
status: 'applied' | 'no-changes' | 'needs-approval' | 'error' | 'cancelled'
|
|
116
|
+
error?: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface CaptureAndSendOptions {
|
|
120
|
+
appSlug: string
|
|
121
|
+
/** What this capture transforms, shown in the overlay (e.g. "book content", "page design"). */
|
|
122
|
+
label?: string
|
|
123
|
+
/** Contextual option controls rendered in the overlay (e.g. design scope). */
|
|
124
|
+
options?: PlatformScreenCaptureOption[]
|
|
125
|
+
/** Build the agent user message from the capture + resolved text. */
|
|
126
|
+
buildMessage?: (result: ScreenAnnotationResult) => string
|
|
127
|
+
/** Session title (string or builder). */
|
|
128
|
+
title?: string | ((result: ScreenAnnotationResult) => string)
|
|
129
|
+
/** Resolve text under the selection in content mode. Defaults to {@link resolveSelectionText}. */
|
|
130
|
+
resolveText?: (rect: PlatformScreenCaptureRect) => string
|
|
131
|
+
/** Total model + tool-resolution turns to drive. Default 8. */
|
|
132
|
+
maxSteps?: number
|
|
133
|
+
/** Progress callback for quiet UI: 'working' first, then a terminal status. */
|
|
134
|
+
onStatus?: (status: CaptureAgentStatus) => void
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Guidance for interpreting pen marks. The marks (circles, underlines, arrows)
|
|
139
|
+
* indicate the focus; their scope is a judgment call the agent must make.
|
|
140
|
+
*/
|
|
141
|
+
export function penFocusGuidance(): string {
|
|
142
|
+
return [
|
|
143
|
+
'The user drew marks on the screenshot — focus on what they marked, not the whole captured region.',
|
|
144
|
+
'Marks may circle, underline, or point at content. Use judgment about scope: the user may mean exactly the marked words, the sentence that contains them, or the whole paragraph/section.',
|
|
145
|
+
'Infer the most sensible scope from the marks and the note together. If it is genuinely ambiguous, prefer the smallest coherent unit (usually the sentence) unless the note implies a larger change.',
|
|
146
|
+
].join('\n')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Generic fallback message when an app doesn't supply `buildMessage`. */
|
|
150
|
+
export function defaultAnnotationMessage({
|
|
151
|
+
capture,
|
|
152
|
+
resolvedText,
|
|
153
|
+
}: ScreenAnnotationResult): string {
|
|
154
|
+
const note = capture.note.trim()
|
|
155
|
+
return [
|
|
156
|
+
capture.mode === 'content'
|
|
157
|
+
? 'The user captured and annotated a region of this app and wants a change applied there.'
|
|
158
|
+
: 'The user captured and annotated a region of the app UI (vibe mode).',
|
|
159
|
+
'',
|
|
160
|
+
note ? `Note from the user:\n${note}` : 'No extra note.',
|
|
161
|
+
capture.hasMarks ? `\n${penFocusGuidance()}` : '',
|
|
162
|
+
resolvedText
|
|
163
|
+
? `\nText in the captured region (for reference — the marks show the focus):\n"""\n${resolvedText}\n"""`
|
|
164
|
+
: '',
|
|
165
|
+
'',
|
|
166
|
+
'Use the attached annotated screenshot (with the user’s drawing) to make the change.',
|
|
167
|
+
]
|
|
168
|
+
.filter(line => line !== '')
|
|
169
|
+
.join('\n')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface DataUrlImage {
|
|
173
|
+
mediaType: string
|
|
174
|
+
dataBase64: string
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function splitDataUrl(dataUrl: string): DataUrlImage | null {
|
|
178
|
+
const match = /^data:([^;]+);base64,(.+)$/s.exec(dataUrl)
|
|
179
|
+
if (!match) return null
|
|
180
|
+
return { mediaType: match[1], dataBase64: match[2] }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Open the capture overlay and, on send, run an agent end to end: call the
|
|
185
|
+
* model, then route + resolve its tool calls so the edit is actually applied
|
|
186
|
+
* (not left pending). Content mode targets the app's own agent (scope 'app');
|
|
187
|
+
* vibe mode targets a global agent. `onStatus` reports progress so the caller
|
|
188
|
+
* can show quiet feedback. Returns an outcome describing what happened.
|
|
189
|
+
*/
|
|
190
|
+
export async function captureAndSendToAgent(
|
|
191
|
+
options: CaptureAndSendOptions,
|
|
192
|
+
): Promise<CaptureAgentOutcome> {
|
|
193
|
+
const capture = await startScreenCapture({
|
|
194
|
+
label: options.label,
|
|
195
|
+
options: options.options,
|
|
196
|
+
})
|
|
197
|
+
if (!capture) return { sessionId: null, status: 'cancelled' }
|
|
198
|
+
|
|
199
|
+
const resolveText = options.resolveText ?? resolveSelectionText
|
|
200
|
+
const resolvedText =
|
|
201
|
+
capture.mode === 'content' ? resolveText(capture.rect) : ''
|
|
202
|
+
const result: ScreenAnnotationResult = { capture, resolvedText }
|
|
203
|
+
|
|
204
|
+
const buildMessage = options.buildMessage ?? defaultAnnotationMessage
|
|
205
|
+
const message = buildMessage(result)
|
|
206
|
+
const image = splitDataUrl(capture.imageDataUrl)
|
|
207
|
+
const title =
|
|
208
|
+
typeof options.title === 'function'
|
|
209
|
+
? options.title(result)
|
|
210
|
+
: options.title ?? 'Screen annotation'
|
|
211
|
+
|
|
212
|
+
const scope = capture.mode === 'content' ? ('app' as const) : ('global' as const)
|
|
213
|
+
|
|
214
|
+
options.onStatus?.('working')
|
|
215
|
+
try {
|
|
216
|
+
const record = await createAgentSession({
|
|
217
|
+
scope,
|
|
218
|
+
...(scope === 'app' ? { appSlug: options.appSlug } : {}),
|
|
219
|
+
title,
|
|
220
|
+
})
|
|
221
|
+
const session = {
|
|
222
|
+
scope,
|
|
223
|
+
...(scope === 'app' ? { appSlug: options.appSlug } : {}),
|
|
224
|
+
sessionId: record.sessionId,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let snapshot = await startAgentRun({
|
|
228
|
+
session,
|
|
229
|
+
message,
|
|
230
|
+
...(image ? { images: [image] } : {}),
|
|
231
|
+
})
|
|
232
|
+
const runId = snapshot.runId
|
|
233
|
+
const maxSteps = options.maxSteps ?? DEFAULT_MAX_STEPS
|
|
234
|
+
let appliedTools = false
|
|
235
|
+
|
|
236
|
+
for (let step = 0; step < maxSteps; step += 1) {
|
|
237
|
+
if (snapshot.state === 'ready') {
|
|
238
|
+
snapshot = await callAgentRunModel({ runId, sessionId: record.sessionId })
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
if (snapshot.state === 'pending_tools') {
|
|
242
|
+
const resolved = await resolveAgentRunToolCalls({ runId, session })
|
|
243
|
+
if (resolved.state === 'pending_tools') {
|
|
244
|
+
// A tool still needs explicit approval — hand off to the drawer.
|
|
245
|
+
options.onStatus?.('needs-approval')
|
|
246
|
+
return { sessionId: record.sessionId, status: 'needs-approval' }
|
|
247
|
+
}
|
|
248
|
+
appliedTools = true
|
|
249
|
+
snapshot = resolved
|
|
250
|
+
continue
|
|
251
|
+
}
|
|
252
|
+
break
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (snapshot.state === 'failed') {
|
|
256
|
+
const error = snapshot.error ?? 'The agent run failed.'
|
|
257
|
+
options.onStatus?.('error')
|
|
258
|
+
return { sessionId: record.sessionId, status: 'error', error }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const status = appliedTools ? 'applied' : 'no-changes'
|
|
262
|
+
options.onStatus?.(status)
|
|
263
|
+
return { sessionId: record.sessionId, status }
|
|
264
|
+
} catch (cause) {
|
|
265
|
+
const error = cause instanceof Error ? cause.message : String(cause)
|
|
266
|
+
options.onStatus?.('error')
|
|
267
|
+
return { sessionId: null, status: 'error', error }
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { bridge } from './client.mjs'
|
|
2
|
+
import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
|
|
3
|
+
|
|
4
|
+
/** Ask the shell to store a surface palette for the calling app (whole desktop theme). */
|
|
5
|
+
export async function setSurfacePalette(paletteId) {
|
|
6
|
+
return bridge.call(PLATFORM_BRIDGE_METHODS.THEME_SET_SURFACE_PALETTE, [
|
|
7
|
+
{ paletteId },
|
|
8
|
+
])
|
|
9
|
+
}
|