canopycms 0.2.0-int.121 → 0.3.0

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 (79) hide show
  1. package/CHANGELOG.md +241 -78
  2. package/dist/api/branch.d.ts +2 -2
  3. package/dist/cli/cli.js +0 -18
  4. package/dist/cli/generate-ai-content.js +0 -6
  5. package/dist/cli/init.js +0 -6
  6. package/dist/dev-content-watcher.js +8 -1
  7. package/dist/editor/BranchManager.d.ts +2 -0
  8. package/dist/editor/BranchManager.js +3 -3
  9. package/dist/editor/Editor.js +276 -130
  10. package/dist/editor/EntryNavigator.d.ts +44 -9
  11. package/dist/editor/EntryNavigator.js +278 -104
  12. package/dist/editor/branch-lock.d.ts +42 -0
  13. package/dist/editor/branch-lock.js +79 -0
  14. package/dist/editor/branch-status.d.ts +0 -6
  15. package/dist/editor/branch-status.js +0 -10
  16. package/dist/editor/components/BranchNotAvailableNotice.d.ts +11 -0
  17. package/dist/editor/components/BranchNotAvailableNotice.js +10 -0
  18. package/dist/editor/components/BranchPill.d.ts +3 -18
  19. package/dist/editor/components/BranchPill.js +7 -10
  20. package/dist/editor/components/BranchRowLock.d.ts +3 -1
  21. package/dist/editor/components/BranchRowLock.js +4 -2
  22. package/dist/editor/components/EditorHeader.d.ts +24 -52
  23. package/dist/editor/components/EditorHeader.js +151 -71
  24. package/dist/editor/components/EditorRail.d.ts +8 -8
  25. package/dist/editor/components/EditorRail.js +7 -5
  26. package/dist/editor/components/EntryBreadcrumb.d.ts +5 -1
  27. package/dist/editor/components/EntryBreadcrumb.js +3 -3
  28. package/dist/editor/components/EntryHeader.d.ts +60 -0
  29. package/dist/editor/components/EntryHeader.js +45 -0
  30. package/dist/editor/components/EntrySaveButton.d.ts +24 -0
  31. package/dist/editor/components/EntrySaveButton.js +101 -0
  32. package/dist/editor/components/NoEditPermissionNotice.d.ts +1 -1
  33. package/dist/editor/components/NoEditPermissionNotice.js +1 -1
  34. package/dist/editor/components/RetryToastMessage.d.ts +3 -1
  35. package/dist/editor/components/RetryToastMessage.js +8 -4
  36. package/dist/editor/components/SaveAllButton.d.ts +25 -0
  37. package/dist/editor/components/SaveAllButton.js +81 -0
  38. package/dist/editor/components/UnsavedMark.d.ts +1 -1
  39. package/dist/editor/components/UnsavedMark.js +1 -1
  40. package/dist/editor/components/index.d.ts +1 -1
  41. package/dist/editor/components/index.js +1 -1
  42. package/dist/editor/copy.d.ts +0 -1
  43. package/dist/editor/copy.js +0 -1
  44. package/dist/editor/editor-utils.d.ts +2 -2
  45. package/dist/editor/editor-utils.js +2 -2
  46. package/dist/editor/entry-search.d.ts +10 -16
  47. package/dist/editor/entry-search.js +4 -24
  48. package/dist/editor/hooks/editor-history.d.ts +7 -0
  49. package/dist/editor/hooks/editor-history.js +80 -0
  50. package/dist/editor/hooks/useBranchActions.js +3 -1
  51. package/dist/editor/hooks/useBranchManager.d.ts +7 -3
  52. package/dist/editor/hooks/useBranchManager.js +18 -11
  53. package/dist/editor/hooks/useEntryManager.d.ts +12 -2
  54. package/dist/editor/hooks/useEntryManager.js +141 -20
  55. package/dist/editor/shell/ActivityRail.d.ts +4 -0
  56. package/dist/editor/shell/ActivityRail.js +13 -4
  57. package/dist/editor/shell/EditorShell.d.ts +6 -7
  58. package/dist/editor/shell/EditorShell.js +9 -13
  59. package/dist/editor/shell/PaneLayout.d.ts +1 -0
  60. package/dist/editor/shell/PaneLayout.js +23 -5
  61. package/dist/editor/shell/SidePanelHost.d.ts +11 -4
  62. package/dist/editor/shell/SidePanelHost.js +95 -7
  63. package/dist/editor/shell/TopBar.d.ts +4 -4
  64. package/dist/editor/shell/TopBar.js +6 -3
  65. package/dist/editor/theme.js +22 -1
  66. package/dist/editor/utils/focus-return.d.ts +10 -2
  67. package/dist/editor/utils/focus-return.js +40 -6
  68. package/dist/operating-mode/client-safe-strategy.d.ts +1 -6
  69. package/dist/operating-mode/client-safe-strategy.js +1 -10
  70. package/dist/operating-mode/types.d.ts +0 -3
  71. package/dist/version.d.ts +1 -1
  72. package/dist/version.js +1 -1
  73. package/package.json +1 -1
  74. package/dist/editor/components/BranchLockBanner.d.ts +0 -23
  75. package/dist/editor/components/BranchLockBanner.js +0 -66
  76. package/dist/editor/components/QuickOpenPalette.d.ts +0 -28
  77. package/dist/editor/components/QuickOpenPalette.js +0 -71
  78. package/dist/editor/components/SaveSplitButton.d.ts +0 -31
  79. package/dist/editor/components/SaveSplitButton.js +0 -121
