@wildwinter/app-shell 0.46.4 → 0.47.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 (66) hide show
  1. package/LICENSES.md +5 -4
  2. package/dist/{chunk-EHR2KBZC.js → chunk-5SQHL4YV.js} +3 -3
  3. package/dist/{chunk-EHR2KBZC.js.map → chunk-5SQHL4YV.js.map} +1 -1
  4. package/dist/{chunk-J6RSONXM.js → chunk-FPXEQKWM.js} +4 -4
  5. package/dist/{chunk-J6RSONXM.js.map → chunk-FPXEQKWM.js.map} +1 -1
  6. package/dist/{chunk-R3NXPHRZ.js → chunk-IFWLNIBR.js} +13 -5
  7. package/dist/chunk-IFWLNIBR.js.map +1 -0
  8. package/dist/{chunk-CUFREPJ5.js → chunk-IK3LWBQL.js} +4 -4
  9. package/dist/{chunk-CUFREPJ5.js.map → chunk-IK3LWBQL.js.map} +1 -1
  10. package/dist/{chunk-WKPKBAXS.js → chunk-NKL6DNBZ.js} +4 -4
  11. package/dist/{chunk-WKPKBAXS.js.map → chunk-NKL6DNBZ.js.map} +1 -1
  12. package/dist/{chunk-UG2P777F.js → chunk-QLVBKME7.js} +34 -6
  13. package/dist/chunk-QLVBKME7.js.map +1 -0
  14. package/dist/{chunk-P6WLAEKZ.js → chunk-QVACRWG7.js} +13 -3
  15. package/dist/chunk-QVACRWG7.js.map +1 -0
  16. package/dist/{chunk-2ECJUTSX.js → chunk-T25WXWPB.js} +3 -2
  17. package/dist/chunk-T25WXWPB.js.map +1 -0
  18. package/dist/{chunk-YRFJLJUH.js → chunk-XR4DRDMA.js} +3 -3
  19. package/dist/{chunk-YRFJLJUH.js.map → chunk-XR4DRDMA.js.map} +1 -1
  20. package/dist/{chunk-G2JT3HZH.js → chunk-XXDJCOXN.js} +9 -9
  21. package/dist/{chunk-G2JT3HZH.js.map → chunk-XXDJCOXN.js.map} +1 -1
  22. package/dist/context-menu.cjs.map +1 -1
  23. package/dist/context-menu.js +2 -2
  24. package/dist/dialog.cjs.map +1 -1
  25. package/dist/dialog.js +2 -2
  26. package/dist/index.cjs +80 -17
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +85 -12
  29. package/dist/index.d.ts +85 -12
  30. package/dist/index.js +34 -20
  31. package/dist/index.js.map +1 -1
  32. package/dist/kit-gallery.cjs.map +1 -1
  33. package/dist/kit-gallery.js +3 -3
  34. package/dist/link-status.cjs +12 -5
  35. package/dist/link-status.cjs.map +1 -1
  36. package/dist/link-status.d.cts +1 -1
  37. package/dist/link-status.d.ts +1 -1
  38. package/dist/link-status.js +3 -3
  39. package/dist/menu.cjs +1 -0
  40. package/dist/menu.cjs.map +1 -1
  41. package/dist/menu.d.cts +8 -4
  42. package/dist/menu.d.ts +8 -4
  43. package/dist/menu.js +1 -1
  44. package/dist/{property-list-D3VZCJzi.d.cts → property-list-5mS4J2uy.d.cts} +8 -2
  45. package/dist/{property-list-D3VZCJzi.d.ts → property-list-5mS4J2uy.d.ts} +8 -2
  46. package/dist/property-list.cjs +10 -2
  47. package/dist/property-list.cjs.map +1 -1
  48. package/dist/property-list.d.cts +1 -1
  49. package/dist/property-list.d.ts +1 -1
  50. package/dist/property-list.js +2 -2
  51. package/dist/settings.css +54 -0
  52. package/dist/settings.css.map +1 -1
  53. package/dist/updater-view.cjs.map +1 -1
  54. package/dist/updater-view.js +3 -3
  55. package/dist/util.cjs +11 -0
  56. package/dist/util.cjs.map +1 -1
  57. package/dist/util.d.cts +18 -1
  58. package/dist/util.d.ts +18 -1
  59. package/dist/util.js +1 -1
  60. package/dist/welcome.cjs.map +1 -1
  61. package/dist/welcome.js +4 -4
  62. package/package.json +1 -1
  63. package/dist/chunk-2ECJUTSX.js.map +0 -1
  64. package/dist/chunk-P6WLAEKZ.js.map +0 -1
  65. package/dist/chunk-R3NXPHRZ.js.map +0 -1
  66. package/dist/chunk-UG2P777F.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { EDIT_MENU, FILE_MENU, GO_MENU, MenuLabel, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU } from './menu.cjs';
2
2
  export { HintBarItem, KeyHintOptions, KeyPlatform, hintBar, isKeyCombo, keyHint, keyLabel, keyLegends, keyPlatform, setKeyPlatform, tipWithKey } from './keys.cjs';
3
3
  export { ProblemCopy, ProblemCopyEntry, ProblemCopyTable, ProblemLike, defaultProblemCopy, describeProblem, problemLine, problemName } from './problems.cjs';
