@puredesktop/puredesktop-ui-bridge 2.1.4 → 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 +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 +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
@@ -212,7 +212,7 @@ const Panel = styled.section<{
212
212
  $density: PrintDesignPanelDensity
213
213
  }>`
214
214
  --print-design-page-surface: var(--platform-colors-elevated, #fff);
215
- --print-design-panel-surface: var(--platform-colors-surface, #f8f9fb);
215
+ --print-design-panel-surface: var(--platform-colors-surface, #ffffff);
216
216
  --print-design-preview-paper: #fff;
217
217
  --platform-panel-tab-surface: var(--print-design-page-surface);
218
218
 
@@ -258,7 +258,7 @@ const Panel = styled.section<{
258
258
 
259
259
  const Popover = styled.div`
260
260
  --print-design-page-surface: var(--platform-colors-elevated, #fff);
261
- --print-design-panel-surface: var(--platform-colors-surface, #f8f9fb);
261
+ --print-design-panel-surface: var(--platform-colors-surface, #ffffff);
262
262
  --print-design-preview-paper: #fff;
263
263
  --platform-panel-tab-surface: var(--print-design-page-surface);
264
264
 
@@ -487,9 +487,7 @@ const DesignMain = styled.div<{
487
487
  grid-column: 1 / 2;
488
488
  grid-row: 2 / 3;
489
489
  grid-template-columns: ${
490
- $mode === 'vision'
491
- ? 'minmax(0, 1fr)'
492
- : 'minmax(0, 1fr)'
490
+ $mode === 'vision' ? 'minmax(0, 1fr)' : 'minmax(0, 1fr)'
493
491
  };
494
492
  grid-template-rows: ${
495
493
  $mode === 'vision' ? 'minmax(0, 1fr)' : 'minmax(0, 1fr) auto'
@@ -673,8 +671,7 @@ const Swatch = styled.span<{ $color: string }>`
673
671
  `
674
672
 
675
673
  const PresetList = styled.div<{ $variant: PrintDesignPanelVariant }>`
676
- display: ${({ $variant }) =>
677
- $variant === 'popover' ? 'flex' : 'grid'};
674
+ display: ${({ $variant }) => ($variant === 'popover' ? 'flex' : 'grid')};
678
675
  grid-template-columns: ${({ $variant }) =>
679
676
  $variant === 'workspace'
680
677
  ? 'repeat(auto-fit, minmax(172px, 1fr))'
@@ -1036,7 +1033,7 @@ const VisionEvidenceActionButton = styled.button`
1036
1033
  min-height: 24px;
1037
1034
  padding: 2px 10px;
1038
1035
  border: 1px solid #8cc7a0;
1039
- border-radius: 999px;
1036
+ border-radius: var(--platform-radius-sm);
1040
1037
  background: var(--print-design-panel-surface);
1041
1038
  color: var(--platform-colors-text);
1042
1039
  font: inherit;
@@ -1065,7 +1062,7 @@ const VisionCarouselButton = styled.button`
1065
1062
  height: 24px;
1066
1063
  padding: 0;
1067
1064
  border: 1px solid var(--platform-colors-border);
1068
- border-radius: 999px;
1065
+ border-radius: var(--platform-radius-sm);
1069
1066
  background: var(--print-design-panel-surface);
1070
1067
  color: var(--platform-colors-text);
1071
1068
  cursor: pointer;
@@ -1102,7 +1099,7 @@ const VisionCarouselChip = styled.button<{ $active: boolean }>`
1102
1099
  $active
1103
1100
  ? 'var(--platform-colors-accent, var(--platform-colors-text))'
1104
1101
  : 'var(--platform-colors-border)'};
1105
- border-radius: 999px;
1102
+ border-radius: var(--platform-radius-sm);
1106
1103
  background: ${({ $active }) =>
1107
1104
  $active
1108
1105
  ? 'rgb(from var(--platform-colors-accent, #3454d1) r g b / 0.08)'
@@ -1374,8 +1371,7 @@ const DecisionItem = styled.div<{ $type: string; $startsGroup: boolean }>`
1374
1371
  grid-template-columns: 54px minmax(0, 1fr);
1375
1372
  align-items: start;
1376
1373
  column-gap: var(--platform-spacing-sm);
1377
- padding: ${({ $startsGroup }) =>
1378
- $startsGroup ? '8px 0 6px' : '5px 0 6px'};
1374
+ padding: ${({ $startsGroup }) => ($startsGroup ? '8px 0 6px' : '5px 0 6px')};
1379
1375
  border-top: 1px solid var(--platform-colors-divider);
1380
1376
  background: ${({ $type }) => decisionBackground($type)};
1381
1377
 
@@ -1599,7 +1595,7 @@ function fontStack(
1599
1595
  role: 'serif' | 'sans',
1600
1596
  purpose: 'display' | 'body' | 'label',
1601
1597
  ): string {
1602
- if (role === 'sans') return '"Source Sans 3", system-ui, sans-serif'
1598
+ if (role === 'sans') return '"Archivo", system-ui, sans-serif'
1603
1599
  if (purpose === 'display') return '"Newsreader", Georgia, serif'
1604
1600
  return '"Source Serif 4", Georgia, serif'
1605
1601
  }
@@ -1743,13 +1739,13 @@ function visionComparisonHtml(comparison: PrintDesignVisionComparison): string {
1743
1739
  h1 { margin: 0 0 8px; font-size: 20px; font-weight: 650; }
1744
1740
  .meta { display: grid; gap: 4px; color: #60666d; font-size: 13px; line-height: 1.4; }
1745
1741
  main { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; padding: 18px 24px 24px; }
1746
- section { min-width: 0; background: #fff; border: 1px solid #ddd; border-radius: 10px; padding: 14px; }
1742
+ section { min-width: 0; background: #fff; border: 1px solid #ddd; border-radius: 0; padding: 14px; }
1747
1743
  h2 { margin: 0 0 10px; font-size: 15px; }
1748
1744
  .note { min-height: 44px; margin-bottom: 12px; color: #60666d; font-size: 13px; line-height: 1.4; }
1749
1745
  .stage { display: grid; gap: 10px; }
1750
- img { display: block; width: 100%; max-height: 72vh; object-fit: contain; background: #fff; border: 1px solid #e2e2e2; border-radius: 8px; }
1746
+ img { display: block; width: 100%; max-height: 72vh; object-fit: contain; background: #fff; border: 1px solid #e2e2e2; border-radius: 0; }
1751
1747
  .controls { display: flex; align-items: center; gap: 8px; }
1752
- button { border: 1px solid #ccc; background: #fff; border-radius: 999px; min-width: 28px; height: 28px; cursor: pointer; }
1748
+ button { border: 1px solid #ccc; background: #fff; border-radius: 0; min-width: 28px; height: 28px; cursor: pointer; }
1753
1749
  button:disabled { opacity: .35; cursor: default; }
1754
1750
  .label { flex: 1; min-width: 0; color: #60666d; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1755
1751
  .pdf { margin-top: 8px; font-size: 12px; }
@@ -2253,12 +2249,7 @@ export function PrintDesignPanel({
2253
2249
  data-panel-variant={variant}
2254
2250
  aria-label="Print design"
2255
2251
  >
2256
- <HeaderRow
2257
- $variant={variant}
2258
- $align="center"
2259
- $justify="start"
2260
- $gap="sm"
2261
- >
2252
+ <HeaderRow $variant={variant} $align="center" $justify="start" $gap="sm">
2262
2253
  {variant === 'workspace' ? (
2263
2254
  <WorkspaceIdentity>
2264
2255
  <WorkspaceTitle>Design - PDF Typesetter</WorkspaceTitle>
@@ -2575,7 +2566,10 @@ export function PrintDesignPanel({
2575
2566
 
2576
2567
  <WorkspaceCanvas $variant={variant} $mode={mode}>
2577
2568
  {mode === 'vision' ? (
2578
- <VisionPanel $variant={variant} aria-label="Beta vision typesetting">
2569
+ <VisionPanel
2570
+ $variant={variant}
2571
+ aria-label="Beta vision typesetting"
2572
+ >
2579
2573
  <VisionSettingsRail $variant={variant}>
2580
2574
  <VisionSettingsStrip>
2581
2575
  <SectionTitle>Beta settings</SectionTitle>
@@ -2624,8 +2618,7 @@ export function PrintDesignPanel({
2624
2618
  0,
2625
2619
  Math.min(
2626
2620
  5,
2627
- Number.parseInt(event.currentTarget.value, 10) ||
2628
- 0,
2621
+ Number.parseInt(event.currentTarget.value, 10) || 0,
2629
2622
  ),
2630
2623
  )
2631
2624
  visionTypesetting?.onMaxFixesPerIssueChange?.(value)
@@ -158,7 +158,9 @@ function CrossfadeFrame({
158
158
  useEffect(() => {
159
159
  const top = layers[layers.length - 1]
160
160
  if (!html || top?.html === html) return
161
- targetScroll.current = readFrameScroll(top ? frames.current.get(top.id) ?? null : null)
161
+ targetScroll.current = readFrameScroll(
162
+ top ? frames.current.get(top.id) ?? null : null,
163
+ )
162
164
  setTopVisible(false)
163
165
  setLayers(prev => [...prev, { id: nextId.current++, html }])
164
166
  }, [html, layers])
@@ -226,7 +228,10 @@ function CrossfadeFrame({
226
228
  )
227
229
  }
228
230
 
229
- function annotationMessage({ note, imageDataUrl }: ScreenAnnotationAgentPayload): string {
231
+ function annotationMessage({
232
+ note,
233
+ imageDataUrl,
234
+ }: ScreenAnnotationAgentPayload): string {
230
235
  return [
231
236
  'Please use this paged proof annotation to adjust the print/typesetting CSS, then explain what changed and ask the user to preview again.',
232
237
  '',
@@ -338,7 +343,13 @@ export function PagedProofDialog({
338
343
 
339
344
  return (
340
345
  <>
341
- <Modal open={open} onClose={onClose} title={title} size="fullscreen" chrome>
346
+ <Modal
347
+ open={open}
348
+ onClose={onClose}
349
+ title={title}
350
+ size="fullscreen"
351
+ chrome
352
+ >
342
353
  <StyledBody>
343
354
  <StyledToolbar>
344
355
  <Text as="p" size="sm" tone="secondary">
@@ -404,7 +415,9 @@ export function PagedProofDialog({
404
415
  <StyledIssueList>
405
416
  <Text as="p" size="sm" tone="secondary">
406
417
  {issues.length
407
- ? `${issues.length} issue${issues.length === 1 ? '' : 's'} ready`
418
+ ? `${issues.length} issue${
419
+ issues.length === 1 ? '' : 's'
420
+ } ready`
408
421
  : issueStatus}
409
422
  </Text>
410
423
  {issues.map((issue, index) => (
@@ -9,6 +9,7 @@ export interface EditorViewModeToggleProps {
9
9
  disabled?: boolean
10
10
  className?: string
11
11
  variant?: 'subtle' | 'text'
12
+ iconOnly?: boolean
12
13
  }
13
14
 
14
15
  export function EditorViewModeToggle({
@@ -17,9 +18,11 @@ export function EditorViewModeToggle({
17
18
  disabled = false,
18
19
  className,
19
20
  variant = 'subtle',
21
+ iconOnly = false,
20
22
  }: EditorViewModeToggleProps): React.ReactElement {
21
23
  const nextMode: EditorViewMode = mode === 'page' ? 'boxed' : 'page'
22
24
  const Icon = mode === 'page' ? PanelTop : Focus
25
+ const label = mode === 'page' ? 'Show app tools' : 'Enter quiet mode'
23
26
  return (
24
27
  <Button
25
28
  type="button"
@@ -28,11 +31,12 @@ export function EditorViewModeToggle({
28
31
  className={className}
29
32
  disabled={disabled}
30
33
  aria-pressed={mode === 'page'}
31
- title={mode === 'page' ? 'Show app tools' : 'Enter quiet mode'}
34
+ aria-label={label}
35
+ title={label}
32
36
  onClick={() => onChange(nextMode)}
33
37
  >
34
38
  <Icon size={14} strokeWidth={1.8} aria-hidden="true" />
35
- {mode === 'page' ? 'App mode' : 'Quiet mode'}
39
+ {iconOnly ? null : mode === 'page' ? 'App mode' : 'Quiet mode'}
36
40
  </Button>
37
41
  )
38
42
  }
@@ -58,6 +58,11 @@ export {
58
58
  parseAutoReviewFindingResponse,
59
59
  runAutoReview,
60
60
  validateAutoReviewFindings,
61
+ DocumentToolbarIcon,
62
+ ToolbarActionButton,
63
+ ToolbarActionDivider,
64
+ ToolbarActionIconButton,
65
+ ToolbarActionStrip,
61
66
  ToolbarInlineToolButton,
62
67
  } from '@puredesktop/platform-editor'
63
68
  export type {
@@ -84,6 +89,7 @@ export type {
84
89
  EditorCommentPromptProps,
85
90
  EditorLinkPromptProps,
86
91
  EditorToolbarOverlays,
92
+ ToolbarIconName,
87
93
  SlashCommandItem,
88
94
  SlashCommandRange,
89
95
  SlashCommandRunOptions,
@@ -33,9 +33,12 @@ const PlatformThemeContext = createContext<PlatformThemeContextValue | null>(
33
33
  )
34
34
 
35
35
  function readStoredMode(): PlatformThemeMode {
36
- if (typeof window === 'undefined') return 'dark'
36
+ if (typeof window === 'undefined') return 'light'
37
37
  const stored = window.localStorage.getItem(STORAGE_KEY)
38
- return stored === 'light' || stored === 'dark' ? stored : 'dark'
38
+ if (stored === 'light' || stored === 'dark') return stored
39
+ return window.matchMedia?.('(prefers-color-scheme: dark)').matches
40
+ ? 'dark'
41
+ : 'light'
39
42
  }
40
43
 
41
44
  function themeForMode(mode: PlatformThemeMode): PlatformTheme {
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { appAccentForSlug, applyAppAccentToTheme } from './appAccents.js'
3
+ import { lightTheme } from './themes/light.js'
4
+
5
+ describe('appAccents', () => {
6
+ it('allocates identity hues to all suite apps', () => {
7
+ expect(appAccentForSlug('teams')?.accent).toBe('oklch(0.58 0.13 8)')
8
+ expect(appAccentForSlug('vibes')?.accent).toBe('oklch(0.50 0.14 26)')
9
+ expect(appAccentForSlug('mail')?.accent).toBe('oklch(0.60 0.12 44)')
10
+ expect(appAccentForSlug('files')?.accent).toBe('oklch(0.50 0.13 64)')
11
+ expect(appAccentForSlug('slides')?.accent).toBe('oklch(0.61 0.11 84)')
12
+ expect(appAccentForSlug('writer')?.accent).toBe('oklch(0.50 0.12 102)')
13
+ expect(appAccentForSlug('manuscript')?.accent).toBe('oklch(0.61 0.12 124)')
14
+ expect(appAccentForSlug('book')?.accent).toBe('oklch(0.48 0.14 148)')
15
+ expect(appAccentForSlug('reports')?.accent).toBe('oklch(0.61 0.12 166)')
16
+ expect(appAccentForSlug('chart')?.accent).toBe('oklch(0.48 0.13 184)')
17
+ expect(appAccentForSlug('knowledge')?.accent).toBe('oklch(0.46 0.11 198)')
18
+ expect(appAccentForSlug('research')?.accent).toBe('oklch(0.63 0.12 222)')
19
+ expect(appAccentForSlug('sheets')?.accent).toBe('oklch(0.50 0.13 244)')
20
+ expect(appAccentForSlug('calendar')?.accent).toBe('oklch(0.63 0.13 258)')
21
+ expect(appAccentForSlug('desktop')?.accent).toBe('oklch(0.48 0.13 274)')
22
+ expect(appAccentForSlug('plan')?.accent).toBe('oklch(0.61 0.14 290)')
23
+ expect(appAccentForSlug('review')?.accent).toBe('oklch(0.50 0.15 308)')
24
+ expect(appAccentForSlug('tasks')?.accent).toBe('oklch(0.63 0.15 324)')
25
+ expect(appAccentForSlug('assistant')?.accent).toBe('oklch(0.50 0.15 340)')
26
+ expect(appAccentForSlug('people')?.accent).toBe('oklch(0.61 0.14 356)')
27
+ })
28
+
29
+ it('exposes the deep block tone for large fills', () => {
30
+ expect(appAccentForSlug('research')?.accentBlock).toBe(
31
+ 'oklch(0.40 0.06 222)',
32
+ )
33
+ expect(appAccentForSlug('tasks')?.accentBlock).toBe('oklch(0.40 0.06 324)')
34
+ })
35
+
36
+ it('normalizes pure-prefixed slugs', () => {
37
+ expect(appAccentForSlug('purefiles')?.accent).toBe(
38
+ appAccentForSlug('files')?.accent,
39
+ )
40
+ expect(appAccentForSlug('pureknowledge')?.accent).toBe(
41
+ appAccentForSlug('knowledge')?.accent,
42
+ )
43
+ expect(appAccentForSlug('pureslides')?.accent).toBe(
44
+ appAccentForSlug('slides')?.accent,
45
+ )
46
+ })
47
+
48
+ it('does not mutate the platform theme accent', () => {
49
+ expect(applyAppAccentToTheme(lightTheme, 'tasks')).toBe(lightTheme)
50
+ })
51
+ })
@@ -1,115 +1,178 @@
1
1
  import type { PlatformTheme } from './tokens.js'
2
2
 
3
3
  /**
4
- * Per-app accent identity, owned centrally by the shared UI (not by each app).
5
- *
6
- * The brand system assigns every app one hue ~28–32° apart so the suite reads
7
- * as a family; opening a different app should feel like being "somewhere else".
8
- * The shell derives each app's accent from this map and injects it into that
9
- * app's theme bridge payload, so no app ships its own brand CSS.
4
+ * App identity is a chrome-only layer. It is exposed through CSS variables
5
+ * (`--app-acc`, `--app-bg`, `--app-text`, `--app-block`) and deliberately
6
+ * does not override `--platform-colors-accent`, which belongs to theme/status
7
+ * meaning.
10
8
  */
11
9
 
12
- /** App slug → accent hue in degrees (PURE_BRAND_SYSTEM §2.2). */
13
- export const APP_ACCENT_HUES: Record<string, number> = {
14
- purefiles: 70,
15
- files: 70,
16
- writer: 212,
17
- paper: 200,
18
- book: 150,
19
- chat: 232,
20
- tasks: 8,
21
- review: 265,
22
- knowledge: 38,
23
- slides: 295,
24
- mail: 232,
25
- chart: 200,
26
- reports: 325,
27
- vibes: 348,
28
- today: 38,
29
- calendar: 175,
30
- teams: 188,
31
- pages: 265,
32
- frames: 10,
33
- sheets: 115,
34
- }
35
-
36
- /**
37
- * Pale hues (~30–180°) need a darker accent text so it stays AA-legible on
38
- * light tints — the one per-app variable besides hue (PURE_BRAND_SYSTEM §2.2).
39
- */
40
- function accentTextLightness(hue: number): number {
41
- return hue >= 30 && hue <= 180 ? 31 : 42
42
- }
43
-
44
- function darkAccentFillLightness(hue: number): number {
45
- return hue >= 30 && hue <= 220 ? 30 : 38
46
- }
47
-
48
10
  export interface DerivedAppAccent {
49
11
  accent: string
50
12
  accentHover: string
51
13
  accentMuted: string
52
14
  accentText: string
15
+ accentBlock: string
53
16
  }
54
17
 
55
- /** Derive the accent token set from a single hue. */
56
- export function deriveAppAccent(hue: number): DerivedAppAccent {
57
- return {
58
- accent: `hsl(${hue}, 50%, 49%)`,
59
- accentHover: `hsl(${hue}, 50%, 42%)`,
60
- accentMuted: `hsl(${hue}, 42%, 95%)`,
61
- accentText: `hsl(${hue}, 48%, ${accentTextLightness(hue)}%)`,
62
- }
18
+ const APP_IDENTITY_ACCENTS: Record<string, DerivedAppAccent> = {
19
+ teams: {
20
+ accent: 'oklch(0.58 0.13 8)',
21
+ accentHover: 'oklch(0.40 0.06 8)',
22
+ accentMuted: 'oklch(0.955 0.03 8)',
23
+ accentText: 'oklch(0.40 0.12 8)',
24
+ accentBlock: 'oklch(0.40 0.06 8)',
25
+ },
26
+ vibes: {
27
+ accent: 'oklch(0.50 0.14 26)',
28
+ accentHover: 'oklch(0.40 0.06 26)',
29
+ accentMuted: 'oklch(0.955 0.03 26)',
30
+ accentText: 'oklch(0.40 0.12 26)',
31
+ accentBlock: 'oklch(0.40 0.06 26)',
32
+ },
33
+ mail: {
34
+ accent: 'oklch(0.60 0.12 44)',
35
+ accentHover: 'oklch(0.40 0.06 44)',
36
+ accentMuted: 'oklch(0.93 0.05 44)',
37
+ accentText: 'oklch(0.40 0.12 44)',
38
+ accentBlock: 'oklch(0.40 0.06 44)',
39
+ },
40
+ files: {
41
+ accent: 'oklch(0.50 0.13 64)',
42
+ accentHover: 'oklch(0.40 0.06 64)',
43
+ accentMuted: 'oklch(0.955 0.03 64)',
44
+ accentText: 'oklch(0.40 0.12 64)',
45
+ accentBlock: 'oklch(0.40 0.06 64)',
46
+ },
47
+ slides: {
48
+ accent: 'oklch(0.61 0.11 84)',
49
+ accentHover: 'oklch(0.40 0.06 84)',
50
+ accentMuted: 'oklch(0.955 0.03 84)',
51
+ accentText: 'oklch(0.40 0.11 84)',
52
+ accentBlock: 'oklch(0.40 0.06 84)',
53
+ },
54
+ writer: {
55
+ accent: 'oklch(0.50 0.12 102)',
56
+ accentHover: 'oklch(0.40 0.06 102)',
57
+ accentMuted: 'oklch(0.955 0.03 102)',
58
+ accentText: 'oklch(0.40 0.12 102)',
59
+ accentBlock: 'oklch(0.40 0.06 102)',
60
+ },
61
+ manuscript: {
62
+ accent: 'oklch(0.61 0.12 124)',
63
+ accentHover: 'oklch(0.40 0.06 124)',
64
+ accentMuted: 'oklch(0.955 0.03 124)',
65
+ accentText: 'oklch(0.40 0.12 124)',
66
+ accentBlock: 'oklch(0.40 0.06 124)',
67
+ },
68
+ book: {
69
+ accent: 'oklch(0.48 0.14 148)',
70
+ accentHover: 'oklch(0.40 0.06 148)',
71
+ accentMuted: 'oklch(0.955 0.03 148)',
72
+ accentText: 'oklch(0.40 0.12 148)',
73
+ accentBlock: 'oklch(0.40 0.06 148)',
74
+ },
75
+ reports: {
76
+ accent: 'oklch(0.61 0.12 166)',
77
+ accentHover: 'oklch(0.40 0.06 166)',
78
+ accentMuted: 'oklch(0.955 0.03 166)',
79
+ accentText: 'oklch(0.40 0.12 166)',
80
+ accentBlock: 'oklch(0.40 0.06 166)',
81
+ },
82
+ chart: {
83
+ accent: 'oklch(0.48 0.13 184)',
84
+ accentHover: 'oklch(0.40 0.06 184)',
85
+ accentMuted: 'oklch(0.955 0.03 184)',
86
+ accentText: 'oklch(0.40 0.12 184)',
87
+ accentBlock: 'oklch(0.40 0.06 184)',
88
+ },
89
+ knowledge: {
90
+ accent: 'oklch(0.46 0.11 198)',
91
+ accentHover: 'oklch(0.40 0.06 198)',
92
+ accentMuted: 'oklch(0.955 0.03 198)',
93
+ accentText: 'oklch(0.40 0.11 198)',
94
+ accentBlock: 'oklch(0.40 0.06 198)',
95
+ },
96
+ research: {
97
+ accent: 'oklch(0.63 0.12 222)',
98
+ accentHover: 'oklch(0.40 0.06 222)',
99
+ accentMuted: 'oklch(0.955 0.03 222)',
100
+ accentText: 'oklch(0.40 0.12 222)',
101
+ accentBlock: 'oklch(0.40 0.06 222)',
102
+ },
103
+ sheets: {
104
+ accent: 'oklch(0.50 0.13 244)',
105
+ accentHover: 'oklch(0.40 0.06 244)',
106
+ accentMuted: 'oklch(0.955 0.03 244)',
107
+ accentText: 'oklch(0.40 0.12 244)',
108
+ accentBlock: 'oklch(0.40 0.06 244)',
109
+ },
110
+ calendar: {
111
+ accent: 'oklch(0.63 0.13 258)',
112
+ accentHover: 'oklch(0.40 0.06 258)',
113
+ accentMuted: 'oklch(0.955 0.03 258)',
114
+ accentText: 'oklch(0.40 0.12 258)',
115
+ accentBlock: 'oklch(0.40 0.06 258)',
116
+ },
117
+ desktop: {
118
+ accent: 'oklch(0.48 0.13 274)',
119
+ accentHover: 'oklch(0.40 0.06 274)',
120
+ accentMuted: 'oklch(0.955 0.03 274)',
121
+ accentText: 'oklch(0.40 0.12 274)',
122
+ accentBlock: 'oklch(0.40 0.06 274)',
123
+ },
124
+ plan: {
125
+ accent: 'oklch(0.61 0.14 290)',
126
+ accentHover: 'oklch(0.40 0.06 290)',
127
+ accentMuted: 'oklch(0.955 0.03 290)',
128
+ accentText: 'oklch(0.40 0.12 290)',
129
+ accentBlock: 'oklch(0.40 0.06 290)',
130
+ },
131
+ review: {
132
+ accent: 'oklch(0.50 0.15 308)',
133
+ accentHover: 'oklch(0.40 0.06 308)',
134
+ accentMuted: 'oklch(0.955 0.03 308)',
135
+ accentText: 'oklch(0.40 0.12 308)',
136
+ accentBlock: 'oklch(0.40 0.06 308)',
137
+ },
138
+ tasks: {
139
+ accent: 'oklch(0.63 0.15 324)',
140
+ accentHover: 'oklch(0.40 0.06 324)',
141
+ accentMuted: 'oklch(0.955 0.03 324)',
142
+ accentText: 'oklch(0.40 0.12 324)',
143
+ accentBlock: 'oklch(0.40 0.06 324)',
144
+ },
145
+ assistant: {
146
+ accent: 'oklch(0.50 0.15 340)',
147
+ accentHover: 'oklch(0.40 0.06 340)',
148
+ accentMuted: 'oklch(0.955 0.03 340)',
149
+ accentText: 'oklch(0.40 0.12 340)',
150
+ accentBlock: 'oklch(0.40 0.06 340)',
151
+ },
152
+ people: {
153
+ accent: 'oklch(0.61 0.14 356)',
154
+ accentHover: 'oklch(0.40 0.06 356)',
155
+ accentMuted: 'oklch(0.955 0.03 356)',
156
+ accentText: 'oklch(0.40 0.12 356)',
157
+ accentBlock: 'oklch(0.40 0.06 356)',
158
+ },
63
159
  }
64
160
 
65
161
  export function appAccentForSlug(
66
162
  slug: string | null | undefined,
67
163
  ): DerivedAppAccent | null {
68
164
  if (!slug) return null
69
- const hue = APP_ACCENT_HUES[slug]
70
- return typeof hue === 'number' ? deriveAppAccent(hue) : null
165
+ return APP_IDENTITY_ACCENTS[slug.replace(/^pure/i, '')] ?? null
71
166
  }
72
167
 
73
168
  /**
74
- * Overlay an app's accent hue onto the platform theme. Applies in both modes —
75
- * the accent is the app's identity, used structurally (active tab, selection,
76
- * focus, links), never as a data-label fill.
169
+ * Kept for compatibility with existing shell composition calls. Identity is
170
+ * now applied by CSS variables so theme/status accent remains untouched.
77
171
  */
78
172
  export function applyAppAccentToTheme(
79
173
  theme: PlatformTheme,
80
174
  slug: string | null | undefined,
81
175
  ): PlatformTheme {
82
- const accent = appAccentForSlug(slug)
83
- if (!accent) return theme
84
- const hue = APP_ACCENT_HUES[slug ?? '']
85
- const darkAccent =
86
- typeof hue === 'number'
87
- ? {
88
- accent: `hsl(${hue}, 48%, ${darkAccentFillLightness(hue)}%)`,
89
- accentHover: `hsl(${hue}, 48%, ${darkAccentFillLightness(hue) + 4}%)`,
90
- accentMuted: `hsl(${hue}, 34%, 22%)`,
91
- }
92
- : null
93
- return {
94
- ...theme,
95
- colors: {
96
- ...theme.colors,
97
- accent:
98
- theme.mode === 'dark'
99
- ? darkAccent?.accent ?? accent.accent
100
- : accent.accent,
101
- accentHover:
102
- theme.mode === 'dark'
103
- ? darkAccent?.accentHover ?? accent.accentHover
104
- : accent.accentHover,
105
- accentMuted:
106
- theme.mode === 'dark'
107
- ? darkAccent?.accentMuted ?? accent.accentMuted
108
- : accent.accentMuted,
109
- focus:
110
- theme.mode === 'dark'
111
- ? darkAccent?.accent ?? accent.accent
112
- : accent.accent,
113
- },
114
- }
176
+ void slug
177
+ return theme
115
178
  }