@@ -11,7 +11,7 @@ import type { UserProfile } from '../auth/types.js';
11
11
  * symmetric: the editor client now defaults every missing flag fail-closed
12
12
  * (`?? true`), so a NEW client talking to an OLD server that omits these
13
13
  * fields degrades to fully locked -- read-only, Submit hidden, and (see
14
- * EditorHeader's `branchDataUnavailable`) a "could not be loaded" banner --
14
+ * `branchLockState`'s `branchDataUnavailable`) a "could not be loaded" status --
15
15
  * rather than silently behaving as if nothing changed. That is by design:
16
16
  * wire compatibility here means "doesn't break", not "behaves the same".
17
17
  */
@@ -22,7 +22,7 @@ export interface BranchListItem extends BranchMetadata {
22
22
  * True when content writes are rejected -- base-branch read-only OR a status
23
23
  * lock (submitted/approved/archived). The editor renders its locked state off
24
24
  * this instead of re-deriving the status rule client-side; `readOnly` still
25
- * distinguishes WHICH lock applies, for banner copy.
25
+ * distinguishes WHICH lock applies, for the status copy.
26
26
  */
27
27
  writeBlocked?: boolean;
28
28
  /**
package/dist/cli/cli.js CHANGED
@@ -1938,9 +1938,6 @@ var init_init = __esm({
1938
1938
  supportsBranching() {
1939
1939
  return true;
1940
1940
  }
1941
- supportsStatusBadge() {
1942
- return true;
1943
- }
1944
1941
  supportsComments() {
1945
1942
  return true;
1946
1943
  }
@@ -1967,9 +1964,6 @@ var init_init = __esm({
1967
1964
  supportsBranching() {
1968
1965
  return true;
1969
1966
  }
1970
- supportsStatusBadge() {
1971
- return true;
1972
- }
1973
1967
  supportsComments() {
1974
1968
  return true;
1975
1969
  }
@@ -8401,9 +8395,6 @@ var init_generate_ai_content = __esm({
8401
8395
  supportsBranching() {
8402
8396
  return true;
8403
8397
  }
8404
- supportsStatusBadge() {
8405
- return true;
8406
- }
8407
8398
  supportsComments() {
8408
8399
  return true;
8409
8400
  }
@@ -8430,9 +8421,6 @@ var init_generate_ai_content = __esm({
8430
8421
  supportsBranching() {
8431
8422
  return true;
8432
8423
  }
8433
- supportsStatusBadge() {
8434
- return true;
8435
- }
8436
8424
  supportsComments() {
8437
8425
  return true;
8438
8426
  }
@@ -15556,9 +15544,6 @@ var init_client_safe_strategy2 = __esm({
15556
15544
  supportsBranching() {
15557
15545
  return true;
15558
15546
  }
15559
- supportsStatusBadge() {
15560
- return true;
15561
- }
15562
15547
  supportsComments() {
15563
15548
  return true;
15564
15549
  }
@@ -15585,9 +15570,6 @@ var init_client_safe_strategy2 = __esm({
15585
15570
  supportsBranching() {
15586
15571
  return true;
15587
15572
  }
15588
- supportsStatusBadge() {
15589
- return true;
15590
- }
15591
15573
  supportsComments() {
15592
15574
  return true;
15593
15575
  }
@@ -1025,9 +1025,6 @@ var init_client_safe_strategy = __esm({
1025
1025
  supportsBranching() {
1026
1026
  return true;
1027
1027
  }
1028
- supportsStatusBadge() {
1029
- return true;
1030
- }
1031
1028
  supportsComments() {
1032
1029
  return true;
1033
1030
  }
@@ -1054,9 +1051,6 @@ var init_client_safe_strategy = __esm({
1054
1051
  supportsBranching() {
1055
1052
  return true;
1056
1053
  }
1057
- supportsStatusBadge() {
1058
- return true;
1059
- }
1060
1054
  supportsComments() {
1061
1055
  return true;
1062
1056
  }
package/dist/cli/init.js CHANGED
@@ -1512,9 +1512,6 @@ var ProdClientSafeStrategy = class {
1512
1512
  supportsBranching() {
1513
1513
  return true;
1514
1514
  }
1515
- supportsStatusBadge() {
1516
- return true;
1517
- }
1518
1515
  supportsComments() {
1519
1516
  return true;
1520
1517
  }
@@ -1541,9 +1538,6 @@ var DevClientSafeStrategy = class {
1541
1538
  supportsBranching() {
1542
1539
  return true;
1543
1540
  }
1544
- supportsStatusBadge() {
1545
- return true;
1546
- }
1547
1541
  supportsComments() {
1548
1542
  return true;
1549
1543
  }
@@ -194,7 +194,14 @@ export function startDevContentWatcher(services, options = {}) {
194
194
  }
195
195
  }
196
196
  };
197
- const watcher = chokidar.watch(workingTreeContentDir, { ignoreInitial: true });
197
+ // A save can truncate and then write. chokidar's 50 ms per-path throttle passes only the first
198
+ // `change`, which can land between the two steps: a check then reads a half-written file and
199
+ // reports a divergence nothing retracts. `awaitWriteFinish` emits only once the file's size has
200
+ // held still, and bypasses that throttle.
201
+ const watcher = chokidar.watch(workingTreeContentDir, {
202
+ ignoreInitial: true,
203
+ awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 25 },
204
+ });
198
205
  watcher.on('add', () => void check());
199
206
  watcher.on('change', () => void check());
200
207
  watcher.on('unlink', () => void check());
@@ -70,6 +70,8 @@ export interface BranchManagerProps {
70
70
  currentBranchName?: string;
71
71
  /** Why the open branch's Submit or Withdraw must wait, such as a Save all still writing it. */
72
72
  currentWorkflowBlockedReason?: string;
73
+ /** Opens on the new-branch form, its name field focused, as New branch… in the branch bar asks. */
74
+ startCreating?: boolean;
73
75
  onSelect?: (name: string) => void;
74
76
  /** Awaited before the spinner starts, so a confirmation it shows never reads as a hung create. `false` abandons the create. */
75
77
  onBeforeCreate?: () => Promise<boolean>;
@@ -168,9 +168,9 @@ const BranchCard = ({ branch: b, isCurrent, workflowBlockedReason, user, onSelec
168
168
  ? 'This branch is in review. Withdraw it from review before deleting it.'
169
169
  : undefined, onClick: () => onDelete?.(b.name), children: "Delete branch" })] }))] })] }))] })] }) }));
