@puredesktop/puredesktop-ui-bridge 2.1.3 → 2.1.6

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.
Files changed (183) hide show
  1. package/package.json +1132 -45
  2. package/src/agents/runtime/mapAgentSnapshot.test.ts +1 -3
  3. package/src/agents/runtime/mapAgentSnapshot.ts +8 -4
  4. package/src/agents/runtime/mergeUsageTurn.ts +1 -2
  5. package/src/agents/runtime/pendingToolCalls.test.ts +1 -3
  6. package/src/agents/runtime/unresolvedToolCalls.ts +1 -2
  7. package/src/bridge/agentModelTiers.ts +9 -4
  8. package/src/bridge/agents.mjs +3 -1
  9. package/src/bridge/calendarDraftIntent.ts +12 -4
  10. package/src/bridge/calendarInviteIntent.ts +18 -5
  11. package/src/bridge/client.mjs +45 -45
  12. package/src/bridge/dialog.d.mts +8 -8
  13. package/src/bridge/dialog.mjs +40 -40
  14. package/src/bridge/dialog.test.ts +73 -73
  15. package/src/bridge/fs.d.mts +41 -41
  16. package/src/bridge/fs.mjs +56 -56
  17. package/src/bridge/fs.test.ts +83 -83
  18. package/src/bridge/fs.ts +28 -28
  19. package/src/bridge/googleAuth.ts +3 -1
  20. package/src/bridge/methods.d.mts +6 -0
  21. package/src/bridge/methods.mjs +7 -0
  22. package/src/bridge/network.d.mts +17 -17
  23. package/src/bridge/network.mjs +22 -22
  24. package/src/bridge/pureRender/base.css +324 -76
  25. package/src/bridge/pureRender/compiler.ts +1 -1
  26. package/src/bridge/pureRender/document.test.ts +2 -2
  27. package/src/bridge/pureRender/extraction.test.ts +24 -12
  28. package/src/bridge/pureRender/extraction.ts +10 -7
  29. package/src/bridge/pureRender/flow.css +17 -3
  30. package/src/bridge/pureRender/normalize.ts +28 -26
  31. package/src/bridge/pureRender/presets.ts +7 -7
  32. package/src/bridge/pureRender/profiles/book.css +122 -19
  33. package/src/bridge/pureRender/profiles/manuscript.css +57 -11
  34. package/src/bridge/pureRender/profiles/writer.css +48 -10
  35. package/src/bridge/pureRender/reset.css +20 -5
  36. package/src/bridge/pureRender/theme.ts +37 -9
  37. package/src/bridge/pureRender/tokens/default.css +45 -33
  38. package/src/bridge/react/usePlatformAgentRunLiveState.tsx +4 -1
  39. package/src/bridge/react/usePlatformAgentSessionDrive.tsx +13 -4
  40. package/src/bridge/screenAnnotation.test.ts +4 -1
  41. package/src/bridge/screenAnnotation.ts +6 -2
  42. package/src/bridge/storage.d.mts +27 -27
  43. package/src/bridge/storage.mjs +18 -18
  44. package/src/bridge/storage.test.ts +49 -49
  45. package/src/bridge/types.ts +62 -58
  46. package/src/bridge/window.d.mts +3 -1
  47. package/src/bridge/workspace.d.mts +7 -0
  48. package/src/bridge/workspace.mjs +8 -0
  49. package/src/bridge/workspace.ts +1 -0
  50. package/src/commitments/commitments.test.ts +10 -3
  51. package/src/commitments/commitments.ts +18 -4
  52. package/src/components/accessibility/AccessibilityReviewDialog.tsx +7 -2
  53. package/src/components/accessibility/accessibilityReview.test.ts +11 -4
  54. package/src/components/accessibility/accessibilityReview.ts +18 -10
  55. package/src/components/agent-feedback/ScreenAnnotationAgentDialog.tsx +9 -2
  56. package/src/components/agents/AgentContextUsageBar.tsx +5 -3
  57. package/src/components/agents/AgentDrawerPanel.test.tsx +15 -3
  58. package/src/components/agents/AgentMessageBubble.tsx +1 -3
  59. package/src/components/agents/AgentQuestionPromptList.tsx +3 -3
  60. package/src/components/agents/AgentToolApprovalActions.tsx +1 -3
  61. package/src/components/agents/agentPanelStyles.ts +1 -1
  62. package/src/components/agents/agentToolDisplay.ts +4 -1
  63. package/src/components/assets/asset-library/AssetLibraryDialog.tsx +2 -0
  64. package/src/components/assets/asset-library/AssetLibraryWorkspace.tsx +2 -0
  65. package/src/components/assets/asset-library/detail/AssetLibraryDetailHeader.test.tsx +40 -0
  66. package/src/components/assets/asset-library/detail/AssetLibraryDetailHeader.tsx +7 -3
  67. package/src/components/assets/asset-library/detail/AssetLibraryDetailPreview.tsx +8 -8
  68. package/src/components/assets/asset-library/hooks/useAssetLibraryDialog.ts +13 -2
  69. package/src/components/assets/asset-library/hooks/useAssetLibraryMutations.ts +18 -1
  70. package/src/components/assets/asset-library/sidebar/AssetLibraryListRow.tsx +9 -6
  71. package/src/components/assets/asset-library/sidebar/AssetLibrarySidebarToolbar.tsx +3 -1
  72. package/src/components/assets/asset-library/sidebar/AssetListThumbnail.tsx +1 -1
  73. package/src/components/assets/asset-library/types.ts +6 -0
  74. package/src/components/chrome/OverlayActionCapsule.tsx +2 -2
  75. package/src/components/chrome/PanelTabStrip.tsx +3 -2
  76. package/src/components/chrome/PureAppWordmark.tsx +15 -15
  77. package/src/components/chrome/WorkspaceTabStrip.tsx +19 -14
  78. package/src/components/chrome/documentChromePolicy.test.ts +34 -0
  79. package/src/components/chrome/documentChromePolicy.ts +15 -0
  80. package/src/components/chrome/index.ts +4 -0
  81. package/src/components/chrome/workspaceTabTypes.ts +2 -0
  82. package/src/components/common/buttons/Button.tsx +60 -10
  83. package/src/components/common/buttons/ChoiceChip.tsx +2 -2
  84. package/src/components/common/buttons/IconButton.tsx +26 -0
  85. package/src/components/common/buttons/SegmentedControl.tsx +23 -7
  86. package/src/components/common/canvas/CanvasElementFrame.tsx +37 -28
  87. package/src/components/common/canvas/canvasGeometry.test.ts +4 -5
  88. package/src/components/common/canvas/canvasGeometry.ts +1 -4
  89. package/src/components/common/canvas/useCanvasDragResize.test.tsx +10 -10
  90. package/src/components/common/canvas/useCanvasDragResize.ts +1 -4
  91. package/src/components/common/cards/OpenReviewCard.tsx +6 -6
  92. package/src/components/common/chat/ChatBox.test.tsx +9 -1
  93. package/src/components/common/chat/ChatBox.tsx +3 -4
  94. package/src/components/common/chat/ChatMarkdown.tsx +1 -1
  95. package/src/components/common/chat/ChatModelMenu.tsx +1 -1
  96. package/src/components/common/chat/ChatThinkingIndicator.tsx +1 -1
  97. package/src/components/common/chat/ChatThread.tsx +2 -1
  98. package/src/components/common/chat/ChatToolRow.tsx +2 -2
  99. package/src/components/common/chat/ChatUserBubble.tsx +1 -1
  100. package/src/components/common/connections/ConnectionSetup.tsx +6 -6
  101. package/src/components/common/containers/AppFrame.test.tsx +37 -36
  102. package/src/components/common/containers/AppFrame.tsx +15 -0
  103. package/src/components/common/containers/AppHeader.tsx +6 -3
  104. package/src/components/common/containers/SurfacePanel.tsx +1 -0
  105. package/src/components/common/containers/useOpacityPresence.ts +10 -15
  106. package/src/components/common/context/ContextSelector.tsx +10 -10
  107. package/src/components/common/desk/CreationDesk.tsx +23 -11
  108. package/src/components/common/desk/DailyBrief.tsx +18 -7
  109. package/src/components/common/desk/DeskCard.tsx +11 -6
  110. package/src/components/common/dropdown/MenuButtonDropdown.tsx +58 -6
  111. package/src/components/common/dropdown/MenuDropdownItem.tsx +23 -3
  112. package/src/components/common/dropdown/MenuPanel.tsx +8 -8
  113. package/src/components/common/dropdown/SelectMenu.tsx +8 -3
  114. package/src/components/common/dropdown/menuTypes.ts +2 -1
  115. package/src/components/common/evidence/StructuredEvidenceView.tsx +9 -5
  116. package/src/components/common/feedback/Badge.tsx +19 -5
  117. package/src/components/common/feedback/CapabilityPills.tsx +1 -1
  118. package/src/components/common/feedback/InfoHint.tsx +2 -2
  119. package/src/components/common/feedback/PlatformChip.tsx +9 -9
  120. package/src/components/common/feedback/QuietPill.tsx +5 -8
  121. package/src/components/common/feedback/WorkProgressPanel.tsx +14 -14
  122. package/src/components/common/files/FileTable.tsx +8 -2
  123. package/src/components/common/inputs/Checkbox.tsx +1 -1
  124. package/src/components/common/inputs/EditableSecretField.tsx +1 -3
  125. package/src/components/common/inputs/InlineComposer.tsx +2 -2
  126. package/src/components/common/inputs/PaletteDropdown.tsx +33 -23
  127. package/src/components/common/inputs/SurfacePalettePicker.tsx +1 -1
  128. package/src/components/common/inputs/ToggleSwitch.tsx +2 -2
  129. package/src/components/common/inputs/WorkspacePicker.tsx +3 -3
  130. package/src/components/common/lists/listRowStyles.ts +4 -2
  131. package/src/components/common/lists/quiet-ledger/index.ts +1 -4
  132. package/src/components/common/lists/reorderable/DragHandle.tsx +1 -1
  133. package/src/components/common/lists/reorderable/ReorderableSections.tsx +2 -1
  134. package/src/components/common/media/ImageReferencePicker.tsx +22 -14
  135. package/src/components/common/narrative/NarrativeMention.tsx +15 -9
  136. package/src/components/common/navigation/NavigationCardRow.tsx +2 -2
  137. package/src/components/common/navigation/UnderlineTabs.tsx +1 -0
  138. package/src/components/common/overlays/Modal.tsx +52 -3
  139. package/src/components/common/overlays/ObjectActionStrip.tsx +2 -2
  140. package/src/components/common/people/AutoUpdateReviewList.tsx +2 -2
  141. package/src/components/common/people/EntityList.tsx +7 -3
  142. package/src/components/common/people/EntityPortrait.tsx +6 -1
  143. package/src/components/common/people/LinkedDossierList.tsx +3 -1
  144. package/src/components/common/people/RelationshipMap.tsx +3 -9
  145. package/src/components/common/research/EvidenceDossier.tsx +24 -20
  146. package/src/components/common/tiles/CatalogCard.tsx +1 -1
  147. package/src/components/credentials/CredentialsPanel.tsx +2 -6
  148. package/src/components/data/DataChart.tsx +119 -94
  149. package/src/components/data/dataTable.ts +4 -1
  150. package/src/components/editor/DocumentAppChrome.tsx +172 -0
  151. package/src/components/editor/EditorCommentPromptDialog.tsx +1 -1
  152. package/src/components/editor/EditorLinkPromptDialog.tsx +1 -1
  153. package/src/components/print-design/PrintDesignPanel.test.tsx +8 -6
  154. package/src/components/print-design/PrintDesignPanel.tsx +18 -25
  155. package/src/components/proofing/PagedProofDialog.tsx +17 -4
  156. package/src/editor/EditorViewModeToggle.tsx +6 -2
  157. package/src/editor/index.ts +6 -0
  158. package/src/theme/ThemeProvider.tsx +5 -2
  159. package/src/theme/appAccents.test.ts +51 -0
  160. package/src/theme/appAccents.ts +151 -88
  161. package/src/theme/appIdentityCss.mjs +256 -0
  162. package/src/theme/appIdentityCss.ts +256 -0
  163. package/src/theme/composePlatformTheme.test.ts +43 -14
  164. package/src/theme/platformFont.d.mts +2 -2
  165. package/src/theme/platformFont.mjs +4 -3
  166. package/src/theme/reportsShellChromeSelectors.ts +14 -132
  167. package/src/theme/surfaceCss.mjs +37 -0
  168. package/src/theme/surfaceCss.ts +37 -0
  169. package/src/theme/surfacePalettes.ts +120 -79
  170. package/src/theme/surfaceRoleSelectors.ts +135 -71
  171. package/src/theme/themes/dark.ts +44 -43
  172. package/src/theme/themes/light.ts +40 -38
  173. package/src/theme/themes/palette.ts +32 -30
  174. package/src/theme/themes/shared.ts +17 -17
  175. package/src/theme/utils/GlobalStyles.ts +10 -6
  176. package/src/theme/utils/appFrameDocumentStyles.mjs +6 -1
  177. package/src/theme/utils/cssVariables.ts +169 -0
  178. package/src/writing/DraftAssistantDialog.tsx +85 -85
  179. package/src/writing/WritingStyleManager.tsx +6 -3
  180. package/src/writing/draftTextWithAgent.ts +47 -50
  181. package/src/writing/styleStore.ts +4 -3
  182. package/src/writing/writingStyle.test.ts +8 -2
  183. package/src/writing/writingStyle.ts +19 -8
