@pitcher/js-api 1.30.0 → 1.31.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 (113) hide show
  1. package/js-api.esm.js +80 -17
  2. package/js-api.esm.js.map +1 -1
  3. package/js-api.umd.min.js +15 -15
  4. package/js-api.umd.min.js.map +1 -1
  5. package/lib/apps/browser/components/ContextMenu/TranslateFileButton.test.d.ts +1 -0
  6. package/lib/apps/browser/stores/api.filesFoldersDeleter.test.d.ts +1 -0
  7. package/lib/apps/canvas-builder/components/container/PopupApps/PopupAppModal.spec.d.ts +1 -0
  8. package/lib/apps/canvas-builder/components/container/PresentationTools/PresentationToolsButtons.spec.d.ts +1 -0
  9. package/lib/apps/canvas-builder/components/ui/BottomSheetModal/BottomSheetModal.integration.spec.d.ts +1 -0
  10. package/lib/apps/canvas-builder/components/ui/BottomSheetModal/BottomSheetModal.spec.d.ts +1 -0
  11. package/lib/apps/canvas-builder/components/ui/DynamicContent/section-component-bindings.util.d.ts +32 -0
  12. package/lib/apps/canvas-builder/components/ui/DynamicContent/useDynamicContent.d.ts +4 -0
  13. package/lib/apps/canvas-builder/components/ui/Link/Link.util.d.ts +8 -1
  14. package/lib/apps/canvas-builder/components/ui/Link/Link.util.test.d.ts +1 -0
  15. package/lib/apps/canvas-builder/components/ui/SectionList/SectionItemModal.spec.d.ts +1 -0
  16. package/lib/apps/canvas-builder/composables/useCanvas.d.ts +83 -9
  17. package/lib/apps/canvas-builder/composables/useCanvasBlocks.d.ts +4 -2
  18. package/lib/apps/canvas-builder/composables/useCanvasHistory.d.ts +2 -2
  19. package/lib/apps/canvas-builder/composables/useCanvasTheme.d.ts +2 -2
  20. package/lib/apps/canvas-builder/composables/useNavigatorDrawer.d.ts +25 -0
  21. package/lib/apps/canvas-builder/composables/useNavigatorDrawer.spec.d.ts +1 -0
  22. package/lib/apps/canvas-builder/composables/usePopupApps.d.ts +41 -4
  23. package/lib/apps/canvas-builder/composables/usePopupApps.spec.d.ts +1 -0
  24. package/lib/apps/canvas-builder/composables/usePresentationTools.d.ts +112 -0
  25. package/lib/apps/canvas-builder/composables/usePresentationTools.spec.d.ts +1 -0
  26. package/lib/apps/canvas-builder/types/canvas.d.ts +13 -0
  27. package/lib/apps/canvas-builder/util/canvas.util.d.ts +12 -0
  28. package/lib/apps/canvas-builder/util/completionWizard.util.d.ts +251 -11
  29. package/lib/apps/canvas-builder/util/iframe.util.d.ts +24 -2
  30. package/lib/apps/canvas-builder/util/iframe.util.test.d.ts +1 -0
  31. package/lib/apps/canvas-selector/stores/app.d.ts +14 -0
  32. package/lib/apps/collection-player/components/AppHeader.spec.d.ts +1 -0
  33. package/lib/apps/collection-player/composables/speaker-notes.use.d.ts +14 -0
  34. package/lib/apps/collection-player/composables/speaker-notes.use.spec.d.ts +1 -0
  35. package/lib/apps/collection-player/stores/app.store.d.ts +5 -1
  36. package/lib/apps/collection-player/stores/showNotesToggle.spec.d.ts +1 -0
  37. package/lib/apps/content-selector/appTypes.d.ts +16 -0
  38. package/lib/apps/content-selector/components/Content/AllContent/List/expandFileGuard.spec.d.ts +1 -0
  39. package/lib/apps/notes/NotesApp.accountNotes.spec.d.ts +1 -0
  40. package/lib/apps/notes/NotesApp.speakerNotes.spec.d.ts +1 -0
  41. package/lib/apps/notes/composables/useAccountNotes.d.ts +14 -0
  42. package/lib/apps/notes/composables/useAccountNotes.spec.d.ts +1 -0
  43. package/lib/apps/notes/composables/useAccountOnlyNotesGate.d.ts +21 -0
  44. package/lib/apps/notes/composables/useAccountOnlyNotesGate.spec.d.ts +1 -0
  45. package/lib/apps/notes/composables/useActiveSlideNotes.d.ts +42 -0
  46. package/lib/apps/notes/composables/useActiveSlideNotes.spec.d.ts +1 -0
  47. package/lib/components/CFileAccessManagement/collaborationApi.d.ts +2 -0
  48. package/lib/components/CFilePanel/Details.spec.d.ts +1 -0
  49. package/lib/components/CFilePanel/KeyMessages.spec.d.ts +1 -0
  50. package/lib/components/CFilePanel/Summary.spec.d.ts +1 -0
  51. package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.d.ts +52 -0
  52. package/lib/components/CGlobalLoader/CGlobalLoaderMark.spec.d.ts +1 -0
  53. package/lib/components/CGlobalLoader/globalLoader.paths.d.ts +2 -0
  54. package/lib/components/CGlobalSearch/globalSearchTarget.util.d.ts +31 -0
  55. package/lib/components/CGlobalSearch/globalSearchTarget.util.test.d.ts +1 -0
  56. package/lib/components/CRichTextEditor/extensions/paragraphExtended.d.ts +7 -1
  57. package/lib/components/CRichTextEditor/extensions/paragraphExtended.test.d.ts +1 -0
  58. package/lib/components/canvases/CanvasesManagementBulk.use.d.ts +12 -2
  59. package/lib/components/masterCanvases/CMasterCanvasesManagement.use.d.ts +86 -0
  60. package/lib/components/masterCanvases/CMasterCanvasesManagement.use.spec.d.ts +1 -0
  61. package/lib/components/masterCanvases/CMasterCanvasesPendingShareCell.spec.d.ts +1 -0
  62. package/lib/components/masterCanvases/CMasterCanvasesSharedWithCell.use.d.ts +39 -0
  63. package/lib/components/masterCanvases/CMasterCanvasesSharedWithCell.use.spec.d.ts +1 -0
  64. package/lib/components/masterCanvases/createFromMaster.use.d.ts +255 -0
  65. package/lib/components/masterCanvases/createFromMaster.use.spec.d.ts +1 -0
  66. package/lib/components/masterCanvases/masterCanvasDocuments.use.d.ts +56 -0
  67. package/lib/components/masterCanvases/masterCanvasDocuments.use.spec.d.ts +1 -0
  68. package/lib/components/masterCanvases/masterCanvasShares.use.d.ts +103 -0
  69. package/lib/components/masterCanvases/masterCanvasShares.use.spec.d.ts +1 -0
  70. package/lib/components/masterCanvases/masterFromCanvas.use.d.ts +122 -0
  71. package/lib/components/masterCanvases/masterFromCanvas.use.spec.d.ts +1 -0
  72. package/lib/components/masterCanvases/masterFromTemplate.use.d.ts +50 -0
  73. package/lib/components/masterCanvases/masterFromTemplate.use.spec.d.ts +1 -0
  74. package/lib/components/savedCanvases/CSavedCanvasesManagement.use.d.ts +203 -0
  75. package/lib/components/savedCanvases/savedCanvasDocuments.use.d.ts +111 -0
  76. package/lib/components/savedCanvases/savedCanvasDocuments.use.spec.d.ts +1 -0
  77. package/lib/components/savedCanvases/smartFolders/SmartFolders.use.d.ts +4 -0
  78. package/lib/constants/cdp.const.d.ts +1 -0
  79. package/lib/constants/translationLanguages.const.d.ts +86 -8
  80. package/lib/constants/translationLanguages.const.spec.d.ts +1 -0
  81. package/lib/main.lib.d.ts +5 -0
  82. package/lib/sdk/api/HighLevelApi.d.ts +2 -0
  83. package/lib/sdk/api/modules/appsDb.d.ts +4 -4
  84. package/lib/sdk/api/modules/canvas.d.ts +12 -0
  85. package/lib/sdk/api/modules/ui/types.ui.d.ts +18 -0
  86. package/lib/sdk/interfaces.d.ts +31 -1
  87. package/lib/sdk/main.d.ts +38 -0
  88. package/lib/sdk/payload.types.d.ts +45 -0
  89. package/lib/sdk/platform.d.ts +27 -0
  90. package/lib/sdk/platform.spec.d.ts +1 -0
  91. package/lib/sdk/singleton-scope.spec.d.ts +1 -0
  92. package/lib/sdk/utils/httpFetch.spec.d.ts +1 -0
  93. package/lib/theme/canvasUiUnoPreset.test.d.ts +1 -0
  94. package/lib/types/app.d.ts +55 -1
  95. package/lib/types/canvases.d.ts +15 -0
  96. package/lib/types/launchDarkly.types.d.ts +8 -1
  97. package/lib/types/organizationSettings.types.d.ts +29 -0
  98. package/lib/util/aiSummary.d.ts +30 -0
  99. package/lib/util/aiSummary.spec.d.ts +1 -0
  100. package/lib/util/network.d.ts +35 -0
  101. package/package.json +1 -1
  102. package/types/openapi/models/Canvas.d.ts +1 -0
  103. package/types/openapi/models/CanvasCreate.d.ts +1 -0
  104. package/types/openapi/models/CanvasCreateRequest.d.ts +1 -0
  105. package/types/openapi/models/CanvasRecommendedFile.d.ts +1 -1
  106. package/types/openapi/models/CanvasRetrieve.d.ts +1 -0
  107. package/types/openapi/models/CanvasUpdate.d.ts +1 -0
  108. package/types/openapi/models/CanvasUpdateRequest.d.ts +1 -0
  109. package/types/openapi/models/DeletedCanvas.d.ts +1 -0
  110. package/types/openapi/models/File.d.ts +1 -1
  111. package/types/openapi/models/PatchedCanvasUpdateRequest.d.ts +1 -0
  112. package/types/openapi/models/SharedLink.d.ts +6 -0
  113. package/types/openapi/models/SharedLinkCreateRequest.d.ts +5 -0
