@puredesktop/puredesktop-ui-bridge 2.1.4 → 2.1.7

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 +19 -1
  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 +26 -24
  43. package/src/bridge/storage.mjs +18 -18
  44. package/src/bridge/storage.test.ts +45 -45
  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 -3
  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
@@ -36,6 +36,175 @@ export function themeToCssVars(
36
36
  }
37
37
 
38
38
  walk(theme as unknown as Record<string, unknown>, [])
39
+ if (prefix === PLATFORM_THEME_PREFIX) {
40
+ lines.push(
41
+ '--platform-colors-background: var(--platform-colors-bg);',
42
+ '--platform-colors-text-primary: var(--platform-colors-text);',
43
+ '--platform-colors-text-muted: var(--platform-colors-text-secondary);',
44
+ '--platform-colors-text-tertiary: var(--platform-colors-text-disabled);',
45
+ '--platform-colors-border-subtle: var(--platform-colors-divider);',
46
+ '--platform-colors-focus-ring: var(--platform-colors-focus);',
47
+ '--platform-colors-surface-muted: var(--platform-colors-surface-hover);',
48
+ '--platform-colors-surface-subtle: var(--platform-colors-surface-hover);',
49
+ '--platform-colors-surface-raised: var(--platform-colors-elevated);',
50
+ '--platform-colors-surface-selected: var(--platform-colors-surface-active);',
51
+ '--platform-colors-popover: var(--platform-colors-text);',
52
+ '--platform-colors-popover-text: var(--platform-colors-text-inverse);',
53
+ theme.mode === 'dark'
54
+ ? '--platform-colors-scrim: rgba(0, 0, 0, 0.55);'
55
+ : '--platform-colors-scrim: rgba(28, 26, 23, 0.42);',
56
+ theme.mode === 'dark'
57
+ ? '--platform-colors-tooltip-bg: #ececef;'
58
+ : '--platform-colors-tooltip-bg: var(--platform-colors-text);',
59
+ theme.mode === 'dark'
60
+ ? '--platform-colors-tooltip-text: #1b1b1e;'
61
+ : '--platform-colors-tooltip-text: var(--platform-colors-text-inverse);',
62
+ '--platform-colors-text-on-accent: var(--platform-colors-text-inverse);',
63
+ '--platform-colors-text-on-danger: var(--platform-colors-text-inverse);',
64
+ '--platform-colors-danger-muted: var(--platform-colors-semantic-red-muted);',
65
+ '--platform-colors-danger-border: var(--platform-colors-semantic-red-border);',
66
+ '--platform-colors-danger-text: var(--platform-colors-semantic-red-text);',
67
+ '--platform-colors-warning-muted: var(--platform-colors-semantic-orange-muted);',
68
+ '--platform-colors-warning-border: var(--platform-colors-semantic-orange-border);',
69
+ '--platform-colors-warning-text: var(--platform-colors-semantic-orange-text);',
70
+ '--platform-colors-success-muted: var(--platform-colors-semantic-green-muted);',
71
+ '--platform-colors-success-border: var(--platform-colors-semantic-green-border);',
72
+ '--platform-colors-success-text: var(--platform-colors-semantic-green-text);',
73
+ '--platform-colors-info-muted: var(--platform-colors-semantic-blue-muted);',
74
+ '--platform-colors-info-border: var(--platform-colors-semantic-blue-border);',
75
+ '--platform-colors-info-text: var(--platform-colors-semantic-blue-text);',
76
+ theme.mode === 'dark'
77
+ ? '--platform-colors-disabled-primary: #6e6e74;'
78
+ : '--platform-colors-disabled-primary: #9a9aa0;',
79
+
80
+ '--page: var(--platform-colors-elevated);',
81
+ '--canvas: var(--platform-colors-bg);',
82
+ '--surface: var(--platform-colors-surface);',
83
+ '--chrome: var(--platform-colors-chrome-tab-strip);',
84
+ '--well: var(--platform-colors-surface-active);',
85
+ '--line: var(--platform-colors-divider);',
86
+ '--line-strong: var(--platform-colors-border-strong);',
87
+ '--ink: var(--platform-colors-text);',
88
+ '--ink-2: var(--platform-colors-text-secondary);',
89
+ '--ink-muted: var(--platform-colors-text-secondary);',
90
+ '--ink-faint: var(--platform-colors-text-disabled);',
91
+ theme.mode === 'dark'
92
+ ? '--page-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 18px 48px rgba(0, 0, 0, 0.4);'
93
+ : '--page-shadow: 0 1px 2px rgba(18, 18, 22, 0.05), 0 18px 48px rgba(18, 18, 22, 0.13);',
94
+ '--pure-page: var(--page);',
95
+ '--pure-canvas: var(--canvas);',
96
+ '--pure-surface: var(--surface);',
97
+ '--pure-chrome: var(--chrome);',
98
+ '--pure-well: var(--well);',
99
+ '--pure-page-shadow: var(--page-shadow);',
100
+ '--pure-paper-chrome: var(--chrome);',
101
+ '--pure-paper-canvas: var(--canvas);',
102
+ '--pure-paper-surface: var(--surface);',
103
+ '--pure-paper-sunken: var(--well);',
104
+ '--pure-ink: var(--platform-colors-text);',
105
+ '--pure-ink-2: var(--platform-colors-text-secondary);',
106
+ '--pure-ink-muted: var(--platform-colors-text-secondary);',
107
+ '--pure-ink-faint: var(--platform-colors-text-disabled);',
108
+ '--pure-line: var(--platform-colors-divider);',
109
+ '--pure-line-strong: var(--platform-colors-border-strong);',
110
+ '--pure-line-ink: var(--platform-colors-text);',
111
+ '--pure-info: var(--platform-colors-info);',
112
+ '--pure-info-muted: var(--platform-colors-info-muted);',
113
+ '--pure-success: var(--platform-colors-success);',
114
+ '--pure-success-muted: var(--platform-colors-success-muted);',
115
+ '--pure-attention: var(--platform-colors-warning);',
116
+ '--pure-attention-muted: var(--platform-colors-warning-muted);',
117
+ '--pure-danger: var(--platform-colors-danger);',
118
+ '--pure-danger-muted: var(--platform-colors-danger-muted);',
119
+ '--pure-disabled-primary: var(--platform-colors-disabled-primary);',
120
+ '--pure-font-ui: var(--platform-typography-font-family);',
121
+ '--pure-font-mono: var(--platform-typography-font-family-mono);',
122
+ '--pure-font-serif: var(--platform-typography-font-family-content);',
123
+ '--pure-chrome-os-strip: var(--platform-colors-chrome-tab-strip);',
124
+ '--pure-chrome-wordmark-bar: var(--platform-colors-chrome-titlebar);',
125
+ '--pure-chrome-format-bar: var(--platform-colors-surface);',
126
+ '--pure-chrome-hairline: var(--platform-colors-divider);',
127
+ '--pure-chrome-ink: var(--platform-colors-text);',
128
+ '--pure-chrome-secondary: #6a6a70;',
129
+ '--pure-chrome-tertiary: #8a8a90;',
130
+ '--pure-chrome-faint: var(--platform-colors-text-tertiary);',
131
+ '--pure-chrome-accent-green: #1f9d57;',
132
+ '--pure-type-display-size: 34px;',
133
+ '--pure-type-title-size: 24px;',
134
+ '--pure-type-heading-size: 17px;',
135
+ '--pure-type-body-size: 15px;',
136
+ '--pure-type-small-size: 13px;',
137
+ '--pure-type-label-size: 11px;',
138
+ '--pure-radius-square: 0px;',
139
+ '--pure-radius-pill: 999px;',
140
+
141
+ '--factory-color-ink-2: var(--platform-colors-text-secondary);',
142
+ '--factory-color-soft: var(--platform-colors-text-secondary);',
143
+ '--factory-color-faint: var(--platform-colors-text-disabled);',
144
+ '--factory-color-line: var(--platform-colors-divider);',
145
+ '--factory-color-surface: var(--platform-colors-surface-hover);',
146
+ '--factory-color-control-border: var(--platform-colors-border);',
147
+ '--factory-color-disabled-track: var(--platform-colors-divider);',
148
+ '--factory-color-positive-text: var(--platform-colors-success-text);',
149
+ '--factory-color-paper: var(--platform-colors-surface);',
150
+ '--factory-color-ground: var(--platform-colors-bg);',
151
+ '--factory-color-ink: var(--platform-colors-text);',
152
+ '--factory-color-muted: var(--platform-colors-text-secondary);',
153
+ '--factory-color-rule: var(--platform-colors-border);',
154
+ '--factory-color-rule-strong: var(--platform-colors-border-strong);',
155
+ '--factory-color-positive: var(--platform-colors-semantic-green);',
156
+ '--factory-color-positive-muted: var(--platform-colors-semantic-green-muted);',
157
+ '--factory-font-sans: var(--platform-typography-font-family);',
158
+ '--factory-font-mono: var(--platform-typography-font-family-mono);',
159
+ '--factory-type-display-size: 30px;',
160
+ '--factory-type-title-size: 24px;',
161
+ '--factory-type-heading-size: 17px;',
162
+ '--factory-type-body-size: 14.5px;',
163
+ '--factory-type-small-size: 13px;',
164
+ '--factory-type-label-size: 11px;',
165
+ '--factory-type-line-height: 1.6;',
166
+ '--factory-space-1: 4px;',
167
+ '--factory-space-2: 8px;',
168
+ '--factory-space-3: 12px;',
169
+ '--factory-space-4: 16px;',
170
+ '--factory-space-6: 24px;',
171
+ '--factory-space-8: 32px;',
172
+ '--factory-radius: var(--platform-radius-md);',
173
+ '--factory-shadow: var(--platform-shadow-sm);',
174
+
175
+ '--print-color-ink: #16181d;',
176
+ '--print-color-ink-soft: #3c4049;',
177
+ '--print-color-muted: #71757e;',
178
+ '--print-color-paper: #ffffff;',
179
+ '--print-color-rule: #c9cbd0;',
180
+ '--print-color-rule-soft: #e4e5e9;',
181
+ '--print-color-accent: #1f39c4;',
182
+ '--print-font-serif: "Source Serif 4", Georgia, serif;',
183
+ '--print-font-sans: "Archivo", var(--platform-typography-font-family);',
184
+ '--print-font-mono: "JetBrains Mono", var(--platform-typography-font-family-mono);',
185
+ '--print-body-size: 10.6pt;',
186
+ '--print-leading: 1.5;',
187
+ '--print-measure: 34rem;',
188
+ '--print-rhythm: 15.9pt;',
189
+ '--print-type-caption: 8.4pt;',
190
+ '--print-type-body: 10.6pt;',
191
+ '--print-type-lead: 12.5pt;',
192
+ '--print-type-h3: 12.5pt;',
193
+ '--print-type-h2: 15pt;',
194
+ '--print-type-h1: 24pt;',
195
+ '--print-type-title: 30pt;',
196
+ '--typeset-color-ink: var(--print-color-ink);',
197
+ '--typeset-color-muted: var(--print-color-muted);',
198
+ '--typeset-color-paper: var(--print-color-paper);',
199
+ '--typeset-color-rule: var(--print-color-rule);',
200
+ '--typeset-color-accent: var(--print-color-accent);',
201
+ '--typeset-font-serif: var(--print-font-serif);',
202
+ '--typeset-font-sans: var(--print-font-sans);',
203
+ '--typeset-font-mono: var(--print-font-mono);',
204
+ '--typeset-measure: var(--print-measure);',
205
+ '--typeset-rhythm: var(--print-rhythm);',
206
+ )
207
+ }
39
208
  return lines.join('\n')