@@ -91,8 +91,6 @@ describe('mapApprovalPendingToolCalls', () => {
91
91
  }
92
92
  expect(
93
93
  mapApprovalPendingToolCalls(snapshot, policy, ['call-accepted']),
94
- ).toEqual([
95
- { id: 'call-manual', name: 'book.review', arguments: {} },
96
- ])
94
+ ).toEqual([{ id: 'call-manual', name: 'book.review', arguments: {} }])
97
95
  })
98
96
  })
@@ -12,8 +12,10 @@ import { toolRequiresApproval } from './toolRequiresApproval.js'
12
12
 
13
13
  const VISIBLE_ROLES = new Set(['user', 'assistant', 'tool'])
14
14
 
15
- export function mapRunState(state: string): typeof AgentUiRunState[keyof typeof AgentUiRunState] {
16
- return state as typeof AgentUiRunState[keyof typeof AgentUiRunState]
15
+ export function mapRunState(
16
+ state: string,
17
+ ): (typeof AgentUiRunState)[keyof typeof AgentUiRunState] {
18
+ return state as (typeof AgentUiRunState)[keyof typeof AgentUiRunState]
17
19
  }
18
20
 
19
21
  export function mapSnapshotToMessages(
@@ -69,7 +71,9 @@ export function mapPendingToolCalls(
69
71
  snapshot: PlatformAgentRunSnapshot | null,
70
72
  ): AgentUiToolCall[] {
71
73
  if (!snapshot) return []
72
- return derivePendingToolCalls(snapshot.messages, snapshot.state).map(mapToolCall)
74
+ return derivePendingToolCalls(snapshot.messages, snapshot.state).map(
75
+ mapToolCall,
76
+ )
73
77
  }
74
78
 
75
79
  /** Pending tools that still need explicit user approval. */
@@ -111,7 +115,7 @@ function mapToolCallWithResult(
111
115
  }
112
116
 
113
117
  export function isComposerEnabled(
114
- runState: typeof AgentUiRunState[keyof typeof AgentUiRunState] | null,
118
+ runState: (typeof AgentUiRunState)[keyof typeof AgentUiRunState] | null,
115
119
  busy: boolean,
116
120
  ): boolean {
117
121
  if (busy) return false
@@ -6,8 +6,7 @@ export function mergeUsageTurn(
6
6
  delta: AgentUsageTurnDelta,
7
7
  estimated = false,
8
8
  ): PlatformAgentRunUsage {
9
- const turnTotal =
10
- delta.totalTokens ?? delta.inputTokens + delta.outputTokens
9
+ const turnTotal = delta.totalTokens ?? delta.inputTokens + delta.outputTokens
11
10
  const next: PlatformAgentRunUsage = {
12
11
  inputTokens: (base?.inputTokens ?? 0) + delta.inputTokens,
13
12
  outputTokens: (base?.outputTokens ?? 0) + delta.outputTokens,
@@ -31,9 +31,7 @@ describe('derivePendingToolCalls', () => {
31
31
  {
32
32
  role: 'assistant',
33
33
  content: '',
34
- toolCalls: [
35
- { id: 'tc-1', name: 'book:read', arguments: {} },
36
- ],
34
+ toolCalls: [{ id: 'tc-1', name: 'book:read', arguments: {} }],
37
35
  },
38
36
  ],
39
37
  AgentUiRunState.AwaitingUser,
@@ -26,8 +26,7 @@ export function unresolvedToolCalls(
26
26
  messages
27
27
  .filter(
28
28
  message =>
29
- message.role === 'tool' &&
30
- typeof message.toolCallId === 'string',
29
+ message.role === 'tool' && typeof message.toolCallId === 'string',
31
30
  )
32
31
  .map(message => message.toolCallId as string),
33
32
  )
@@ -8,7 +8,9 @@ export const DEFAULT_AGENT_LOW_LEVEL_MODEL_ID = 'deepseek:deepseek-v4-flash'
8
8
  export const AGENT_AUTO_MODEL_ID = 'auto'
9
9
  export const AGENT_AUTO_MODEL_LABEL = 'Auto'
10
10
 
11
- export function isAgentAutoModelId(modelId: string | undefined | null): boolean {
11
+ export function isAgentAutoModelId(
12
+ modelId: string | undefined | null,
13
+ ): boolean {
12
14
  return modelId === AGENT_AUTO_MODEL_ID
13
15
  }
14
16
 
@@ -36,9 +38,12 @@ export interface PlatformAgentSessionScopeDefaults {
36
38
 
37
39
  /** Resolve tier ids from shell preferences with catalog fallbacks. */
38
40
  export function resolveAgentModelTiersFromPrefs(
39
- prefs: {
40
- agentModelTiers?: Partial<PlatformAgentModelTiers>
41
- } | null | undefined,
41
+ prefs:
42
+ | {
43
+ agentModelTiers?: Partial<PlatformAgentModelTiers>
44
+ }
45
+ | null
46
+ | undefined,
42
47
  ): PlatformAgentModelTiers {
43
48
  const stored = prefs?.agentModelTiers
44
49
  return {
@@ -129,7 +129,9 @@ export async function advanceAgentSessionPending(request) {
129
129
  * @returns {Promise<import('./agentTypes.js').PlatformAgentSessionDriveResult>}
130
130
  */
131
131
  export async function interruptAgentSession(request) {
132
- return bridge.call(PLATFORM_BRIDGE_METHODS.AGENTS_SESSION_INTERRUPT, [request])
132
+ return bridge.call(PLATFORM_BRIDGE_METHODS.AGENTS_SESSION_INTERRUPT, [
133
+ request,
134
+ ])
133
135
  }
134
136
 
135
137
  /**
@@ -39,17 +39,25 @@ export interface CalendarDraftIntentStore {
39
39
  intents: Record<string, CalendarDraftIntent>
40
40
  }
41
41
 
42
- export function calendarDraftResourceId(accountId: string, threadId: string): string {
43
- return `${CALENDAR_DRAFT_INTENT_PREFIX}${encodeURIComponent(accountId)}:${encodeURIComponent(threadId)}`
42
+ export function calendarDraftResourceId(
43
+ accountId: string,
44
+ threadId: string,
45
+ ): string {
46
+ return `${CALENDAR_DRAFT_INTENT_PREFIX}${encodeURIComponent(
47
+ accountId,
48
+ )}:${encodeURIComponent(threadId)}`
44
49
  }
45
50
 
46
51
  export function isCalendarDraftResourceId(resourceId: string): boolean {
47
52
  return resourceId.startsWith(CALENDAR_DRAFT_INTENT_PREFIX)
48
53
  }
49
54
 
50
- export function normalizeCalendarDraftIntentStore(value: unknown): CalendarDraftIntentStore {
55
+ export function normalizeCalendarDraftIntentStore(
56
+ value: unknown,
57
+ ): CalendarDraftIntentStore {
51
58
  if (!value || typeof value !== 'object') return { intents: {} }
52
59
  const intents = (value as Partial<CalendarDraftIntentStore>).intents
53
- if (!intents || typeof intents !== 'object' || Array.isArray(intents)) return { intents: {} }
60
+ if (!intents || typeof intents !== 'object' || Array.isArray(intents))
61
+ return { intents: {} }
54
62
  return { intents: intents as Record<string, CalendarDraftIntent> }
55
63
  }
@@ -1,9 +1,14 @@
1
1
  export const CALENDAR_INVITE_INTENT_STORAGE_SLUG = 'calendar'
2
- export const CALENDAR_INVITE_INTENT_STORAGE_FILE = 'calendar-invite-intents.json'
2
+ export const CALENDAR_INVITE_INTENT_STORAGE_FILE =
3
+ 'calendar-invite-intents.json'
3
4
  export const CALENDAR_INVITE_INTENT_PREFIX = 'calendar-invite:mail:'
4
5
 
5
6
  export type CalendarInviteMethod = 'REQUEST' | 'REPLY' | 'CANCEL' | 'PUBLISH'
6
- export type CalendarInviteResponse = 'accepted' | 'declined' | 'tentative' | 'needsAction'
7
+ export type CalendarInviteResponse =
8
+ | 'accepted'
9
+ | 'declined'
10
+ | 'tentative'
11
+ | 'needsAction'
7
12
 
8
13
  export interface CalendarInviteContact {
9
14
  name: string
@@ -48,15 +53,23 @@ export interface CalendarInviteIntentStore {
48
53
  intents: Record<string, CalendarInviteIntent>
49
54
  }
50
55
 
51
- export function calendarInviteResourceId(accountId: string, messageId: string, uid: string): string {
52
- return `${CALENDAR_INVITE_INTENT_PREFIX}${encodeURIComponent(accountId)}:${encodeURIComponent(messageId)}:${encodeURIComponent(uid)}`
56
+ export function calendarInviteResourceId(
57
+ accountId: string,
58
+ messageId: string,
59
+ uid: string,
60
+ ): string {
61
+ return `${CALENDAR_INVITE_INTENT_PREFIX}${encodeURIComponent(
62
+ accountId,
63
+ )}:${encodeURIComponent(messageId)}:${encodeURIComponent(uid)}`
53
64
  }
54
65
 
55
66
  export function isCalendarInviteResourceId(resourceId: string): boolean {
56
67
  return resourceId.startsWith(CALENDAR_INVITE_INTENT_PREFIX)
57
68
  }
58
69
 
59
- export function normalizeCalendarInviteIntentStore(value: unknown): CalendarInviteIntentStore {
70
+ export function normalizeCalendarInviteIntentStore(
71
+ value: unknown,
72
+ ): CalendarInviteIntentStore {
60
73
  if (!value || typeof value !== 'object') return { intents: {} }
61
74
  const intents = (value as Partial<CalendarInviteIntentStore>).intents
62
75
  if (!intents || typeof intents !== 'object') return { intents: {} }
@@ -21,16 +21,16 @@ const HANDSHAKE_MS = 800
21
21
  let readyPromise = null
22
22
  let bridgeMeta = null
23
23
  let eventListenerAttached = false
24
- const eventListeners = new Map()
25
-
26
- function postHandshake() {
27
- window.parent.postMessage({ channel: CHANNEL, type: 'handshake' }, '*')
28
- }
29
-
30
- function ensureEventListener() {
31
- if (eventListenerAttached) return
32
- eventListenerAttached = true
33
- window.addEventListener('message', event => {
24
+ const eventListeners = new Map()
25
+
26
+ function postHandshake() {
27
+ window.parent.postMessage({ channel: CHANNEL, type: 'handshake' }, '*')
28
+ }
29
+
30
+ function ensureEventListener() {
31
+ if (eventListenerAttached) return
32
+ eventListenerAttached = true
33
+ window.addEventListener('message', event => {
34
34
  const data = event.data
35
35
  if (!data || data.channel !== CHANNEL || data.type !== 'event') return
36
36
  const handlers = eventListeners.get(data.name)
@@ -58,24 +58,24 @@ function offEvent(name, handler) {
58
58
  eventListeners.get(name)?.delete(handler)
59
59
  }
60
60
 
61
- function waitForReady() {
62
- if (readyPromise) return readyPromise
63
- readyPromise = new Promise((resolve, reject) => {
64
- const timeout = window.setTimeout(() => {
65
- window.clearInterval(handshakeTimer)
66
- reject(
67
- new Error(
61
+ function waitForReady() {
62
+ if (readyPromise) return readyPromise
63
+ readyPromise = new Promise((resolve, reject) => {
64
+ const timeout = window.setTimeout(() => {
65
+ window.clearInterval(handshakeTimer)
66
+ reject(
67
+ new Error(
68
68
  'PureScience plugin bridge ready timeout (is this page inside the desktop iframe?)',
69
69
  ),
70
70
  )
71
71
  }, 15_000)
72
72
 
73
- const onMessage = event => {
74
- const data = event.data
75
- if (!data || data.channel !== CHANNEL || data.type !== 'ready') return
76
- window.clearTimeout(timeout)
77
- window.clearInterval(handshakeTimer)
78
- window.removeEventListener('message', onMessage)
73
+ const onMessage = event => {
74
+ const data = event.data
75
+ if (!data || data.channel !== CHANNEL || data.type !== 'ready') return
76
+ window.clearTimeout(timeout)
77
+ window.clearInterval(handshakeTimer)
78
+ window.removeEventListener('message', onMessage)
79
79
  const viewport =
80
80
  data.viewport &&
81
81
  typeof data.viewport === 'object' &&
@@ -100,21 +100,21 @@ function waitForReady() {
100
100
  }
101
101
  applyThemeFromHost(data.theme)
102
102
  resolve(bridgeMeta)
103
- }
104
-
105
- window.addEventListener('message', onMessage)
106
- postHandshake()
107
- const handshakeTimer = window.setInterval(postHandshake, HANDSHAKE_MS)
108
- })
109
- return readyPromise
110
- }
103
+ }
104
+
105
+ window.addEventListener('message', onMessage)
106
+ postHandshake()
107
+ const handshakeTimer = window.setInterval(postHandshake, HANDSHAKE_MS)
108
+ })
109
+ return readyPromise
110
+ }
111
111
 
112
- function call(method, args = []) {
113
- return waitForReady().then(
114
- () =>
115
- new Promise((resolve, reject) => {
116
- const id = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
117
- const onMessage = event => {
112
+ function call(method, args = []) {
113
+ return waitForReady().then(
114
+ () =>
115
+ new Promise((resolve, reject) => {
116
+ const id = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
117
+ const onMessage = event => {
118
118
  const data = event.data
119
119
  if (
120
120
  !data ||
@@ -124,14 +124,14 @@ function call(method, args = []) {
124
124
  ) {
125
125
  return
126
126
  }
127
- window.removeEventListener('message', onMessage)
128
- if (data.ok) resolve(data.result)
129
- else reject(new Error(data.error || 'Bridge request failed'))
130
- }
131
- window.addEventListener('message', onMessage)
132
- window.parent.postMessage(
133
- { channel: CHANNEL, type: 'request', id, method, args },
134
- '*',
127
+ window.removeEventListener('message', onMessage)
128
+ if (data.ok) resolve(data.result)
129
+ else reject(new Error(data.error || 'Bridge request failed'))
130
+ }
131
+ window.addEventListener('message', onMessage)
132
+ window.parent.postMessage(
133
+ { channel: CHANNEL, type: 'request', id, method, args },
134
+ '*',
135
135
  )
136
136
  }),
137
137
  )
@@ -1,8 +1,8 @@
1
- export interface PlatformDialogPathResult {
2
- path: string | null
3
- }
4
-
5
- export declare function openPlatformFolderDialog(): Promise<string | null>
6
- export declare function savePlatformFolderDialog(): Promise<string | null>
7
- export declare function openPlatformImageDialog(): Promise<string | null>
8
- export declare function openPlatformFileDialog(): Promise<string | null>
1
+ export interface PlatformDialogPathResult {
2
+ path: string | null
3
+ }
4
+
5
+ export declare function openPlatformFolderDialog(): Promise<string | null>
6
+ export declare function savePlatformFolderDialog(): Promise<string | null>
7
+ export declare function openPlatformImageDialog(): Promise<string | null>
8
+ export declare function openPlatformFileDialog(): Promise<string | null>
@@ -1,43 +1,43 @@
1
- import { bridge } from './client.mjs'
2
- import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
-
4
- /**
5
- * @returns {Promise<string | null>} Absolute path to the picked folder, or null if cancelled.
6
- */
7
- export async function openPlatformFolderDialog() {
8
- const result = await bridge.call(
9
- PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FOLDER,
10
- [],
11
- )
12
- return result?.path ?? null
13
- }
14
-
15
- /**
16
- * @returns {Promise<string | null>} Absolute path to the picked parent folder, or null if cancelled.
17
- */
18
- export async function savePlatformFolderDialog() {
19
- const result = await bridge.call(
20
- PLATFORM_BRIDGE_METHODS.DIALOG_SAVE_FOLDER,
21
- [],
22
- )
23
- return result?.path ?? null
24
- }
25
-
26
- /**
27
- * @returns {Promise<string | null>} Absolute path to the picked image, or null if cancelled.
28
- */
29
- export async function openPlatformImageDialog() {
1
+ import { bridge } from './client.mjs'
2
+ import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
+
4
+ /**
5
+ * @returns {Promise<string | null>} Absolute path to the picked folder, or null if cancelled.
6
+ */
7
+ export async function openPlatformFolderDialog() {
8
+ const result = await bridge.call(
9
+ PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FOLDER,
10
+ [],
11
+ )
12
+ return result?.path ?? null
13
+ }
14
+
15
+ /**
16
+ * @returns {Promise<string | null>} Absolute path to the picked parent folder, or null if cancelled.
17
+ */
18
+ export async function savePlatformFolderDialog() {
19
+ const result = await bridge.call(
20
+ PLATFORM_BRIDGE_METHODS.DIALOG_SAVE_FOLDER,
21
+ [],
22
+ )
23
+ return result?.path ?? null
24
+ }
25
+
26
+ /**
27
+ * @returns {Promise<string | null>} Absolute path to the picked image, or null if cancelled.
28
+ */
29
+ export async function openPlatformImageDialog() {
30
30
  const result = await bridge.call(
31
31
  PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_IMAGE,
32
32
  [],
33
- )
34
- return result?.path ?? null
35
- }
36
-
37
- /**
38
- * @returns {Promise<string | null>} Absolute path to the picked file, or null if cancelled.
39
- */
40
- export async function openPlatformFileDialog() {
41
- const result = await bridge.call(PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FILE, [])
42
- return result?.path ?? null
43
- }
33
+ )
34
+ return result?.path ?? null
35
+ }
36
+
37
+ /**
38
+ * @returns {Promise<string | null>} Absolute path to the picked file, or null if cancelled.
39
+ */
40
+ export async function openPlatformFileDialog() {
41
+ const result = await bridge.call(PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FILE, [])
42
+ return result?.path ?? null
43
+ }
@@ -1,73 +1,73 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
- import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
-
4
- const bridgeCall = vi.hoisted(() => vi.fn())
5
-
6
- vi.mock('./client.mjs', () => ({
7
- bridge: {
8
- call: bridgeCall,
9
- },
10
- }))
11
-
12
- import {
13
- openPlatformFileDialog,
14
- openPlatformFolderDialog,
15
- openPlatformImageDialog,
16
- savePlatformFolderDialog,
17
- } from './dialog.mjs'
18
-
19
- beforeEach(() => {
20
- bridgeCall.mockReset()
21
- })
22
-
23
- describe('dialog bridge helpers', () => {
24
- it('opens a folder dialog using the shell method with no arguments', async () => {
25
- bridgeCall.mockResolvedValue({ path: '/tmp' })
26
-
27
- await expect(openPlatformFolderDialog()).resolves.toBe('/tmp')
28
-
29
- expect(bridgeCall).toHaveBeenCalledWith(
30
- PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FOLDER,
31
- [],
32
- )
33
- })
34
-
35
- it('opens a save folder dialog using the shell method with no arguments', async () => {
36
- bridgeCall.mockResolvedValue({ path: '/tmp/project' })
37
-
38
- await expect(savePlatformFolderDialog()).resolves.toBe('/tmp/project')
39
-
40
- expect(bridgeCall).toHaveBeenCalledWith(
41
- PLATFORM_BRIDGE_METHODS.DIALOG_SAVE_FOLDER,
42
- [],
43
- )
44
- })
45
-
46
- it('opens an image dialog using the shell method with no arguments', async () => {
47
- bridgeCall.mockResolvedValue({ path: '/tmp/image.png' })
48
-
49
- await expect(openPlatformImageDialog()).resolves.toBe('/tmp/image.png')
50
-
51
- expect(bridgeCall).toHaveBeenCalledWith(
52
- PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_IMAGE,
53
- [],
54
- )
55
- })
56
-
57
- it('opens a file dialog using the shell method with no arguments', async () => {
58
- bridgeCall.mockResolvedValue({ path: '/tmp/file.md' })
59
-
60
- await expect(openPlatformFileDialog()).resolves.toBe('/tmp/file.md')
61
-
62
- expect(bridgeCall).toHaveBeenCalledWith(
63
- PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FILE,
64
- [],
65
- )
66
- })
67
-
68
- it('returns null when the shell dialog is cancelled', async () => {
69
- bridgeCall.mockResolvedValue({ path: null })
70
-
71
- await expect(openPlatformFolderDialog()).resolves.toBeNull()
72
- })
73
- })
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
+
4
+ const bridgeCall = vi.hoisted(() => vi.fn())
5
+
6
+ vi.mock('./client.mjs', () => ({
7
+ bridge: {
8
+ call: bridgeCall,
9
+ },
10
+ }))
11
+
12
+ import {
13
+ openPlatformFileDialog,
14
+ openPlatformFolderDialog,
15
+ openPlatformImageDialog,
16
+ savePlatformFolderDialog,
17
+ } from './dialog.mjs'
18
+
19
+ beforeEach(() => {
20
+ bridgeCall.mockReset()
21
+ })
22
+
23
+ describe('dialog bridge helpers', () => {
24
+ it('opens a folder dialog using the shell method with no arguments', async () => {
25
+ bridgeCall.mockResolvedValue({ path: '/tmp' })
26
+
27
+ await expect(openPlatformFolderDialog()).resolves.toBe('/tmp')
28
+
29
+ expect(bridgeCall).toHaveBeenCalledWith(
30
+ PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FOLDER,
31
+ [],
32
+ )
33
+ })
34
+
35
+ it('opens a save folder dialog using the shell method with no arguments', async () => {
36
+ bridgeCall.mockResolvedValue({ path: '/tmp/project' })
37
+
38
+ await expect(savePlatformFolderDialog()).resolves.toBe('/tmp/project')
39
+
40
+ expect(bridgeCall).toHaveBeenCalledWith(
41
+ PLATFORM_BRIDGE_METHODS.DIALOG_SAVE_FOLDER,
42
+ [],
43
+ )
44
+ })
45
+
46
+ it('opens an image dialog using the shell method with no arguments', async () => {
47
+ bridgeCall.mockResolvedValue({ path: '/tmp/image.png' })
48
+
49
+ await expect(openPlatformImageDialog()).resolves.toBe('/tmp/image.png')
50
+
51
+ expect(bridgeCall).toHaveBeenCalledWith(
52
+ PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_IMAGE,
53
+ [],
54
+ )
55
+ })
56
+
57
+ it('opens a file dialog using the shell method with no arguments', async () => {
58
+ bridgeCall.mockResolvedValue({ path: '/tmp/file.md' })
59
+
60
+ await expect(openPlatformFileDialog()).resolves.toBe('/tmp/file.md')
61
+
62
+ expect(bridgeCall).toHaveBeenCalledWith(
63
+ PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_FILE,
64
+ [],
65
+ )
66
+ })
67
+
68
+ it('returns null when the shell dialog is cancelled', async () => {
69
+ bridgeCall.mockResolvedValue({ path: null })
70
+
71
+ await expect(openPlatformFolderDialog()).resolves.toBeNull()
72
+ })
73
+ })
@@ -1,31 +1,31 @@
1
- import type {
2
- PlatformFileCreateFolderResult,
3
- PlatformFileDeleteResult,
4
- PlatformFileListResult,
5
- PlatformFilePreviewUrlResult,
6
- PlatformFileReadBinaryResult,
7
- PlatformFileReadPreviewResult,
8
- PlatformFileRenameResult,
9
- PlatformFileWriteResult,
10
- } from './types.js'
1
+ import type {
2
+ PlatformFileCreateFolderResult,
3
+ PlatformFileDeleteResult,
4
+ PlatformFileListResult,
5
+ PlatformFilePreviewUrlResult,
6
+ PlatformFileReadBinaryResult,
7
+ PlatformFileReadPreviewResult,
8
+ PlatformFileRenameResult,
9
+ PlatformFileWriteResult,
10
+ } from './types.js'
11
11
 
12
- export declare const PLATFORM_FILE_TEXT_PREVIEW_MAX_BYTES: number
13
- export declare const PLATFORM_FILE_BINARY_PREVIEW_MAX_BYTES: number
14
-
15
- export declare function listPlatformFiles(
16
- rootPath: string,
17
- ): Promise<PlatformFileListResult>
18
-
19
- export declare function readPlatformTextFile(path: string): Promise<string>
20
-
21
- export declare function writePlatformTextFile(
22
- path: string,
23
- content: string,
24
- ): Promise<PlatformFileWriteResult>
25
-
26
- export declare function readPlatformFilePreview(
27
- path: string,
28
- maxBytes?: number,
12
+ export declare const PLATFORM_FILE_TEXT_PREVIEW_MAX_BYTES: number
13
+ export declare const PLATFORM_FILE_BINARY_PREVIEW_MAX_BYTES: number
14
+
15
+ export declare function listPlatformFiles(
16
+ rootPath: string,
17
+ ): Promise<PlatformFileListResult>
18
+
19
+ export declare function readPlatformTextFile(path: string): Promise<string>
20
+
21
+ export declare function writePlatformTextFile(
22
+ path: string,
23
+ content: string,
24
+ ): Promise<PlatformFileWriteResult>
25
+
26
+ export declare function readPlatformFilePreview(
27
+ path: string,
28
+ maxBytes?: number,
29
29
  ): Promise<PlatformFileReadPreviewResult>
30
30
 
31
31
  export declare function readPlatformFileBinary(
@@ -51,17 +51,17 @@ export declare function writePlatformFileBinary(
51
51
  base64: string,
52
52
  ): Promise<{ ok: true }>
53
53
 
54
- export declare function createPlatformFolder(
55
- parentPath: string,
56
- name: string,
57
- ): Promise<PlatformFileCreateFolderResult>
58
-
59
- export declare function renamePlatformFile(
60
- path: string,
61
- name: string,
62
- ): Promise<PlatformFileRenameResult>
63
-
64
- export declare function deletePlatformFile(
65
- path: string,
66
- recursive?: boolean,
67
- ): Promise<PlatformFileDeleteResult>
54
+ export declare function createPlatformFolder(
55
+ parentPath: string,
56
+ name: string,
57
+ ): Promise<PlatformFileCreateFolderResult>
58
+
59
+ export declare function renamePlatformFile(
60
+ path: string,
61
+ name: string,
62
+ ): Promise<PlatformFileRenameResult>
63
+
64
+ export declare function deletePlatformFile(
65
+ path: string,
66
+ recursive?: boolean,
67
+ ): Promise<PlatformFileDeleteResult>