170
170
  };
171
- export const BranchManager = ({ branches, mode, user, currentBranchName, currentWorkflowBlockedReason, onSelect, onBeforeCreate, onCreate, onDelete, onSubmit, onWithdraw, onRequestChanges, onClose: _, comments = [], currentUserId, canResolve = false, onAddComment, onResolveThread, highlightThreadId, onGetUserMetadata, onGetOwnerMetadata, onGetCommentUserMetadata, }) => {
171
+ export const BranchManager = ({ branches, mode, user, currentBranchName, currentWorkflowBlockedReason, startCreating = false, onSelect, onBeforeCreate, onCreate, onDelete, onSubmit, onWithdraw, onRequestChanges, onClose: _, comments = [], currentUserId, canResolve = false, onAddComment, onResolveThread, highlightThreadId, onGetUserMetadata, onGetOwnerMetadata, onGetCommentUserMetadata, }) => {
172
172
  const supportsBranching = clientOperatingStrategy(mode).supportsBranching();
173
- const [showCreateForm, setShowCreateForm] = useState(false);
173
+ const [showCreateForm, setShowCreateForm] = useState(startCreating);
174
174
  const [newBranchName, setNewBranchName] = useState('');
175
175
  const [newBranchTitle, setNewBranchTitle] = useState('');
176
176
  const [newBranchDescription, setNewBranchDescription] = useState('');
@@ -209,5 +209,5 @@ export const BranchManager = ({ branches, mode, user, currentBranchName, current
209
209
  ]
210
210
  : branches;
211
211
  const hasBranchThreads = comments.some((t) => t.type === 'branch');
212
- return (_jsxs(Stack, { h: "100%", style: { display: 'flex', flexDirection: 'column' }, gap: 0, "data-testid": "branch-manager", children: [currentUserId && onAddComment && onResolveThread && hasBranchThreads && (_jsx(Stack, { pt: "sm", children: _jsx(BranchComments, { comments: comments, currentUserId: currentUserId, canResolve: canResolve, onAddComment: onAddComment, onResolveThread: onResolveThread, highlightThreadId: highlightThreadId, onGetUserMetadata: onGetCommentUserMetadata }) })), !!supportsBranching && (_jsxs(Stack, { gap: "sm", pt: "sm", children: [_jsx(Button, { variant: "light", size: "sm", fullWidth: true, onClick: () => setShowCreateForm(!showCreateForm), disabled: creating, "data-testid": "create-branch-button", children: showCreateForm ? 'Cancel' : `${EDITOR_ACTIONS.newBranch}…` }), _jsx(Collapse, { in: showCreateForm, children: _jsx(Paper, { withBorder: true, p: "md", children: _jsxs(Stack, { gap: "sm", children: [_jsx(TextInput, { label: "Branch name", placeholder: "feature/my-branch", value: newBranchName, onChange: (e) => setNewBranchName(e.target.value), required: true, disabled: creating, "data-testid": "branch-name-input" }), _jsx(TextInput, { label: "Title", placeholder: "Brief description", value: newBranchTitle, onChange: (e) => setNewBranchTitle(e.target.value), disabled: creating, "data-testid": "branch-title-input" }), _jsx(Textarea, { label: "Description", placeholder: "Detailed description of the changes", value: newBranchDescription, onChange: (e) => setNewBranchDescription(e.target.value), minRows: 2, disabled: creating, "data-testid": "branch-description-textarea" }), _jsx(Button, { size: "sm", onClick: handleCreate, disabled: !newBranchName.trim(), loading: creating, fullWidth: true, "data-testid": "create-branch-submit", children: "Create branch" })] }) }) })] })), _jsx(ScrollArea, { style: { flex: 1 }, pb: "md", children: branches.length === 0 ? (_jsx(Text, { size: "sm", c: "dimmed", py: "md", children: !supportsBranching ? 'Branch management is not available.' : 'No branches available.' })) : (_jsx(Stack, { gap: "sm", children: orderedBranches.map((b) => (_jsx(BranchCard, { branch: b, isCurrent: b.name === currentBranchName, workflowBlockedReason: b.name === currentBranchName ? currentWorkflowBlockedReason : undefined, user: user, onSelect: onSelect, onSubmit: onSubmit, onWithdraw: onWithdraw, onRequestChanges: onRequestChanges, onDelete: onDelete, onGetUserMetadata: onGetUserMetadata, onGetOwnerMetadata: onGetOwnerMetadata }, b.name))) })) })] }));
212
+ return (_jsxs(Stack, { h: "100%", style: { display: 'flex', flexDirection: 'column' }, gap: 0, "data-testid": "branch-manager", children: [currentUserId && onAddComment && onResolveThread && hasBranchThreads && (_jsx(Stack, { pt: "sm", children: _jsx(BranchComments, { comments: comments, currentUserId: currentUserId, canResolve: canResolve, onAddComment: onAddComment, onResolveThread: onResolveThread, highlightThreadId: highlightThreadId, onGetUserMetadata: onGetCommentUserMetadata }) })), !!supportsBranching && (_jsxs(Stack, { gap: "sm", pt: "sm", children: [_jsx(Button, { variant: "light", size: "sm", fullWidth: true, onClick: () => setShowCreateForm(!showCreateForm), disabled: creating, "data-testid": "create-branch-button", children: showCreateForm ? 'Cancel' : `${EDITOR_ACTIONS.newBranch}…` }), _jsx(Collapse, { in: showCreateForm, children: _jsx(Paper, { withBorder: true, p: "md", children: _jsxs(Stack, { gap: "sm", children: [_jsx(TextInput, { label: "Branch name", placeholder: "feature/my-branch", value: newBranchName, onChange: (e) => setNewBranchName(e.target.value), required: true, disabled: creating, "data-autofocus": startCreating || undefined, "data-testid": "branch-name-input" }), _jsx(TextInput, { label: "Title", placeholder: "Brief description", value: newBranchTitle, onChange: (e) => setNewBranchTitle(e.target.value), disabled: creating, "data-testid": "branch-title-input" }), _jsx(Textarea, { label: "Description", placeholder: "Detailed description of the changes", value: newBranchDescription, onChange: (e) => setNewBranchDescription(e.target.value), minRows: 2, disabled: creating, "data-testid": "branch-description-textarea" }), _jsx(Button, { size: "sm", onClick: handleCreate, disabled: !newBranchName.trim(), loading: creating, fullWidth: true, "data-testid": "create-branch-submit", children: "Create branch" })] }) }) })] })), _jsx(ScrollArea, { style: { flex: 1 }, pb: "md", children: branches.length === 0 ? (_jsx(Text, { size: "sm", c: "dimmed", py: "md", children: !supportsBranching ? 'Branch management is not available.' : 'No branches available.' })) : (_jsx(Stack, { gap: "sm", children: orderedBranches.map((b) => (_jsx(BranchCard, { branch: b, isCurrent: b.name === currentBranchName, workflowBlockedReason: b.name === currentBranchName ? currentWorkflowBlockedReason : undefined, user: user, onSelect: onSelect, onSubmit: onSubmit, onWithdraw: onWithdraw, onRequestChanges: onRequestChanges, onDelete: onDelete, onGetUserMetadata: onGetUserMetadata, onGetOwnerMetadata: onGetOwnerMetadata }, b.name))) })) })] }));
213
213
  };