40
209
  }
41
210
 
@@ -180,99 +180,99 @@ export function DraftAssistantDialog({
180
180
  closeOnBackdropClick={!generating}
181
181
  >
182
182
  <StyledShell>
183
- <StyledBody>
184
- {mode === 'rewrite' && selectionText ? (
183
+ <StyledBody>
184
+ {mode === 'rewrite' && selectionText ? (
185
+ <div>
186
+ <StyledLabel>Selected</StyledLabel>
187
+ <StyledSelectionQuote>{selectionText}</StyledSelectionQuote>
188
+ </div>
189
+ ) : null}
190
+
185
191
  <div>
186
- <StyledLabel>Selected</StyledLabel>
187
- <StyledSelectionQuote>{selectionText}</StyledSelectionQuote>
192
+ <StyledLabel>
193
+ {mode === 'rewrite' ? 'How should it change?' : 'What to draft'}
194
+ </StyledLabel>
195
+ <TextAreaField
196
+ autoFocus
197
+ value={instruction}
198
+ disabled={generating}
199
+ placeholder={
200
+ mode === 'rewrite'
201
+ ? 'e.g. tighten this, make it warmer, add an example…'
202
+ : 'e.g. draft an opening paragraph about…'
203
+ }
204
+ onChange={event => setInstruction(event.target.value)}
205
+ />
188
206
  </div>
189
- ) : null}
190
-
191
- <div>
192
- <StyledLabel>
193
- {mode === 'rewrite' ? 'How should it change?' : 'What to draft'}
194
- </StyledLabel>
195
- <TextAreaField
196
- autoFocus
197
- value={instruction}
198
- disabled={generating}
199
- placeholder={
200
- mode === 'rewrite'
201
- ? 'e.g. tighten this, make it warmer, add an example…'
202
- : 'e.g. draft an opening paragraph about…'
203
- }
204
- onChange={event => setInstruction(event.target.value)}
205
- />
206
- </div>
207
-
208
- <div>
209
- <StyledLabel>Style</StyledLabel>
210
- <WritingStyleManager
211
- styles={styles}
212
- selectedIds={selectedIds}
213
- disabled={generating}
214
- onChange={onStylesChange}
215
- onSelectedChange={setSelectedIds}
216
- />
217
- </div>
218
207
 
219
- {context?.trim() ? (
220
- <StyledToggle>
221
- <Checkbox
222
- checked={useContext}
208
+ <div>
209
+ <StyledLabel>Style</StyledLabel>
210
+ <WritingStyleManager
211
+ styles={styles}
212
+ selectedIds={selectedIds}
223
213
  disabled={generating}
224
- onChange={event => setUseContext(event.target.checked)}
214
+ onChange={onStylesChange}
215
+ onSelectedChange={setSelectedIds}
225
216
  />
226
- Ground in this document (abstract, references…)
227
- </StyledToggle>
228
- ) : null}
217
+ </div>
229
218
 
230
- {error ? <InlineBanner variant="danger">{error}</InlineBanner> : null}
219
+ {context?.trim() ? (
220
+ <StyledToggle>
221
+ <Checkbox
222
+ checked={useContext}
223
+ disabled={generating}
224
+ onChange={event => setUseContext(event.target.checked)}
225
+ />
226
+ Ground in this document (abstract, references…)
227
+ </StyledToggle>
228
+ ) : null}
231
229
 
232
- <div>
233
- <StyledLabel>Preview</StyledLabel>
234
- {result ? (
235
- <StyledPreview dangerouslySetInnerHTML={{ __html: result }} />
236
- ) : (
237
- <StyledPreviewEmpty>
238
- {generating
239
- ? 'Drafting…'
240
- : 'Generate a draft to preview it here.'}
241
- </StyledPreviewEmpty>
242
- )}
243
- </div>
244
- </StyledBody>
230
+ {error ? <InlineBanner variant="danger">{error}</InlineBanner> : null}
245
231
 
246
- <StyledActions>
247
- <Button
248
- type="button"
249
- variant="subtle"
250
- size="md"
251
- disabled={generating}
252
- onClick={onClose}
253
- >
254
- Cancel
255
- </Button>
256
- <Button
257
- type="button"
258
- variant="primary"
259
- size="md"
260
- loading={generating}
261
- disabled={generating}
262
- onClick={() => void generate()}
263
- >
264
- {result ? 'Regenerate' : 'Generate'}
265
- </Button>
266
- <Button
267
- type="button"
268
- variant="primary"
269
- size="md"
270
- disabled={!result || generating}
271
- onClick={() => onInsert(result)}
272
- >
273
- {mode === 'rewrite' ? 'Replace selection' : 'Insert'}
274
- </Button>
275
- </StyledActions>
232
+ <div>
233
+ <StyledLabel>Preview</StyledLabel>
234
+ {result ? (
235
+ <StyledPreview dangerouslySetInnerHTML={{ __html: result }} />
236
+ ) : (
237
+ <StyledPreviewEmpty>
238
+ {generating
239
+ ? 'Drafting…'
240
+ : 'Generate a draft to preview it here.'}
241
+ </StyledPreviewEmpty>
242
+ )}
243
+ </div>
244
+ </StyledBody>
245
+
246
+ <StyledActions>
247
+ <Button
248
+ type="button"
249
+ variant="subtle"
250
+ size="md"
251
+ disabled={generating}
252
+ onClick={onClose}
253
+ >
254
+ Cancel
255
+ </Button>
256
+ <Button
257
+ type="button"
258
+ variant="primary"
259
+ size="md"
260
+ loading={generating}
261
+ disabled={generating}
262
+ onClick={() => void generate()}
263
+ >
264
+ {result ? 'Regenerate' : 'Generate'}
265
+ </Button>
266
+ <Button
267
+ type="button"
268
+ variant="primary"
269
+ size="md"
270
+ disabled={!result || generating}
271
+ onClick={() => onInsert(result)}
272
+ >
273
+ {mode === 'rewrite' ? 'Replace selection' : 'Insert'}
274
+ </Button>
275
+ </StyledActions>
276
276
  </StyledShell>
277
277
  </Modal>
278
278
  )
@@ -201,7 +201,9 @@ export function WritingStyleManager({
201
201
  setErrors(current => ({
202
202
  ...current,
203
203
  [style.id]:
204
- cause instanceof Error ? cause.message : 'Could not distill the style.',
204
+ cause instanceof Error
205
+ ? cause.message
206
+ : 'Could not distill the style.',
205
207
  }))
206
208
  } finally {
207
209
  setDistilling(current => ({ ...current, [style.id]: false }))
@@ -213,8 +215,9 @@ export function WritingStyleManager({
213
215
  return (
214
216
  <StyledRoot>
215
217
  <StyledHint>
216
- Your own named styles guide the agent’s voice — never the book’s content.
217
- Pick which apply to this draft. Saved across books; manage them below.
218
+ Your own named styles guide the agent’s voice — never the book’s
219
+ content. Pick which apply to this draft. Saved across books; manage them
220
+ below.
218
221
  </StyledHint>
219
222
 
220
223
  <StyledChips>
@@ -1,24 +1,11 @@
1
1
  import {
2
- callAgentRunModel,
3
2
  createAgentSession,
4
3
  deleteAgentSession,
5
- startAgentRun,
4
+ sendAgentSessionMessage,
5
+ type PlatformAgentRunSnapshot,
6
+ type PlatformAgentSessionRecord,
6
7
  } from '../bridge/agents.js'
7
8
 
8
- interface AgentSessionRecord {
9
- sessionId: string
10
- config: { modelId: string }
11
- }
12
-
13
- interface AgentRunSnapshot {
14
- runId: string
15
- state: string
16
- messages: Array<{ role: string; content: string }>
17
- error?: string
18
- }
19
-
20
- const MAX_TURNS = 4
21
-
22
9
  export type DraftTextMode = 'write' | 'rewrite'
23
10
 
24
11
  export interface DraftTextInput {
@@ -38,8 +25,8 @@ function systemPrompt(mode: DraftTextMode): string {
38
25
  mode === 'rewrite'
39
26
  ? 'You rewrite a passage of a book to the author’s instruction, preserving its meaning.'
40
27
  : 'You draft a passage of book prose from the author’s instruction.',
41
- 'Return clean HTML only — paragraphs <p>, sub-headings <h2>, lists where useful.',
42
- 'No markdown, no code fences, no <h1>, no preamble, no commentary.',
28
+ 'Return clean HTML only — no markdown, no code fences, no <h1>, no preamble, no commentary.',
29
+ 'Match the structure to the request. When asked for a paragraph (or just prose), return only <p> paragraphs — no headings, no lists. Add sub-headings <h2> or lists only when the request calls for a multi-part section or the content clearly warrants structure.',
43
30
  'Do not invent precise statistics, dates, quotations, or citations.',
44
31
  ].join('\n')
45
32
  }
@@ -49,11 +36,19 @@ function message(input: DraftTextInput): string {
49
36
  input.mode === 'rewrite' && input.selectionText
50
37
  ? `Passage to rewrite:\n"""${input.selectionText.slice(0, 6000)}"""`
51
38
  : input.selectionText
52
- ? `Nearby text for context (do not repeat):\n"""${input.selectionText.slice(0, 2000)}"""`
53
- : '',
54
- `Instruction: ${input.instruction.trim() || (input.mode === 'rewrite' ? 'Improve this passage.' : 'Draft this.')}`,
39
+ ? `Nearby text for context (do not repeat):\n"""${input.selectionText.slice(
40
+ 0,
41
+ 2000,
42
+ )}"""`
43
+ : '',
44
+ `Instruction: ${
45
+ input.instruction.trim() ||
46
+ (input.mode === 'rewrite' ? 'Improve this passage.' : 'Draft this.')
47
+ }`,
55
48
  input.context?.trim()
56
- ? `\nReference material — use it for facts, terminology, and claims; do NOT copy its wording as voice and do NOT invent beyond it:\n"""${input.context.trim().slice(0, 4000)}"""`
49
+ ? `\nReference material — use it for facts, terminology, and claims; do NOT copy its wording as voice and do NOT invent beyond it:\n"""${input.context
50
+ .trim()
51
+ .slice(0, 4000)}"""`
57
52
  : '',
58
53
  input.styleGuidance ? `\n${input.styleGuidance}` : '',
59
54
  '',
@@ -70,24 +65,31 @@ function cleanHtml(raw: string): string {
70
65
  return value.trim()
71
66
  }
72
67
 
73
- function latestAssistant(snapshot: AgentRunSnapshot): string {
68
+ function latestAssistant(snapshot: PlatformAgentRunSnapshot): string {
74
69
  return (
75
70
  [...snapshot.messages]
76
71
  .reverse()
77
- .find(entry => entry.role === 'assistant')
72
+ .find(entry => entry.role === 'assistant' && entry.content.trim())
78
73
  ?.content.trim() ?? ''
79
74
  )
80
75
  }
81
76
 
82
- /** Draft or rewrite a passage via the agent. Returns HTML (empty on failure). */
77
+ const DRAFT_APP_SLUG = 'book'
78
+
79
+ /**
80
+ * Draft or rewrite a passage via the agent. Returns HTML (empty on failure).
81
+ *
82
+ * Stays on the single session-drive path (`sendAgentSessionMessage`) per the
83
+ * agent-steering docs — one server-driven turn, no manual run stepping.
84
+ */
83
85
  export async function draftTextWithAgent(
84
86
  input: DraftTextInput,
85
87
  ): Promise<string> {
86
- let session: AgentSessionRecord | null = null
88
+ let session: PlatformAgentSessionRecord | null = null
87
89
  try {
88
- session = (await createAgentSession({
90
+ session = await createAgentSession({
89
91
  scope: 'app',
90
- appSlug: 'book',
92
+ appSlug: DRAFT_APP_SLUG,
91
93
  title: 'Draft passage',
92
94
  config: {
93
95
  sessionPrompt: systemPrompt(input.mode),
@@ -95,36 +97,31 @@ export async function draftTextWithAgent(
95
97
  allowedTargetApps: ['__purebook_draft_text_no_tools__'],
96
98
  defaultRequiresApproval: true,
97
99
  },
98
- } as never,
99
- })) as AgentSessionRecord
100
-
101
- let snapshot = (await startAgentRun({
102
- session: { scope: 'app', sessionId: session.sessionId, appSlug: 'book' },
103
- message: message(input),
104
- })) as AgentRunSnapshot
105
-
106
- for (let turn = 0; turn < MAX_TURNS; turn += 1) {
107
- if (snapshot.state === 'ready') {
108
- snapshot = (await callAgentRunModel({
109
- runId: snapshot.runId,
110
- sessionId: session.sessionId,
111
- })) as AgentRunSnapshot
112
- continue
113
- }
114
- break
100
+ },
101
+ })
102
+ const sessionRef = {
103
+ scope: 'app' as const,
104
+ appSlug: DRAFT_APP_SLUG,
105
+ sessionId: session.sessionId,
115
106
  }
116
-
117
- if (snapshot.state === 'awaiting_user') {
118
- return cleanHtml(latestAssistant(snapshot))
107
+ const driven = await sendAgentSessionMessage({
108
+ session: sessionRef,
109
+ message: message(input),
110
+ })
111
+ const snapshot = driven.snapshot
112
+ if (snapshot.state !== 'awaiting_user' && snapshot.state !== 'ready') {
113
+ throw new Error(
114
+ snapshot.error || `The model stopped at ${snapshot.state}.`,
115
+ )
119
116
  }
120
- throw new Error(snapshot.error || `The model stopped at ${snapshot.state}.`)
117
+ return cleanHtml(latestAssistant(snapshot))
121
118
  } finally {
122
119
  if (session) {
123
120
  try {
124
121
  await deleteAgentSession({
125
122
  scope: 'app',
123
+ appSlug: DRAFT_APP_SLUG,
126
124
  sessionId: session.sessionId,
127
- appSlug: 'book',
128
125
  })
129
126
  } catch {
130
127
  /* best-effort cleanup */
@@ -14,9 +14,10 @@ const STORAGE_FILE = 'writing-styles.json'
14
14
 
15
15
  export async function loadSharedWritingStyles(): Promise<BookWritingStyle> {
16
16
  try {
17
- const result = (await bridge.call(PLATFORM_BRIDGE_METHODS.STORAGE_READ_JSON, [
18
- { appSlug: STORAGE_SLUG, fileName: STORAGE_FILE },
19
- ])) as { value?: unknown } | null
17
+ const result = (await bridge.call(
18
+ PLATFORM_BRIDGE_METHODS.STORAGE_READ_JSON,
19
+ [{ appSlug: STORAGE_SLUG, fileName: STORAGE_FILE }],
20
+ )) as { value?: unknown } | null
20
21
  return normalizeWritingStyle(result?.value)
21
22
  } catch {
22
23
  return normalizeWritingStyle(null)
@@ -21,7 +21,11 @@ describe('normalizeWritingStyle', () => {
21
21
  it('keeps current-shape styles and drops empty ones', () => {
22
22
  const { styles } = normalizeWritingStyle({
23
23
  styles: [
24
- { id: 'a', name: 'Docs', sources: [{ id: 's', label: 'x', text: 'hi' }] },
24
+ {
25
+ id: 'a',
26
+ name: 'Docs',
27
+ sources: [{ id: 's', label: 'x', text: 'hi' }],
28
+ },
25
29
  { id: 'b', name: 'Empty', sources: [], notes: '', skill: '' },
26
30
  { id: 'c', name: 'Notes only', notes: 'be terse' },
27
31
  ],
@@ -43,7 +47,9 @@ describe('normalizeWritingStyle', () => {
43
47
 
44
48
  it('disables a skill flag when there is no skill text', () => {
45
49
  const { styles } = normalizeWritingStyle({
46
- styles: [{ id: 'a', name: 'X', notes: 'n', skill: '', skillEnabled: true }],
50
+ styles: [
51
+ { id: 'a', name: 'X', notes: 'n', skill: '', skillEnabled: true },
52
+ ],
47
53
  })
48
54
  expect(styles[0].skillEnabled).toBe(false)
49
55
  })
@@ -88,10 +88,15 @@ function coerceStyle(item: unknown): WritingStyle | null {
88
88
  : []
89
89
 
90
90
  // Legacy migration: a flat StyleExample { label, text } becomes one source.
91
- if (sources.length === 0 && typeof entry.text === 'string' && entry.text.trim()) {
91
+ if (
92
+ sources.length === 0 &&
93
+ typeof entry.text === 'string' &&
94
+ entry.text.trim()
95
+ ) {
92
96
  sources = [
93
97
  {
94
- id: typeof entry.id === 'string' && entry.id ? entry.id : randomId('src'),
98
+ id:
99
+ typeof entry.id === 'string' && entry.id ? entry.id : randomId('src'),
95
100
  label:
96
101
  typeof entry.label === 'string' && entry.label.trim()
97
102
  ? entry.label.trim()
@@ -105,8 +110,8 @@ function coerceStyle(item: unknown): WritingStyle | null {
105
110
  typeof entry.name === 'string' && entry.name.trim()
106
111
  ? entry.name.trim()
107
112
  : typeof entry.label === 'string' && entry.label.trim()
108
- ? entry.label.trim()
109
- : 'Untitled style'
113
+ ? entry.label.trim()
114
+ : 'Untitled style'
110
115
  const notes = typeof entry.notes === 'string' ? entry.notes : ''
111
116
  const include = typeof entry.include === 'string' ? entry.include : ''
112
117
  const exclude = typeof entry.exclude === 'string' ? entry.exclude : ''
@@ -142,8 +147,8 @@ export function normalizeWritingStyle(value: unknown): BookWritingStyle {
142
147
  const source = Array.isArray(record.styles)
143
148
  ? record.styles
144
149
  : Array.isArray(record.examples)
145
- ? record.examples
146
- : []
150
+ ? record.examples
151
+ : []
147
152
  const styles = source
148
153
  .map(coerceStyle)
149
154
  .filter((style): style is WritingStyle => style !== null)
@@ -160,11 +165,17 @@ export function styleRawGuidance(style: WritingStyle): string {
160
165
  if (style.notes.trim()) lines.push(`Guidance: ${style.notes.trim()}`)
161
166
  if (style.include.trim()) {
162
167
  lines.push(
163
- `Prefer these words and phrases where they fit naturally: ${normalizeWordList(style.include)}.`,
168
+ `Prefer these words and phrases where they fit naturally: ${normalizeWordList(
169
+ style.include,
170
+ )}.`,
164
171
  )
165
172
  }
166
173
  if (style.exclude.trim()) {
167
- lines.push(`Avoid these words and phrases entirely: ${normalizeWordList(style.exclude)}.`)
174
+ lines.push(
175
+ `Avoid these words and phrases entirely: ${normalizeWordList(
176
+ style.exclude,
177
+ )}.`,
178
+ )
168
179
  }
169
180
  const samples = style.sources
170
181
  .map(source => source.text.trim())