@@ -0,0 +1,255 @@
1
+ import { CanvasCreateRequest, CanvasRetrieve } from '../../../types/openapi';
2
+ /**
3
+ * Metadata for a MASTER created from an existing canvas ("Use as master").
4
+ *
5
+ * Strips the source deck's own bookkeeping — the async-download record, so the new master does not
6
+ * offer a download of the SOURCE rendered before it became a master, and `frozen_until`, so a
7
+ * brand-new master is not born read-only.
8
+ *
9
+ * Deliberately KEEPS `pitcher.completion_wizard`, which is what separates this from
10
+ * `buildChildMetadata`. A master is a finished source: block 3 lets the wizard state survive into
11
+ * `context` (`buildMasterContext` prunes identity only), so pruning the `metadata` twin here would
12
+ * desynchronise the two and make the list's Progress column disagree with the wizard itself.
13
+ * Reusing `buildChildMetadata` would have been the intuitive fix and the wrong one.
14
+ */
15
+ export declare function buildMasterMetadata(sourceMetadata?: CanvasRetrieve['metadata'] | null): Record<string, unknown> | undefined;
16
+ /**
17
+ * The create payload for "Use as master pitchdeck" (PIT-7488 block 3).
18
+ *
19
+ * Mirror image of `buildCreateFromMasterPayload`, and it lives beside it for the same reason
20
+ * `buildMasterMetadata` does: the two directions must be read together or they drift.
21
+ *
22
+ * **`owned_by_id` is absent, and that absence is the contract (PIT-8362).** Django declares the
23
+ * field `default=CreateOnlyDefault(CurrentUserDefault())`, so omitting it is precisely what makes
24
+ * the master belong to whoever promoted the deck — the only owner "My Master Pitchdecks" can
25
+ * meaningfully show, since it is a personal, RESTRICTED, owner-scoped tab that the promote flow
26
+ * navigates to on success. Sending the SOURCE deck's owner instead (block 3's original rule,
27
+ * reverted here) put a colleague's master in the promoter's tab, and `CanvasOwnedByFieldValidator`
28
+ * 400s that transfer outright for anyone but an org/instance admin. Do not reintroduce the field.
29
+ *
30
+ * The rest mirrors the source deck, minus what a master must not inherit:
31
+ * - `account: null` + `access_type: RESTRICTED` — masters are account-less internal sources rather
32
+ * than shared content (`resolve_account` nulls the account server-side anyway).
33
+ * - `context` — identity is stripped so every deck built from this master renders the PRESENTING
34
+ * rep in `{{user.name}}`, not the promoter. See `buildMasterContext`.
35
+ * - `metadata` — drops the source's async-download record (its Actions cell would otherwise offer a
36
+ * completed download of the deck as it was BEFORE becoming a master) and `pitcher.frozen_until`
37
+ * (a brand-new master born read-only). Deliberately NOT `buildChildMetadata`: that also drops the
38
+ * completion wizard, which a master keeps, and `buildMasterContext` keeps its `context` twin —
39
+ * the two must agree.
40
+ *
41
+ * - `is_draft: false` — forced, NOT inherited (PIT-8360). Masters have no draft state since the
42
+ * Ready column was dropped from the master tab (validation item #44), so nothing can clear the
43
+ * flag afterwards: a deck promoted while still a draft would produce a permanently-draft master.
44
+ * This keeps the promote path agreeing with the other three master-creation paths in
45
+ * `MasterCanvasesView`, which force it too, rather than leaving `is_draft` set on masters with
46
+ * nothing that sets it FOR them, waiting to bite whoever next writes a filter against it.
47
+ */
48
+ export declare function buildMasterFromCanvasPayload(opts: {
49
+ source: CanvasRetrieve;
50
+ name: string;
51
+ }): Omit<CanvasCreateRequest, 'instance_id'>;
52
+ /**
53
+ * Drops the SOURCE's completion-wizard progress from a context that is otherwise copied verbatim.
54
+ *
55
+ * For the plain Duplicate action, which — unlike create-from-master — legitimately keeps the
56
+ * source's identity: the copy belongs to the same person, for the same account, so stripping
57
+ * `user`/`account` there would blank a deck the rep expects to be a faithful duplicate. The wizard
58
+ * state is different: `isCompletionWizardShowing` is `wizardSteps.length > 0 &&
59
+ * !context.pitcher.completion_wizard.completed`, so inheriting `completed: true` makes the copy
60
+ * open straight into the editor with the wizard silently skipped.
61
+ *
62
+ * Returns `undefined` when nothing survives, so the payload omits the field rather than writing an
63
+ * empty object over the server's default.
64
+ */
65
+ export declare function withoutWizardState(context?: CanvasRetrieve['context'] | null): Record<string, unknown> | undefined;
66
+ /**
67
+ * Context for a DUPLICATE (PIT-7488, peer review round 2).
68
+ *
69
+ * A pitchdeck copy strips the wizard state outright — see `withoutWizardState`. A MASTER copy cannot
70
+ * do either extreme:
71
+ *
72
+ * - Stripping it outright makes the copy look un-run, so the wizard reopens as a fresh `create`
73
+ * seeded from the ADMIN GROUP CONFIG and completing it writes that seed over the curated slides
74
+ * the rep just duplicated.
75
+ * - Keeping it verbatim inherits `completed: true`, which is the switch `isCompletionWizardShowing`
76
+ * reads — the copy then opens straight into the editor and never offers the wizard at all, while
77
+ * its Progress chip (from `metadata`) still says In progress. That desync is the whole reason
78
+ * `withoutWizardState` exists.
79
+ *
80
+ * So a master copy is re-marked as what it actually is: a deck that HAS been through the wizard but
81
+ * is not finished. `react_saved` is exactly that state — `resolveWizardFlow` reads it as `edit` (so
82
+ * the copy restores its OWN content), while `completed` stays falsy so the wizard still shows and
83
+ * the chip still reads In progress, agreeing with `buildChildMetadata`.
84
+ *
85
+ * The marker is added only when the source carries a RESTORABLE artifact — see
86
+ * `hasRestorableArtifact`, which is the sole gate on this path because the edit flow has no
87
+ * downstream `fromArtifact` check. A master with nothing to restore must not claim `react_saved`, or
88
+ * the edit flow restores from an artifact that is not there and reopens the empty-`edit` bug (Q9).
89
+ * `initial_section_list_data` is dropped either way, so the copy re-seeds its candidate baseline
90
+ * from its own content rather than re-offering slides the source deliberately dropped.
91
+ */
92
+ export declare function buildDuplicateContext(context?: CanvasRetrieve['context'] | null, opts?: {
93
+ isMaster?: boolean;
94
+ }): Record<string, unknown> | undefined;
95
+ /**
96
+ * Freezing a master says "this source is locked", not "every deck built from it is locked" — and a
97
+ * brand new deck the rep cannot edit is unusable.
98
+ */
99
+ export declare function buildChildMetadata(masterMetadata?: CanvasRetrieve['metadata'] | null): Record<string, unknown> | undefined;
100
+ /**
101
+ * The child's context: the master's configuration plus this session's identity.
102
+ *
103
+ * Identity is **stripped and re-added** rather than merged over, and that is the load-bearing
104
+ * detail. `getDefaultCrmShape` runs its result through `removeEmptyValues`, so on a non-CRM org
105
+ * (or with no live call) it emits no `account` and no `meeting` at all — a plain merge would then
106
+ * leave whatever the master carried, which is the wrong customer's name on the new deck. Stripping
107
+ * first means the child's identity keys can only ever come from the current session.
108
+ *
109
+ * It cannot rely on block 3 having cleaned the master either: `buildMasterContext` runs once, at
110
+ * creation, and opening a master in the builder afterwards deep-merges live `Account` /
111
+ * `salesforce_data` / `pitcher_data` back into its context. Every master is therefore re-poisonable
112
+ * between creation and use, and this is the last gate before a customer sees the result.
113
+ *
114
+ * `accountName` overrides whatever the CRM shape resolved because `getDefaultCrmShape` prefers the
115
+ * LIVE call's selected account (`call.selectedAccount.Name ?? accountName`). The account chosen in
116
+ * the dialog is the deck's account, so a rep prepping a deck for Beta during a call with Acme must
117
+ * not get Acme's name interpolated into it.
118
+ *
119
+ * The wizard artifact's OWN identity goes too (`withoutWizardIdentity`): the top-level key list
120
+ * never reached `wizard.subtitle`, so the promoter's customer rode onto the child's cover slide via
121
+ * `{{wizard.subtitle.label}}` (PIT-8399). Masters are re-poisonable after creation, so this pass
122
+ * has to run here as well as in `buildMasterContext`.
123
+ *
124
+ * The master's wizard PROGRESS is replaced rather than merely dropped (`pitcher.completion_wizard`,
125
+ * see `WIZARD_STATE_PITCHER_KEY`). Dropping it is what lets the completion wizard run on the new
126
+ * deck — on a `enable_react_completion_wizard` instance, the `pitchdeck-wizard-ng` app — but a deck
127
+ * with NO wizard state at all also reads as never-curated, and the wizard's create flow then
128
+ * reseeds its agenda from the ADMIN GROUP CONFIG, silently dropping every custom agenda point the
129
+ * master carried (PIT-8399, the reported defect). So the progress is reset to the one fact that
130
+ * survives: `from_master`, which says "this deck's content was copied from a curated source, so its
131
+ * own artifact is the authoritative agenda". `resolveWizardAgendaSource` reads it.
132
+ *
133
+ * `from_master` deliberately sets NONE of `completed` / `edited` / `react_saved`, so
134
+ * `resolveWizardFlow` still answers `'create'`: the deck really is being created, the editor still
135
+ * says so, and identity stays with the create flow rather than being restored off the master. That
136
+ * is what separates this from `buildDuplicateContext`, which marks a master COPY `react_saved` and
137
+ * accepts the `'edit'` flow because a duplicate legitimately IS the source. Do not unify the two.
138
+ *
139
+ * **Knock-on the marker's PRESENCE has** (review): `App.vue` computes
140
+ * `hasWizardState: isCompletionWizardEnabled || !!context.pitcher.completion_wizard` as a
141
+ * deliberate proxy for the window before the app roster loads, so a from-master deck now satisfies
142
+ * it from creation onward where it previously had no such key. With
143
+ * `enable_edit_opens_completion_wizard` on, `isWizardOwnedEditSurface` therefore holds the surface
144
+ * for such a deck immediately — correct whenever the deck has wizard steps, which is every normal
145
+ * case, and the marker is only written when the master carried an artifact, which means the wizard
146
+ * had already run on it. The sharp edge is a deck with an artifact and ZERO steps (its SectionList
147
+ * node deleted in the raw builder after the wizard ran): it is demoted out of edit mode with no
148
+ * wizard to open. That edge PREDATES this change — every deck that has ever run the wizard carries
149
+ * the same key — and this only adds one more way to reach it, so it is accepted rather than worked
150
+ * around here. Do not "optimise" the marker away on the assumption it is inert.
151
+ *
152
+ * Written only when the copied context actually still CARRIES an artifact (`context.wizard`) — not
153
+ * when the master merely had wizard PROGRESS. Those two come apart in both directions and both are
154
+ * wrong: `ensureWizardInitialized` writes `pitcher.completion_wizard` when the wizard OPENS, so a
155
+ * master whose wizard was opened and cancelled has progress and no artifact (5 such masters live on
156
+ * test-eu), while a master with an artifact and no progress key would be left unfixed. Claiming an
157
+ * artifact that is not there reopens the empty-`edit` bug from the other side (PIT-7488 QA Q9): the
158
+ * restore finds no skeletons and parks every ref on one unnamed point.
159
+ *
160
+ * Returns `undefined` rather than `{}` so the create payload omits the field instead of writing an
161
+ * empty object over the server's default.
162
+ */
163
+ export declare function buildContextFromMaster(opts: {
164
+ masterContext?: CanvasRetrieve['context'] | null;
165
+ /** `getDefaultCrmShape({ accountName, myUser })` — passed in because it needs Vue injection. */
166
+ crmShape?: Record<string, unknown> | null;
167
+ /** The account chosen in the dialog, already privacy-mode-adjusted (blank name = store no name). */
168
+ account?: {
169
+ id: string;
170
+ name?: string;
171
+ } | null;
172
+ }): Record<string, unknown> | undefined;
173
+ /**
174
+ * The create payload for a pitchdeck built from a master.
175
+ *
176
+ * `is_master: false` is spelled out rather than left to the spread: the fetched master carries
177
+ * `is_master: true`, and the flow also runs FROM the master tab, whose own `creator` exists to make
178
+ * masters. The child is an ordinary deck — it appears on the Pitchdecks tab, it has an account, and
179
+ * it is the thing the rep presents.
180
+ *
181
+ * `is_draft: false` matches the platform's other rep-facing creation flows (the create wizard posts
182
+ * `is_draft: false`). Inheriting the master's flag would silently mark customer-facing decks as
183
+ * drafts whenever the master was left in draft — a master's own status is bookkeeping for a source
184
+ * artifact and says nothing about the deck made from it.
185
+ *
186
+ * `access_type` is deliberately absent: it rides along from the master via the spread, and the host
187
+ * `creator` forces the value it wants (RESTRICTED for rep-created decks).
188
+ */
189
+ export declare function buildCreateFromMasterPayload(opts: {
190
+ master: CanvasRetrieve;
191
+ name: string;
192
+ /** Already privacy-mode-adjusted by the caller (`enable_crm_privacy_mode` blanks the name). */
193
+ account?: {
194
+ id: string;
195
+ name?: string;
196
+ } | null;
197
+ crmShape?: Record<string, unknown> | null;
198
+ }): Omit<CanvasCreateRequest, 'instance_id'>;
199
+ /**
200
+ * Whether the dialog must collect an account before it can create.
201
+ *
202
+ * Follows the platform setting rather than the prototype's always-required Account field: on a
203
+ * non-CRM org there are no accounts to offer, so requiring one would make the whole feature
204
+ * unreachable. Same rule the create modal and the duplicate flow already apply.
205
+ */
206
+ export declare function shouldRequireAccountOnCreateFromMaster(opts: {
207
+ isCRM?: boolean;
208
+ requireAccountOnCanvasCreation?: boolean;
209
+ }): boolean;
210
+ type MasterOption = Pick<CanvasRetrieve, 'id' | 'name'>;
211
+ /**
212
+ * Guarantees the master a row action was fired from is in the offered list.
213
+ *
214
+ * Since PIT-8360 dropped the picker's `is_draft: false` fetch filter the two lists agree, so this is
215
+ * now a guard rather than the everyday path: it still covers a master created or renamed after the
216
+ * list was fetched, and the `fetchAll` truncation at `MAX_FETCH_LIMIT`. Without it the rep could
217
+ * click a master's "Use to create pitchdeck" and land in a picker that neither preselects nor offers
218
+ * it. Sibling of `buildMasterPickerOptions`, which applies the same rule to the Vue dialog's option
219
+ * shape.
220
+ */
221
+ export declare function withPreselectedMaster<T extends {
222
+ id: string;
223
+ }>(masters: T[], preselected?: T | null): T[];
224
+ /**
225
+ * Options for the dialog's master picker.
226
+ *
227
+ * The picker lists every master since PIT-8360 dropped the fetch's `is_draft: false` filter — with
228
+ * the master tab's Ready column gone, nothing can clear the flag, so filtering would permanently
229
+ * hide masters already stored as drafts.
230
+ *
231
+ * **No draft marker on the labels** (PR review). It used to suffix "(Draft)", which was already
232
+ * unreachable — the fetch narrows to `id,name,tags,modified_at`, so every option arrived with
233
+ * `is_draft === undefined`. Restoring it via the `fields` list would have been the wrong repair:
234
+ * masters have no draft state to advertise any more, and a badge on a subset of them describes a
235
+ * distinction nothing in the product can act on. Dropping it also removes the hook a future reader
236
+ * would most likely rebuild a filter around.
237
+ *
238
+ * The preselected master is still injected — see `withPreselectedMaster` for the cases that still
239
+ * reaches.
240
+ */
241
+ export declare function buildMasterPickerOptions(opts: {
242
+ masters: MasterOption[];
243
+ preselected?: MasterOption | null;
244
+ }): {
245
+ label: string;
246
+ value: string;
247
+ }[];
248
+ /** Guards the submit button. A blank name would create a nameless row in the rep's own list. */
249
+ export declare function canSubmitCreateFromMaster(opts: {
250
+ name?: string | null;
251
+ masterId?: string | null;
252
+ isAccountRequired?: boolean;
253
+ accountId?: string | null;
254
+ }): boolean;
255
+ export {};
@@ -0,0 +1,56 @@
1
+ /**
2
+ * PIT-7488 block 7 — pure logic behind the Documents column.
3
+ *
4
+ * Display only. The decision of WHICH masters to render lives server-side, in next-core's
5
+ * `selectCanvasIdsNeedingRender`: the bulk read already computes `is_stale` in SQL, so keeping the
6
+ * policy next to it avoids two copies of the same rule drifting apart in two languages.
7
+ *
8
+ * Kept free of Vue so the state machine is testable on its own.
9
+ */
10
+ /**
11
+ * Mirrors next-core's `MasterDocumentsState`. Duplicated deliberately — next-core types are not
12
+ * published to canvas-ui, and canvas-ui must not import from next-core.
13
+ */
14
+ export interface MasterDocumentsState {
15
+ status: 'pending' | 'rendering' | 'ready' | 'failed';
16
+ pdf_url: string | null;
17
+ pptx_url: string | null;
18
+ rendered_at: string | null;
19
+ is_stale: boolean;
20
+ attempts: number;
21
+ error: string | null;
22
+ }
23
+ export type DocumentsCellState =
24
+ /** No documents row at all — this master has never been rendered. */
25
+ 'none'
26
+ /** A render is queued or running. */
27
+ | 'rendering'
28
+ /** Current documents are available. */
29
+ | 'ready'
30
+ /** Documents are available but out of date; a fresh render is coming. */
31
+ | 'ready_refreshing'
32
+ /** The last render failed. */
33
+ | 'failed';
34
+ /**
35
+ * Must match next-core's `MAX_RENDER_ATTEMPTS`. A client offering a fourth retry would render a
36
+ * button the server refuses to act on.
37
+ */
38
+ export declare const MAX_RENDER_ATTEMPTS = 3;
39
+ /**
40
+ * Maps a documents row onto what the cell should show.
41
+ *
42
+ * The `ready` + stale case deliberately keeps the existing links rather than reverting to a spinner:
43
+ * a slightly out-of-date document a rep can open beats a dead control. Only a genuinely absent
44
+ * artifact shows progress.
45
+ */
46
+ export declare function resolveDocumentsCellState(state: MasterDocumentsState | undefined | null): DocumentsCellState;
47
+ /**
48
+ * Whether to offer Retry. Only for a failed row, and only while the server would still act on it.
49
+ */
50
+ export declare function canRetryDocuments(state: MasterDocumentsState | undefined | null): boolean;
51
+ /**
52
+ * Whether the cell has anything openable. Drives the disabled state of each link independently —
53
+ * the two artifacts have different lifetimes (the PPTX object expires days before the PDF's URL
54
+ * mapping does), so one can be present while the other is not.
55
+ */
56
+ export declare function hasOpenableDocument(state: MasterDocumentsState | undefined | null, format: 'pdf' | 'pptx'): boolean;
@@ -0,0 +1,103 @@
1
+ import { MiniUser } from '../../../types/openapi';
2
+ /** Exactly `Collaboration['user']` and the "Sent to" cell's prop element (PIT-8421; was "Shared with"). */
3
+ export type MasterCanvasShareUser = MiniUser;
4
+ /**
5
+ * PIT-7488 block 5 — may the current user CHANGE this master canvas (Edit / Delete / Freeze)?
6
+ *
7
+ * The owner, **or an instance admin/editor**. That second arm mirrors Django's write gate
8
+ * exactly — `CanvasPermission.has_object_permission` returns True for
9
+ * `is_instance_admin or is_instance_editor or obj.is_owner(user)` — and it matters because the
10
+ * master list is not "only mine" for those roles: admins and editors route through
11
+ * `filter_by_admin_access`, so they see every master in the instance. Gating on ownership alone
12
+ * made the UI STRICTER than the API and silently removed Edit/Delete/Freeze from every master an
13
+ * admin did not personally own.
14
+ *
15
+ * A share recipient never reaches this gate on the SENDER's master: accepting an invitation mints
16
+ * a copy they own, so they meet the ownership arm on their own row and the sender's original is
17
+ * not in their list at all. That is why ng-rest-api#1688's read gates were reverted (#1689) —
18
+ * under invitation semantics there is no "viewing someone else's master" state to widen for.
19
+ *
20
+ * Fails closed, and deliberately not a bare `ownerId === myUserId`: that returns true when both
21
+ * are `undefined`, which would hand a recipient full control of any master whose owner failed to
22
+ * serialise.
23
+ */
24
+ export declare function canManageMasterCanvas(opts: {
25
+ ownerId?: number | null;
26
+ myUserId?: number | null;
27
+ isAdminOrEditor?: boolean;
28
+ }): boolean;
29
+ /**
30
+ * PIT-7488 block 5 — may the current user SHARE this master canvas?
31
+ *
32
+ * The owner ONLY — deliberately narrower than `canManageMasterCanvas`. The next-core route is
33
+ * owner-gated (`master.ownedById !== user.id` → 403), so offering "Share with" to an admin on
34
+ * someone else's master would surface an action the API refuses. Sharing is the owner's call
35
+ * about their own material, not a moderation capability.
36
+ */
37
+ export declare function canShareMasterCanvas(opts: {
38
+ ownerId?: number | null;
39
+ myUserId?: number | null;
40
+ }): boolean;
41
+ /**
42
+ * Per-user outcome of a send, mirroring next-core's `InvitationResult`.
43
+ *
44
+ * `already_invited` is benign (the UNIQUE key makes a repeat a no-op); `not_a_member` means the
45
+ * picker offered someone the server then refused, which the sender needs told.
46
+ */
47
+ export type MasterShareInvitationStatus = 'invited' | 'already_invited' | 'not_a_member' | 'self';
48
+ export interface MasterShareInvitationResult {
49
+ user_id: number;
50
+ status: MasterShareInvitationStatus;
51
+ }
52
+ /** One pending invitation, as the recipient's list renders it. */
53
+ export interface PendingMasterShare {
54
+ share_id: number;
55
+ canvas_id: string;
56
+ canvas_name: string;
57
+ instance_id: string;
58
+ shared_by: MiniUser;
59
+ created_at: string;
60
+ }
61
+ export declare function pendingRowId(shareId: number): string;
62
+ export declare function isPendingRowId(id?: string | number | null): boolean;
63
+ /**
64
+ * The share id behind a pending row id, or `null` if it is not one.
65
+ *
66
+ * Digits only, deliberately: `Number('')` is `0` and `Number.isInteger(0)` is true, so a bare
67
+ * `'pending:'` used to parse to share id 0 — and `Number` also accepts whitespace, `0x` and
68
+ * exponent forms. Share ids are a positive `serial`, so anything else is a bug upstream and must
69
+ * read as "not a pending row" rather than as row 0.
70
+ */
71
+ export declare function pendingShareIdFromRowId(id?: string | number | null): number | null;
72
+ /**
73
+ * Shapes a pending invitation as a table row.
74
+ *
75
+ * `owned_by` carries the SHARER, which is what lets the existing Owner column render "who sent
76
+ * this" with no special-casing — for an invitation the only person worth naming is the sender.
77
+ * `is_master` keeps it consistent with the tab it appears on; every other canvas field is left
78
+ * absent so the remaining cells render empty rather than inventing state the invitation has not
79
+ * got (no tags, no progress, no modified date, no ready/draft status).
80
+ */
81
+ export interface PendingMasterShareRow {
82
+ id: string;
83
+ name: string;
84
+ owned_by: MiniUser;
85
+ is_master: true;
86
+ created_at: string;
87
+ modified_at: string;
88
+ __pendingShare: PendingMasterShare;
89
+ }
90
+ export declare function toPendingRow(share: PendingMasterShare): PendingMasterShareRow;
91
+ /**
92
+ * Pending invitations first, then the user's own masters.
93
+ *
94
+ * Prepended rather than sorted in: an invitation is an inbox item needing a decision, and the
95
+ * table's sort applies to the fetched page — a pending row sorted by `modified_at` would sink
96
+ * below the fold on a rep with many masters and never be seen.
97
+ *
98
+ * Returns the list unchanged when there is nothing pending, so the non-master variants and the
99
+ * common case allocate nothing.
100
+ */
101
+ export declare function mergePendingRows<T extends {
102
+ id: string;
103
+ }>(rows: T[], pending: PendingMasterShare[]): (T | PendingMasterShareRow)[];
@@ -0,0 +1,122 @@
1
+ import { CanvasRetrieve } from '../../../types/openapi';
2
+ /**
3
+ * PIT-7488 block 3 — turning an existing pitchdeck into a master.
4
+ *
5
+ * The deck is DUPLICATED, never mutated: the rep keeps the deck they were presenting, and a new
6
+ * `is_master` copy appears on the master tab.
7
+ *
8
+ * **The promoter owns the master, always — so the create payload must OMIT `owned_by_id`**
9
+ * (PIT-8362; the payload itself is `buildMasterFromCanvasPayload`, beside its block-4 mirror in
10
+ * `createFromMaster.use`). Django declares that field
11
+ * `default=CreateOnlyDefault(CurrentUserDefault())`, so leaving it out is precisely what assigns
12
+ * the caller; sending anything is a transfer, which `CanvasOwnedByFieldValidator` 400s for anyone
13
+ * but an org/instance admin.
14
+ *
15
+ * This reverses block 3's original rule, so read the history before "fixing" it back. That rule
16
+ * kept the SOURCE deck's owner, on the reasoning that a master should stay attributed to whoever
17
+ * built the deck. Vontobel's validation rejected it: "My Master Pitchdecks" is a personal,
18
+ * RESTRICTED, owner-scoped tab that the promote flow navigates to on success, so a master owned by
19
+ * someone else lands in a tab it does not belong in — invisible to a rep (`filter_by_member_access`
20
+ * is `owned_by=me OR public`), and attributed to a stranger for an admin, who sees the whole
21
+ * instance anyway.
22
+ */
23
+ /**
24
+ * Context keys that must NOT survive onto a master.
25
+ *
26
+ * A master is a reusable *source*, so anything baked into its context propagates to every deck
27
+ * built from it later — `{{user.name}}` would render whoever ran "Use as master" rather than the
28
+ * rep actually presenting. That is the wrong-identity-on-a-copy defect class, one step upstream, and it is the
29
+ * same reasoning that keeps the master download path from PATCHing `context`.
30
+ *
31
+ * This is a DENYLIST on purpose. `pitcher_canvas.context` is a free-form client-written
32
+ * JSONField that also carries customer-specific keys, so an allowlist would silently drop those,
33
+ * and would drop any config key added later — which is exactly how PIT-7473 broke the pitchdeck
34
+ * wizard. Failing this way round means a *new* identity key leaks until it is listed here, which
35
+ * shows up as a visibly wrong name on a deck and is a one-line fix.
36
+ */
37
+ export declare const MASTER_CONTEXT_IDENTITY_KEYS: readonly ["user", "today", "Account", "salesforce_data", "Meeting", "pitcher_data", "analytics", "account", "meeting", "SelectedAccount"];
38
+ /**
39
+ * Top-level context key FAMILIES that are identity, matched by prefix rather than by name.
40
+ *
41
+ * `ctx_overwrite_<slideIndex>` is written by the `react-salesforce-admin` populator
42
+ * (`public/populator.html:509-525`) and holds BOTH a `pitcher_data` and a `salesforce_data` copy of
43
+ * a matched customer record, scoped to one slide — the same two namespaces
44
+ * `MASTER_CONTEXT_IDENTITY_KEYS` already strips at the top level, one nesting deeper and therefore
45
+ * invisible to a literal-key `omit`. A master promoted from a rep's deck carried the promoter's
46
+ * customer (client name, ids, figures) onto every deck built from it, per slide (PIT-8399 review).
47
+ *
48
+ * The key count is unbounded (one per matched slide) and the names embed a component id, so this
49
+ * cannot be expressed as literals — hence a prefix list.
50
+ *
51
+ * **Not to be confused with `context.wizard.overwrite_<sectionListId>_<n>`**, which is the WIZARD's
52
+ * positional slide override (`buildStepUpdate`) and is curation a master must keep. The two are
53
+ * cleanly separable: the populator's lives at the top level, the wizard's inside `wizard`.
54
+ */
55
+ export declare const MASTER_CONTEXT_IDENTITY_KEY_PREFIXES: readonly ["ctx_overwrite_"];
56
+ /** Drops every top-level key matching one of the identity prefixes above. Non-mutating. */
57
+ export declare function withoutPrefixedIdentityKeys(context: Record<string, unknown>): Record<string, unknown>;
58
+ /**
59
+ * Whether a context carries a wizard artifact the restore could actually rebuild POINTS from.
60
+ *
61
+ * Mirrors the two — and only two — skeleton sources of the app's restore walk: the enrichment's
62
+ * per-point spans (`react_wizard.points`) and the vanilla group selections
63
+ * (`group_selections_<sectionListId>`). BOTH must be non-empty arrays: `for (const sel of
64
+ * selections)` and the enrichment loop each build zero skeletons from `[]`, so a present-but-empty
65
+ * key describes nothing. Checking only the selections KEY made this looser than the app's
66
+ * `fromArtifact` (which uses `selections.length > 0`) — and on the duplicate path, which has no
67
+ * second gate, looser means unguarded. Everything else under `wizard` is answers, cover picks or
68
+ * admin config: the live repro deck carries `selected_assets`, `selected_asset_url`, `selected_file`
69
+ * and `title`, and the populator reads a `wizard.dataIndexMap` — none of which can rebuild a point.
70
+ *
71
+ * So `!!context.wizard` is NOT the same question, and the difference bites hardest in
72
+ * `buildDuplicateContext`: its marker selects `react_saved`, which resolves to the EDIT flow, whose
73
+ * restore is deliberately ungated on the app's own `fromArtifact` check (a vanilla deck legitimately
74
+ * restores by attribution). A master whose `wizard` held only cover picks therefore opened with an
75
+ * agenda rebuilt by attribution — the PIT-7488 QA Q9 outcome — with nothing downstream to catch it
76
+ * (peer review F2).
77
+ *
78
+ * Deliberately WEAKER than the app's `fromArtifact`, which also requires the enrichment spans to
79
+ * partition the node's ref list. The host has no refs to hand here, so it answers "are there
80
+ * skeleton sources at all"; the app makes the precise call. That is the two-gate design, and it is
81
+ * why the two predicates are allowed to differ.
82
+ */
83
+ export declare function hasRestorableArtifact(context?: Record<string, unknown> | null): boolean;
84
+ /** Suffix appended to the source deck's name to seed the dialog. Matches the prototype. */
85
+ export declare const MASTER_NAME_SUFFIX = "(master)";
86
+ /**
87
+ * Strips the identity above out of `context.wizard`, leaving the curation intact.
88
+ *
89
+ * Non-mutating — `omit` returns a new object at every level this touches, so the source master's
90
+ * own context is never altered. A `wizard` (or a `react_wizard`) left with nothing in it is
91
+ * dropped rather than kept as `{}`, the same rule `withoutPitcherKeys` follows for
92
+ * `context.pitcher`. A context carrying no `wizard` at all is returned untouched, which is what
93
+ * makes this safe to compose onto every master-context builder.
94
+ *
95
+ * Deliberately does NOT touch `react_wizard.points` / `version`: `restoreAgenda` validates the
96
+ * enrichment by checking that its per-point `slide_count`s partition the node's ref list exactly,
97
+ * so pruning identity here cannot invalidate the artifact the agenda is rebuilt from.
98
+ */
99
+ export declare function withoutWizardIdentity(context: Record<string, unknown>): Record<string, unknown>;
100
+ /**
101
+ * The context to store on the new master: everything the source deck carried, minus identity.
102
+ *
103
+ * Identity is pruned at all THREE places it hides — the top-level keys above, the prefixed
104
+ * `ctx_overwrite_*` families (`withoutPrefixedIdentityKeys`), and the wizard artifact's own
105
+ * (`withoutWizardIdentity`). A master promoted from a deck the rep had run the
106
+ * wizard on carried the promoter's customer in `wizard.subtitle`, which deck templates render on
107
+ * the cover slide, so the top-level pass alone was not enough (PIT-8399).
108
+ *
109
+ * Deliberately NOT gated on `copy_context_to_canvas`. That flag exists because copying context
110
+ * wholesale was risky, and the risk was the identity keys stripped here — gating on it would
111
+ * hand flag-off instances a master with no `section_list_config`, reintroducing PIT-7473 for
112
+ * every deck built from it.
113
+ *
114
+ * Returns `undefined` rather than `{}` when nothing survives, so the create payload omits the
115
+ * field instead of writing an empty object over the server's default.
116
+ */
117
+ export declare function buildMasterContext(context?: CanvasRetrieve['context'] | null): Record<string, unknown> | undefined;
118
+ /**
119
+ * Seeds the dialog's name field. The rep can overwrite it; the suffix only exists so the master
120
+ * is distinguishable from the deck it came from in a list sorted by name.
121
+ */
122
+ export declare function defaultMasterNameFor(sourceName?: string | null): string;
@@ -0,0 +1,50 @@
1
+ import { CanvasCreateRequest, CanvasRetrieve } from '../../../types/openapi';
2
+ /**
3
+ * PIT-7488 — creating a master FROM AN ADMIN TEMPLATE.
4
+ *
5
+ * The fourth and last of the master copy paths, and the one that was missing: block 3 is
6
+ * deck -> master (`buildMasterContext`), block 4 is master -> deck (`buildContextFromMaster`),
7
+ * block 5 is master -> shared copy (next-core's `buildSharedCopyContext`), and this is
8
+ * template -> master.
9
+ *
10
+ * It exists because **Django does not clone a template server-side.** `POST /canvases` stores
11
+ * `template_id` as a plain FK and nothing else, so every create path clones client-side —
12
+ * `canvas.create.ts` for pitchdecks, `buildCreateFromMasterPayload` for decks from masters. The
13
+ * master tab was the one path that never did, so a master built from a template was created
14
+ * EMPTY: no `content` means no `SectionList`/`ContentList` nodes, and
15
+ * `useCanvasCompletionWizard.isCompletionWizardEnabled` is `wizardSteps.length > 0`, so the
16
+ * pitchdeck wizard could never run on it either. One missing clone, two reported symptoms.
17
+ *
18
+ * Not built on `slotty-ui-ng`'s shared `createCanvas` util on purpose: that util assigns
19
+ * `content`/`theme`/`context` AFTER spreading the caller's payload, so a sanitized master context
20
+ * cannot be passed in — and what it writes instead is wrong in both flag states (with
21
+ * `copy_context_to_canvas` on it copies the template context wholesale, identity and completed
22
+ * wizard state included; with it off it writes `{}`, losing `section_list_config`). It also
23
+ * prompts for an account and merges CRM data into the context on orgs with
24
+ * `require_account_on_canvas_creation`, which no master may carry.
25
+ */
26
+ /** The narrow slice of a fetched template this needs — request it as `fields=content,theme,context`. */
27
+ export interface MasterTemplateSource extends Pick<CanvasRetrieve, 'content' | 'theme' | 'context'> {
28
+ }
29
+ /** The create-payload fields to spread over the master's own invariants. */
30
+ export interface MasterFromTemplatePayload extends Pick<CanvasCreateRequest, 'content' | 'theme' | 'context'> {
31
+ }
32
+ /**
33
+ * Builds the `content`/`theme`/`context` a master should inherit from the template it was created
34
+ * from. Pure, non-mutating, and **deliberately ungated**.
35
+ *
36
+ * Not gated on `copy_context_to_canvas` for the same reason `buildMasterContext` is not: that flag
37
+ * exists because copying context wholesale was risky, and the risk *is* the identity keys stripped
38
+ * here. Gating on it would hand flag-off instances a master with no `section_list_config`, which is
39
+ * PIT-7473 reintroduced for every deck later built from that master.
40
+ *
41
+ * `metadata` is deliberately NOT copied. Nothing the wizard reads lives there, and a template's
42
+ * metadata carries its own bookkeeping — the async-download record (`job_id`, `pdf_url`, `pptx_url`,
43
+ * `conversion_date`) and `frozen_until`, which would make a brand-new master born read-only and
44
+ * offering a download of the TEMPLATE. Having none is the truthful state for a new master.
45
+ *
46
+ * Every field is omitted rather than emitted as `undefined`/`{}` when there is nothing to carry, so
47
+ * the no-template case ("New master pitchdeck") posts byte-identical to what it posts today instead
48
+ * of writing empty objects over the server's defaults.
49
+ */
50
+ export declare function buildMasterFromTemplatePayload(template?: MasterTemplateSource | null): MasterFromTemplatePayload;