4
- export { D as DefaultControlOptions, a as DupGuard, P as PROPERTY_TYPES, b as PropertyDeclLike, c as PropertyListHandle, d as PropertyListOptions, e as PropertyListType, S as SettingsDialog, f as SettingsDialogOptions, g as SettingsSection, h as SettingsSectionHandle, i as defaultControl, j as dupGuard, k as expandableRow, l as focusNewRow, m as mountPropertyList, n as mountSettingsDialog, r as revealRow, o as revealRowWhenReady } from './property-list-D3VZCJzi.cjs';
4
+ export { D as DefaultControlOptions, a as DupGuard, P as PROPERTY_TYPES, b as PropertyDeclLike, c as PropertyListHandle, d as PropertyListOptions, e as PropertyListType, S as SettingsDialog, f as SettingsDialogOptions, g as SettingsSection, h as SettingsSectionHandle, i as defaultControl, j as dupGuard, k as expandableRow, l as focusNewRow, m as mountPropertyList, n as mountSettingsDialog, r as revealRow, o as revealRowWhenReady } from './property-list-5mS4J2uy.cjs';
5
5
  export { DialogFrame, DialogFrameOptions, dialogFrame } from './dialog.cjs';
6
6
  export { Floating, FloatingOptions, createFloating } from './floating.cjs';
7
7
  export { ContextItem, PopoverOptions, openContextMenu, openPopover } from './context-menu.cjs';
@@ -11,7 +11,7 @@ export { UpdaterDownloadProgress, UpdaterPromptOptions } from './updater.cjs';
11
11
  export { Welcome, WelcomeAction, WelcomeGroup, WelcomeGroupItem, WelcomeOptions, WelcomeRecent, mountWelcome } from './welcome.cjs';
12
12
  export { KitGallery, KitGalleryChoice, KitGalleryItem, KitGalleryOptions, KitGallerySection, kitTile, openKitGallery } from './kit-gallery.cjs';
13
13
  export { LinkStatus, LinkStatusChip, LinkStatusClass, LinkStatusOptions, linkAddress, linkStatusClass, linkStatusTip, mountLinkStatus } from './link-status.cjs';
14
- export { Debounced, copyWithFeedback, debounce, formatCount, isEditableTarget, plural, relativeTime } from './util.cjs';
14
+ export { Debounced, copyWithFeedback, debounce, formatCount, isEditableTarget, isLocalOrigin, plural, relativeTime } from './util.cjs';
15
15
  import './app-store.cjs';
16
16
  import 'electron';
17
17
 
@@ -87,7 +87,9 @@ declare const icon: {
87
87
  };
88
88
  /**
89
89
  * Every word in the vocabulary. The 26 the glyph table had, plus the seven the
90
- * 2026-09 review found the apps drawing by hand with nothing to check against.
90
+ * 2026-09 review found the apps drawing by hand with nothing to check against,
91
+ * and the two the 2026-10 Patterpad review found the same way (`copy`,
92
+ * `speaker`).
91
93
  */
92
94
  type IconName = keyof typeof icon
93
95
  /** History back, the topbar's quiet arrow pair (nav-history.ts). An ARROW,
@@ -107,7 +109,13 @@ type IconName = keyof typeof icon
107
109
  | "settings"
108
110
  /** Record. The icon is the DISC; painting it in the danger colour while
109
111
  * recording is the caller's job, through the button's own colour. */
110
- | "record";
112
+ | "record"
113
+ /** Copy this to the clipboard. The confirmation is `tick`, shown by the
114
+ * caller while `copyWithFeedback` holds `.copied` on the button. */
115
+ | "copy"
116
+ /** Sound: play with audio, a table read. The act of hearing, not a volume
117
+ * control. */
118
+ | "speaker";
111
119
  /**
112
120
  * The drawings as strings, by word, for a caller that builds markup itself.
113
121
  * Prefer `iconHtml(name, size)`, which sizes it; these carry no width/height.
@@ -128,6 +136,7 @@ declare const iconSvg: {
128
136
  readonly noteOutline: "<svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linejoin=\"round\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M3.4 2h4.9l3.9 3.9v7.5a1 1 0 0 1-1 1H3.4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z\"/><path d=\"M8.3 2.2v3.8h3.8\"/><path d=\"M4.9 8h6.2M4.9 10h6.2M4.9 12h3.7\" stroke-width=\"0.95\"/></svg>";
129
137
  readonly search: string;
130
138
  readonly close: string;
139
+ readonly copy: string;
131
140
  readonly play: string;
132
141
  readonly more: string;
133
142
  readonly collapsed: string;
@@ -159,6 +168,7 @@ declare const iconSvg: {
159
168
  readonly pin: string;
160
169
  readonly settings: string;
161
170
  readonly record: string;
171
+ readonly speaker: string;
162
172
  };
163
173
  type IconSvgName = keyof typeof iconSvg;
164
174
  /** Every word, in table order, for a test or a gallery to walk. */
@@ -205,8 +215,13 @@ declare function el<K extends keyof HTMLElementTagNameMap>(tag: K, classNameOrPr
205
215
  * call sites yet still gets the drawn set; any other string is set as text,
206
216
  * which is the old behaviour and on its way out. */
207
217
  declare function iconBtn(name: IconName | (string & {}), title: string, onClick: () => void, disabled?: boolean, danger?: boolean): HTMLButtonElement;
218
+ /** The sentence under a settings row: plain text, or nodes when it carries
219
+ * markup (a `code` for a file name, a `b` for an option's name). */
220
+ type FieldHint = string | Node | Array<string | Node>;
208
221
  /**
209
- * A captioned field: `<label class="shell-labelled"><span class="shell-fieldcap">…</span>control</label>`.
222
+ * A captioned field, the family's settings row (the 2026-10 review, ruling C):
223
+ * the caption on the left and the control inline beside it,
224
+ * `<label class="shell-labelled"><span class="shell-fieldcap">…</span>control</label>`.
210
225
  *
211
226
  * The label is pointed at the field it captions, EXPLICITLY, and never at a button. A `<label>` with no
212
227
  * `for` forwards a click to its first labelable descendant, and buttons are labelable - so a caption
@@ -214,12 +229,44 @@ declare function iconBtn(name: IconName | (string & {}), title: string, onClick:
214
229
  * turns every click on the row's dead space into a press of the FIRST button in it. That shipped:
215
230
  * patterkit/patter#44, where clicking a Game Data list value anywhere but its ✕ deleted the first value
216
231
  * in the list. Reported as "clicking a value removes the wrong one", which is not a wrong-index bug at
217
- * all - the click never reached the chip.
232
+ * all - the click never reached the chip. The same rule makes a control GROUP safe to caption: a field
233
+ * with its own Choose… or Import… button beside it (`.shell-inline`) captions the field, and the button
234
+ * stays a button.
218
235
  *
219
236
  * When the control holds no labelable field (a chips editor with the add input removed, say), this is a
220
237
  * plain `<div>`: no caption behaviour is better than a caption that presses something.
238
+ *
239
+ * With a `hint`, the row and the sentence under it come back together in a `.shell-field`, the hint as
240
+ * the field's description (`aria-describedby`) rather than inside the label, where it would become part
241
+ * of the field's NAME and be read out every time it takes focus. Without one, the row is exactly what it
242
+ * always was.
243
+ */
244
+ declare function labelled(label: string, control: HTMLElement, hint?: FieldHint): HTMLElement;
245
+ interface LabelledToggleOptions {
246
+ checked?: boolean;
247
+ /** The sentence under the row: what turning it on does. */
248
+ hint?: FieldHint;
249
+ /** An option that only means something while the row above it is set (Patterpad's "Embed source
250
+ * language for debug" under an IDs-only build): indented under that row with a rule, so it reads as
251
+ * belonging to it rather than as a peer. */
252
+ sub?: boolean;
253
+ disabled?: boolean;
254
+ /** Fired on every flip, with the new state. The input is handed back too, for a caller that reads it
255
+ * on Save instead. */
256
+ onChange?: (checked: boolean) => void;
257
+ }
258
+ /**
259
+ * The settings toggle, `labelled`'s partner (ruling C): the caption on the left, the checkbox beside it
260
+ * where any other row has its control, and the optional hint under the row. One shape in both apps, so
261
+ * a switch in one app's settings looks like a switch in the other's. Clicking the caption flips it, as
262
+ * the label is the checkbox's.
263
+ *
264
+ * Returns the outermost row (to append, hide, or indent) and the input (to read or listen to).
221
265
  */
222
- declare function labelled(label: string, control: HTMLElement): HTMLElement;
266
+ declare function labelledToggle(label: string, opts?: LabelledToggleOptions): {
267
+ row: HTMLElement;
268
+ input: HTMLInputElement;
269
+ };
223
270
  /** Swap item `i` with its neighbour `i + delta` IN PLACE (the up/down reorder list editors share);
224
271
  * a no-op when the target is out of range. Returns whether anything moved. */
225
272
  declare function moveItem<T>(arr: T[], i: number, delta: number): boolean;
@@ -775,7 +822,9 @@ interface DocLine {
775
822
  * what stops a note being copied down and then disagreeing with itself. */
776
823
  interface Notes {
777
824
  own: DocLine[];
778
- inherited: {
825
+ /** Outermost first. Absent or empty in an app whose own UI already shows
826
+ * each ancestor's notes (Patterpad's stacked inspector). */
827
+ inherited?: {
779
828
  id: string;
780
829
  label: string;
781
830
  lines: DocLine[];
@@ -784,13 +833,25 @@ interface Notes {
784
833
  classes: string[];
785
834
  }
786
835
  interface NotesEditorOptions {
787
- /** What this is about, for the dialog's title ("Notes: Arrive at the gate"). */
788
- subject: string;
836
+ /** What this is about, for the dialog's title ("Notes: Arrive at the gate").
837
+ * Absent, the title is "Notes". */
838
+ subject?: string;
839
+ /** One line under the title, for what the classes are FOR in this app
840
+ * (Patterpad's "A VO note travels with the voice script…"): the dialog
841
+ * frame's own sub line. */
842
+ sub?: string;
789
843
  notes: Notes;
844
+ /** Open on this class: its text area is shown even when empty, and the
845
+ * caret is in it. Patterpad's "Needs re-record" asks why the line needs
846
+ * another take, and the answer is a VO note, so it opens on "vo". Ignored
847
+ * when the class is neither offered (`notes.classes`) nor already written
848
+ * on, so a prompt cannot add a class the thing does not take. "" is the
849
+ * editor-only bucket. */
850
+ focus?: string;
790
851
  /** Commit. Called once, on close, and only when something changed. */
791
852
  save: (lines: DocLine[]) => void;
792
853
  }
793
- /** Open the notes modal. Resolves when it closes. */
854
+ /** Open the notes modal. Returns at once; `save` runs when it closes. */
794
855
  declare function openNotesEditor(opts: NotesEditorOptions): void;
795
856
 
796
857
  /** One message. The author is a NAME stamped at posting time, not a reference:
@@ -1021,6 +1082,12 @@ interface StaleBarOptions {
1021
1082
  * scene". Written into "<subject> changed in the editor."
1022
1083
  */
1023
1084
  subject: string;
1085
+ /**
1086
+ * The whole first sentence, for when "changed in the editor" is not what
1087
+ * happened: another project was opened, or the project was closed. Replaces
1088
+ * "<subject> changed in the editor."; the advice still follows it.
1089
+ */
1090
+ message?: string;
1024
1091
  /** Start again from the current source. */
1025
1092
  onRestart: () => void;
1026
1093
  /** The button, when "Restart" is not the app's word for it. */
@@ -1129,6 +1196,12 @@ interface NavHistory<Place> {
1129
1196
  * is validating the whole stack on every paint). */
1130
1197
  canBack(): boolean;
1131
1198
  canForward(): boolean;
1199
+ /** Forget both sides, for when the places stop meaning anything: a
1200
+ * different project opened, or the welcome screen with none. Without it
1201
+ * Back walks into the project just closed (Patterpad, 2026-10-07: on the
1202
+ * welcome screen the arrows looked live and did nothing). The caller
1203
+ * re-`set`s its arrows afterwards, as after any other change. */
1204
+ clear(): void;
1132
1205
  }
1133
1206
  declare function createNavHistory<Place>(opts: NavHistoryOptions<Place>): NavHistory<Place>;
1134
1207
  /**
@@ -1152,4 +1225,4 @@ declare function historyNav(onBack: () => void, onForward: () => void): {
1152
1225
  set(canBack: boolean, canForward: boolean): void;
1153
1226
  };
1154
1227
 
1155
- export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type Crumb, type DocLine, type ElProps, type FollowButton, type FollowButtonOptions, type GameIdEditorOptions, ICON_NAMES, ICON_SIZE, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobBeginOptions, type JobProgressOptions, type JobProgressView, type NavHistory, type NavHistoryOptions, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, type PropertyRefOptions, RESERVED_PROPERTY_NAMES, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type ToastKind, type ToastOptions, type ToolWindowHeadOptions, type TooltipOptions, type VcBadge, type VcMap, askIdentity, bindPropertyName, bindPropertyRef, breadcrumb, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createNavHistory, createSaveController, dismissToast, el, firstIllegalPropertyName, foldVc, followButton, gameIdify, hideTip, historyNav, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isCaseOnlyPropertyName, isIconName, isValidGameId, isValidPropertyName, labelled, lockControls, lockNotice, metaLine, mountJobProgress, mountPaneShell, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameProblem, propertyNameify, propertyRefProblem, renderStepperBar, revalidatePropertyRefs, saveIndicator, showAbout, stageChips, staleBar, tagChips, tipAt, tipBold, toast, toolWindowHead, vcBadgeFor, wireReorder };
1228
+ export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type Crumb, type DocLine, type ElProps, type FieldHint, type FollowButton, type FollowButtonOptions, type GameIdEditorOptions, ICON_NAMES, ICON_SIZE, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobBeginOptions, type JobProgressOptions, type JobProgressView, type LabelledToggleOptions, type NavHistory, type NavHistoryOptions, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, type PropertyRefOptions, RESERVED_PROPERTY_NAMES, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type ToastKind, type ToastOptions, type ToolWindowHeadOptions, type TooltipOptions, type VcBadge, type VcMap, askIdentity, bindPropertyName, bindPropertyRef, breadcrumb, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createNavHistory, createSaveController, dismissToast, el, firstIllegalPropertyName, foldVc, followButton, gameIdify, hideTip, historyNav, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isCaseOnlyPropertyName, isIconName, isValidGameId, isValidPropertyName, labelled, labelledToggle, lockControls, lockNotice, metaLine, mountJobProgress, mountPaneShell, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameProblem, propertyNameify, propertyRefProblem, renderStepperBar, revalidatePropertyRefs, saveIndicator, showAbout, stageChips, staleBar, tagChips, tipAt, tipBold, toast, toolWindowHead, vcBadgeFor, wireReorder };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { EDIT_MENU, FILE_MENU, GO_MENU, MenuLabel, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU } from './menu.js';
2
2
  export { HintBarItem, KeyHintOptions, KeyPlatform, hintBar, isKeyCombo, keyHint, keyLabel, keyLegends, keyPlatform, setKeyPlatform, tipWithKey } from './keys.js';
3
3
  export { ProblemCopy, ProblemCopyEntry, ProblemCopyTable, ProblemLike, defaultProblemCopy, describeProblem, problemLine, problemName } from './problems.js';
4
- export { D as DefaultControlOptions, a as DupGuard, P as PROPERTY_TYPES, b as PropertyDeclLike, c as PropertyListHandle, d as PropertyListOptions, e as PropertyListType, S as SettingsDialog, f as SettingsDialogOptions, g as SettingsSection, h as SettingsSectionHandle, i as defaultControl, j as dupGuard, k as expandableRow, l as focusNewRow, m as mountPropertyList, n as mountSettingsDialog, r as revealRow, o as revealRowWhenReady } from './property-list-D3VZCJzi.js';
4
+ export { D as DefaultControlOptions, a as DupGuard, P as PROPERTY_TYPES, b as PropertyDeclLike, c as PropertyListHandle, d as PropertyListOptions, e as PropertyListType, S as SettingsDialog, f as SettingsDialogOptions, g as SettingsSection, h as SettingsSectionHandle, i as defaultControl, j as dupGuard, k as expandableRow, l as focusNewRow, m as mountPropertyList, n as mountSettingsDialog, r as revealRow, o as revealRowWhenReady } from './property-list-5mS4J2uy.js';
5
5
  export { DialogFrame, DialogFrameOptions, dialogFrame } from './dialog.js';
6
6
  export { Floating, FloatingOptions, createFloating } from './floating.js';
7
7
  export { ContextItem, PopoverOptions, openContextMenu, openPopover } from './context-menu.js';
@@ -11,7 +11,7 @@ export { UpdaterDownloadProgress, UpdaterPromptOptions } from './updater.js';
11
11
  export { Welcome, WelcomeAction, WelcomeGroup, WelcomeGroupItem, WelcomeOptions, WelcomeRecent, mountWelcome } from './welcome.js';
12
12
  export { KitGallery, KitGalleryChoice, KitGalleryItem, KitGalleryOptions, KitGallerySection, kitTile, openKitGallery } from './kit-gallery.js';
13
13
  export { LinkStatus, LinkStatusChip, LinkStatusClass, LinkStatusOptions, linkAddress, linkStatusClass, linkStatusTip, mountLinkStatus } from './link-status.js';
14
- export { Debounced, copyWithFeedback, debounce, formatCount, isEditableTarget, plural, relativeTime } from './util.js';
14
+ export { Debounced, copyWithFeedback, debounce, formatCount, isEditableTarget, isLocalOrigin, plural, relativeTime } from './util.js';
15
15
  import './app-store.js';
16
16
  import 'electron';
17
17
 
@@ -87,7 +87,9 @@ declare const icon: {
87
87
  };
88
88
  /**
89
89
  * Every word in the vocabulary. The 26 the glyph table had, plus the seven the
90
- * 2026-09 review found the apps drawing by hand with nothing to check against.
90
+ * 2026-09 review found the apps drawing by hand with nothing to check against,
91
+ * and the two the 2026-10 Patterpad review found the same way (`copy`,
92
+ * `speaker`).
91
93
  */
92
94
  type IconName = keyof typeof icon
93
95
  /** History back, the topbar's quiet arrow pair (nav-history.ts). An ARROW,
@@ -107,7 +109,13 @@ type IconName = keyof typeof icon
107
109
  | "settings"
108
110
  /** Record. The icon is the DISC; painting it in the danger colour while
109
111
  * recording is the caller's job, through the button's own colour. */
110
- | "record";
112
+ | "record"
113
+ /** Copy this to the clipboard. The confirmation is `tick`, shown by the
114
+ * caller while `copyWithFeedback` holds `.copied` on the button. */
115
+ | "copy"
116
+ /** Sound: play with audio, a table read. The act of hearing, not a volume
117
+ * control. */
118
+ | "speaker";
111
119
  /**
112
120
  * The drawings as strings, by word, for a caller that builds markup itself.
113
121
  * Prefer `iconHtml(name, size)`, which sizes it; these carry no width/height.
@@ -128,6 +136,7 @@ declare const iconSvg: {
128
136
  readonly noteOutline: "<svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linejoin=\"round\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M3.4 2h4.9l3.9 3.9v7.5a1 1 0 0 1-1 1H3.4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z\"/><path d=\"M8.3 2.2v3.8h3.8\"/><path d=\"M4.9 8h6.2M4.9 10h6.2M4.9 12h3.7\" stroke-width=\"0.95\"/></svg>";
129
137
  readonly search: string;
130
138
  readonly close: string;
139
+ readonly copy: string;
131
140
  readonly play: string;
132
141
  readonly more: string;
133
142
  readonly collapsed: string;
@@ -159,6 +168,7 @@ declare const iconSvg: {
159
168
  readonly pin: string;
160
169
  readonly settings: string;
161
170
  readonly record: string;
171
+ readonly speaker: string;
162
172
  };
163
173
  type IconSvgName = keyof typeof iconSvg;
164
174
  /** Every word, in table order, for a test or a gallery to walk. */
@@ -205,8 +215,13 @@ declare function el<K extends keyof HTMLElementTagNameMap>(tag: K, classNameOrPr
205
215
  * call sites yet still gets the drawn set; any other string is set as text,
206
216
  * which is the old behaviour and on its way out. */
207
217
  declare function iconBtn(name: IconName | (string & {}), title: string, onClick: () => void, disabled?: boolean, danger?: boolean): HTMLButtonElement;
218
+ /** The sentence under a settings row: plain text, or nodes when it carries
219
+ * markup (a `code` for a file name, a `b` for an option's name). */
220
+ type FieldHint = string | Node | Array<string | Node>;
208
221
  /**
209
- * A captioned field: `<label class="shell-labelled"><span class="shell-fieldcap">…</span>control</label>`.
222
+ * A captioned field, the family's settings row (the 2026-10 review, ruling C):
223
+ * the caption on the left and the control inline beside it,
224
+ * `<label class="shell-labelled"><span class="shell-fieldcap">…</span>control</label>`.
210
225
  *
211
226
  * The label is pointed at the field it captions, EXPLICITLY, and never at a button. A `<label>` with no
212
227
  * `for` forwards a click to its first labelable descendant, and buttons are labelable - so a caption
@@ -214,12 +229,44 @@ declare function iconBtn(name: IconName | (string & {}), title: string, onClick:
214
229
  * turns every click on the row's dead space into a press of the FIRST button in it. That shipped:
215
230
  * patterkit/patter#44, where clicking a Game Data list value anywhere but its ✕ deleted the first value
216
231
  * in the list. Reported as "clicking a value removes the wrong one", which is not a wrong-index bug at
217
- * all - the click never reached the chip.
232
+ * all - the click never reached the chip. The same rule makes a control GROUP safe to caption: a field
233
+ * with its own Choose… or Import… button beside it (`.shell-inline`) captions the field, and the button
234
+ * stays a button.
218
235
  *
219
236
  * When the control holds no labelable field (a chips editor with the add input removed, say), this is a
220
237
  * plain `<div>`: no caption behaviour is better than a caption that presses something.
238
+ *
239
+ * With a `hint`, the row and the sentence under it come back together in a `.shell-field`, the hint as
240
+ * the field's description (`aria-describedby`) rather than inside the label, where it would become part
241
+ * of the field's NAME and be read out every time it takes focus. Without one, the row is exactly what it
242
+ * always was.
243
+ */
244
+ declare function labelled(label: string, control: HTMLElement, hint?: FieldHint): HTMLElement;
245
+ interface LabelledToggleOptions {
246
+ checked?: boolean;
247
+ /** The sentence under the row: what turning it on does. */
248
+ hint?: FieldHint;
249
+ /** An option that only means something while the row above it is set (Patterpad's "Embed source
250
+ * language for debug" under an IDs-only build): indented under that row with a rule, so it reads as
251
+ * belonging to it rather than as a peer. */
252
+ sub?: boolean;
253
+ disabled?: boolean;
254
+ /** Fired on every flip, with the new state. The input is handed back too, for a caller that reads it
255
+ * on Save instead. */
256
+ onChange?: (checked: boolean) => void;
257
+ }
258
+ /**
259
+ * The settings toggle, `labelled`'s partner (ruling C): the caption on the left, the checkbox beside it
260
+ * where any other row has its control, and the optional hint under the row. One shape in both apps, so
261
+ * a switch in one app's settings looks like a switch in the other's. Clicking the caption flips it, as
262
+ * the label is the checkbox's.
263
+ *
264
+ * Returns the outermost row (to append, hide, or indent) and the input (to read or listen to).
221
265
  */
222
- declare function labelled(label: string, control: HTMLElement): HTMLElement;
266
+ declare function labelledToggle(label: string, opts?: LabelledToggleOptions): {
267
+ row: HTMLElement;
268
+ input: HTMLInputElement;
269
+ };
223
270
  /** Swap item `i` with its neighbour `i + delta` IN PLACE (the up/down reorder list editors share);
224
271
  * a no-op when the target is out of range. Returns whether anything moved. */
225
272
  declare function moveItem<T>(arr: T[], i: number, delta: number): boolean;
@@ -775,7 +822,9 @@ interface DocLine {
775
822
  * what stops a note being copied down and then disagreeing with itself. */
776
823
  interface Notes {
777
824
  own: DocLine[];
778
- inherited: {
825
+ /** Outermost first. Absent or empty in an app whose own UI already shows
826
+ * each ancestor's notes (Patterpad's stacked inspector). */
827
+ inherited?: {
779
828
  id: string;
780
829
  label: string;
781
830
  lines: DocLine[];
@@ -784,13 +833,25 @@ interface Notes {
784
833
  classes: string[];
785
834
  }
786
835
  interface NotesEditorOptions {
787
- /** What this is about, for the dialog's title ("Notes: Arrive at the gate"). */
788
- subject: string;
836
+ /** What this is about, for the dialog's title ("Notes: Arrive at the gate").
837
+ * Absent, the title is "Notes". */
838
+ subject?: string;
839
+ /** One line under the title, for what the classes are FOR in this app
840
+ * (Patterpad's "A VO note travels with the voice script…"): the dialog
841
+ * frame's own sub line. */
842
+ sub?: string;
789
843
  notes: Notes;
844
+ /** Open on this class: its text area is shown even when empty, and the
845
+ * caret is in it. Patterpad's "Needs re-record" asks why the line needs
846
+ * another take, and the answer is a VO note, so it opens on "vo". Ignored
847
+ * when the class is neither offered (`notes.classes`) nor already written
848
+ * on, so a prompt cannot add a class the thing does not take. "" is the
849
+ * editor-only bucket. */
850
+ focus?: string;
790
851
  /** Commit. Called once, on close, and only when something changed. */
791
852
  save: (lines: DocLine[]) => void;
792
853
  }
793
- /** Open the notes modal. Resolves when it closes. */
854
+ /** Open the notes modal. Returns at once; `save` runs when it closes. */
794
855
  declare function openNotesEditor(opts: NotesEditorOptions): void;
795
856
 
796
857
  /** One message. The author is a NAME stamped at posting time, not a reference:
@@ -1021,6 +1082,12 @@ interface StaleBarOptions {
1021
1082
  * scene". Written into "<subject> changed in the editor."
1022
1083
  */
1023
1084
  subject: string;
1085
+ /**
1086
+ * The whole first sentence, for when "changed in the editor" is not what
1087
+ * happened: another project was opened, or the project was closed. Replaces
1088
+ * "<subject> changed in the editor."; the advice still follows it.
1089
+ */
1090
+ message?: string;
1024
1091
  /** Start again from the current source. */
1025
1092
  onRestart: () => void;
1026
1093
  /** The button, when "Restart" is not the app's word for it. */
@@ -1129,6 +1196,12 @@ interface NavHistory<Place> {
1129
1196
  * is validating the whole stack on every paint). */
1130
1197
  canBack(): boolean;
1131
1198
  canForward(): boolean;
1199
+ /** Forget both sides, for when the places stop meaning anything: a
1200
+ * different project opened, or the welcome screen with none. Without it
1201
+ * Back walks into the project just closed (Patterpad, 2026-10-07: on the
1202
+ * welcome screen the arrows looked live and did nothing). The caller
1203
+ * re-`set`s its arrows afterwards, as after any other change. */
1204
+ clear(): void;
1132
1205
  }
1133
1206
  declare function createNavHistory<Place>(opts: NavHistoryOptions<Place>): NavHistory<Place>;
1134
1207
  /**
@@ -1152,4 +1225,4 @@ declare function historyNav(onBack: () => void, onForward: () => void): {
1152
1225
  set(canBack: boolean, canForward: boolean): void;
1153
1226
  };
1154
1227
 
1155
- export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type Crumb, type DocLine, type ElProps, type FollowButton, type FollowButtonOptions, type GameIdEditorOptions, ICON_NAMES, ICON_SIZE, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobBeginOptions, type JobProgressOptions, type JobProgressView, type NavHistory, type NavHistoryOptions, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, type PropertyRefOptions, RESERVED_PROPERTY_NAMES, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type ToastKind, type ToastOptions, type ToolWindowHeadOptions, type TooltipOptions, type VcBadge, type VcMap, askIdentity, bindPropertyName, bindPropertyRef, breadcrumb, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createNavHistory, createSaveController, dismissToast, el, firstIllegalPropertyName, foldVc, followButton, gameIdify, hideTip, historyNav, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isCaseOnlyPropertyName, isIconName, isValidGameId, isValidPropertyName, labelled, lockControls, lockNotice, metaLine, mountJobProgress, mountPaneShell, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameProblem, propertyNameify, propertyRefProblem, renderStepperBar, revalidatePropertyRefs, saveIndicator, showAbout, stageChips, staleBar, tagChips, tipAt, tipBold, toast, toolWindowHead, vcBadgeFor, wireReorder };
1228
+ export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type Crumb, type DocLine, type ElProps, type FieldHint, type FollowButton, type FollowButtonOptions, type GameIdEditorOptions, ICON_NAMES, ICON_SIZE, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobBeginOptions, type JobProgressOptions, type JobProgressView, type LabelledToggleOptions, type NavHistory, type NavHistoryOptions, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, type PropertyRefOptions, RESERVED_PROPERTY_NAMES, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type ToastKind, type ToastOptions, type ToolWindowHeadOptions, type TooltipOptions, type VcBadge, type VcMap, askIdentity, bindPropertyName, bindPropertyRef, breadcrumb, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createNavHistory, createSaveController, dismissToast, el, firstIllegalPropertyName, foldVc, followButton, gameIdify, hideTip, historyNav, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isCaseOnlyPropertyName, isIconName, isValidGameId, isValidPropertyName, labelled, labelledToggle, lockControls, lockNotice, metaLine, mountJobProgress, mountPaneShell, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameProblem, propertyNameify, propertyRefProblem, renderStepperBar, revalidatePropertyRefs, saveIndicator, showAbout, stageChips, staleBar, tagChips, tipAt, tipBold, toast, toolWindowHead, vcBadgeFor, wireReorder };
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
1
- export { mountWelcome } from './chunk-CUFREPJ5.js';
2
- export { feedUpdaterDownloadProgress, showUpdaterDialog } from './chunk-WKPKBAXS.js';
1
+ export { mountWelcome } from './chunk-IK3LWBQL.js';
2
+ export { feedUpdaterDownloadProgress, showUpdaterDialog } from './chunk-NKL6DNBZ.js';
3
3
  export { defaultProblemCopy, describeProblem, problemLine, problemName } from './chunk-4OAORWEM.js';
4
- export { PROPERTY_NAME_HINT, PROPERTY_TYPES, RESERVED_PROPERTY_NAMES, bindPropertyName, bindPropertyRef, defaultControl, dupGuard, expandableRow, firstIllegalPropertyName, focusNewRow, isCaseOnlyPropertyName, isValidPropertyName, mountPropertyList, mountSettingsDialog, propertyNameProblem, propertyNameify, propertyRefProblem, revalidatePropertyRefs, revealRow, revealRowWhenReady } from './chunk-R3NXPHRZ.js';
4
+ export { PROPERTY_NAME_HINT, PROPERTY_TYPES, RESERVED_PROPERTY_NAMES, bindPropertyName, bindPropertyRef, defaultControl, dupGuard, expandableRow, firstIllegalPropertyName, focusNewRow, isCaseOnlyPropertyName, isValidPropertyName, mountPropertyList, mountSettingsDialog, propertyNameProblem, propertyNameify, propertyRefProblem, revalidatePropertyRefs, revealRow, revealRowWhenReady } from './chunk-IFWLNIBR.js';
5
5
  import { isKeyCombo, tipWithKey } from './chunk-YWMSQFXX.js';
6
6
  export { hintBar, isKeyCombo, keyHint, keyLabel, keyLegends, keyPlatform, setKeyPlatform, tipWithKey } from './chunk-YWMSQFXX.js';
7
- export { kitTile, openKitGallery } from './chunk-J6RSONXM.js';
8
- export { linkAddress, linkStatusClass, linkStatusTip, mountLinkStatus } from './chunk-G2JT3HZH.js';
9
- import { isEditableTarget } from './chunk-P6WLAEKZ.js';
10
- export { copyWithFeedback, debounce, formatCount, isEditableTarget, plural, relativeTime } from './chunk-P6WLAEKZ.js';
11
- export { EDIT_MENU, FILE_MENU, GO_MENU, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU } from './chunk-2ECJUTSX.js';
12
- import { dialogFrame } from './chunk-YRFJLJUH.js';
13
- export { dialogFrame } from './chunk-YRFJLJUH.js';
7
+ export { kitTile, openKitGallery } from './chunk-FPXEQKWM.js';
8
+ export { linkAddress, linkStatusClass, linkStatusTip, mountLinkStatus } from './chunk-XXDJCOXN.js';
9
+ import { isEditableTarget } from './chunk-QVACRWG7.js';
10
+ export { copyWithFeedback, debounce, formatCount, isEditableTarget, isLocalOrigin, plural, relativeTime } from './chunk-QVACRWG7.js';
11
+ export { EDIT_MENU, FILE_MENU, GO_MENU, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU } from './chunk-T25WXWPB.js';
12
+ import { dialogFrame } from './chunk-XR4DRDMA.js';
13
+ export { dialogFrame } from './chunk-XR4DRDMA.js';
14
14
  export { createFloating } from './chunk-PFERSFRB.js';
15
15
  export { JOB_PROGRESS } from './chunk-MDGOIT3O.js';
16
- export { openContextMenu, openPopover } from './chunk-EHR2KBZC.js';
17
- import { ensureTooltipHost, el, iconNode, labelled, icon } from './chunk-UG2P777F.js';
18
- export { ICON_NAMES, ICON_SIZE, breadcrumb, el, hideTip, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isIconName, labelled, metaLine, moveItem, stageChips, tagChips, tipAt, tipBold, wireReorder } from './chunk-UG2P777F.js';
16
+ export { openContextMenu, openPopover } from './chunk-5SQHL4YV.js';
17
+ import { ensureTooltipHost, el, iconNode, labelled, icon } from './chunk-QLVBKME7.js';
18
+ export { ICON_NAMES, ICON_SIZE, breadcrumb, el, hideTip, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isIconName, labelled, labelledToggle, metaLine, moveItem, stageChips, tagChips, tipAt, tipBold, wireReorder } from './chunk-QLVBKME7.js';
19
19
  import { closeWithExit } from './chunk-IU7EY6BI.js';
20
20
  export { closeWithExit } from './chunk-IU7EY6BI.js';
21
21
 
@@ -640,16 +640,19 @@ var CLASS_LABEL = {
640
640
  everyone: "Everyone",
641
641
  writing: "Writing",
642
642
  vo: "Voice (VO)",
643
- loc: "Localisers"
643
+ loc: "Localisers",
644
+ editor: "Editors"
644
645
  };
645
646
  var label = (cls) => cls === EDITOR_ONLY ? "Note (editor-only)" : CLASS_LABEL[cls] ?? cls[0].toUpperCase() + cls.slice(1);
646
647
  var PLACEHOLDER = {
647
648
  vo: "Direction for the voice actor or director",
648
- loc: "Context for translators"
649
+ loc: "Context for translators",
650
+ editor: "Context for an outside editor"
649
651
  };
650
652
  var placeholder = (cls) => cls === EDITOR_ONLY ? "An internal note, never exported" : PLACEHOLDER[cls] ?? "Why this is here";
651
653
  function openNotesEditor(opts) {
652
654
  const { notes } = opts;
655
+ const inherited = notes.inherited ?? [];
653
656
  const seed = /* @__PURE__ */ new Map();
654
657
  for (const line of notes.own) {
655
658
  const key = line.type ?? EDITOR_ONLY;
@@ -657,6 +660,8 @@ function openNotesEditor(opts) {
657
660
  ${line.text}` : line.text);
658
661
  }
659
662
  const shown = [...seed.keys()];
663
+ const focus = opts.focus !== void 0 && (opts.focus === EDITOR_ONLY || notes.classes.includes(opts.focus) || seed.has(opts.focus)) ? opts.focus : void 0;
664
+ if (focus !== void 0 && !shown.includes(focus)) shown.push(focus);
660
665
  if (shown.length === 0) shown.push(notes.classes[0] ?? EDITOR_ONLY);
661
666
  const areas = /* @__PURE__ */ new Map();
662
667
  const value = () => {
@@ -674,13 +679,13 @@ ${line.text}` : line.text);
674
679
  const body = el("div", "shell-notes-body");
675
680
  const render = () => {
676
681
  body.replaceChildren();
677
- if (notes.inherited.length > 0) {
682
+ if (inherited.length > 0) {
678
683
  const from = el(
679
684
  "div",
680
685
  { className: "shell-notes-inherited" },
681
686
  el("div", "shell-notes-caption", "Inherited")
682
687
  );
683
- for (const level of notes.inherited) {
688
+ for (const level of inherited) {
684
689
  from.append(el(
685
690
  "div",
686
691
  { className: "shell-notes-level" },
@@ -737,7 +742,12 @@ ${line.text}` : line.text);
737
742
  const next = value();
738
743
  if (JSON.stringify(next) !== before) opts.save(next);
739
744
  };
740
- const frame = dialogFrame({ title: `Notes: ${opts.subject}`, className: "shell-notes", onClose: commit });
745
+ const frame = dialogFrame({
746
+ title: opts.subject !== void 0 ? `Notes: ${opts.subject}` : "Notes",
747
+ ...opts.sub !== void 0 ? { sub: opts.sub } : {},
748
+ className: "shell-notes",
749
+ onClose: commit
750
+ });
741
751
  frame.body.append(body);
742
752
  frame.actions.append(done);
743
753
  done.addEventListener("click", () => {
@@ -745,7 +755,7 @@ ${line.text}` : line.text);
745
755
  frame.close();
746
756
  });
747
757
  frame.open();
748
- areas.get(shown[0] ?? EDITOR_ONLY)?.focus();
758
+ areas.get(focus ?? shown[0] ?? EDITOR_ONLY)?.focus();
749
759
  }
750
760
 
751
761
  // src/comments.ts
@@ -1075,7 +1085,7 @@ function staleBar(opts) {
1075
1085
  { className: "stale-bar" },
1076
1086
  el("span", {
1077
1087
  className: "stale-bar-msg",
1078
- text: `${opts.subject} changed in the editor. ${opts.advice ?? "Restart to play the new version."}`
1088
+ text: `${opts.message ?? `${opts.subject} changed in the editor.`} ${opts.advice ?? "Restart to play the new version."}`
1079
1089
  }),
1080
1090
  // The icon is a node beside the word, not a character inside it; the
1081
1091
  // button's flex gap sets the space (stale.css).
@@ -1217,6 +1227,10 @@ function createNavHistory(opts) {
1217
1227
  },
1218
1228
  canForward() {
1219
1229
  return ahead.length > 0;
1230
+ },
1231
+ clear() {
1232
+ past = [];
1233
+ ahead = [];
1220
1234
  }
1221
1235
  };
1222
1236
  }