notations 1.0.2 → 1.0.4

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 (68) hide show
  1. package/dist/notations.umd.js +3933 -12155
  2. package/dist/notations.umd.min.js +2 -3
  3. package/dist/notations.umd.min.js.map +1 -1
  4. package/lib/cjs/parser.js +1 -1
  5. package/lib/cjs/parser.js.map +1 -1
  6. package/lib/cjs/web/components/DockViewPlayground.d.ts +51 -0
  7. package/lib/cjs/web/components/DockViewPlayground.js +364 -0
  8. package/lib/cjs/web/components/DockViewPlayground.js.map +1 -0
  9. package/lib/cjs/web/components/NotationBlock.d.ts +35 -0
  10. package/lib/cjs/web/components/NotationBlock.js +219 -0
  11. package/lib/cjs/web/components/NotationBlock.js.map +1 -0
  12. package/lib/cjs/web/components/NotebookCell.d.ts +41 -0
  13. package/lib/cjs/web/components/NotebookCell.js +269 -0
  14. package/lib/cjs/web/components/NotebookCell.js.map +1 -0
  15. package/lib/cjs/web/components/NotebookView.d.ts +37 -0
  16. package/lib/cjs/web/components/NotebookView.js +379 -0
  17. package/lib/cjs/web/components/NotebookView.js.map +1 -0
  18. package/lib/cjs/web/components/SideBySideEditor.d.ts +47 -0
  19. package/lib/cjs/web/components/SideBySideEditor.js +171 -0
  20. package/lib/cjs/web/components/SideBySideEditor.js.map +1 -0
  21. package/lib/cjs/web/dockview.d.ts +2 -0
  22. package/lib/cjs/web/dockview.js +11 -0
  23. package/lib/cjs/web/dockview.js.map +1 -0
  24. package/lib/cjs/web/index.d.ts +8 -0
  25. package/lib/cjs/web/index.js +34 -0
  26. package/lib/cjs/web/index.js.map +1 -0
  27. package/lib/cjs/web/types/notebook.d.ts +64 -0
  28. package/lib/cjs/web/types/notebook.js +56 -0
  29. package/lib/cjs/web/types/notebook.js.map +1 -0
  30. package/lib/cjs/web/utils/cellFactory.d.ts +16 -0
  31. package/lib/cjs/web/utils/cellFactory.js +137 -0
  32. package/lib/cjs/web/utils/cellFactory.js.map +1 -0
  33. package/lib/cjs/web/utils/sourceSerializer.d.ts +19 -0
  34. package/lib/cjs/web/utils/sourceSerializer.js +162 -0
  35. package/lib/cjs/web/utils/sourceSerializer.js.map +1 -0
  36. package/lib/esm/parser.js +1 -1
  37. package/lib/esm/parser.js.map +1 -1
  38. package/lib/esm/web/components/DockViewPlayground.d.ts +51 -0
  39. package/lib/esm/web/components/DockViewPlayground.js +358 -0
  40. package/lib/esm/web/components/DockViewPlayground.js.map +1 -0
  41. package/lib/esm/web/components/NotationBlock.d.ts +35 -0
  42. package/lib/esm/web/components/NotationBlock.js +216 -0
  43. package/lib/esm/web/components/NotationBlock.js.map +1 -0
  44. package/lib/esm/web/components/NotebookCell.d.ts +41 -0
  45. package/lib/esm/web/components/NotebookCell.js +266 -0
  46. package/lib/esm/web/components/NotebookCell.js.map +1 -0
  47. package/lib/esm/web/components/NotebookView.d.ts +37 -0
  48. package/lib/esm/web/components/NotebookView.js +376 -0
  49. package/lib/esm/web/components/NotebookView.js.map +1 -0
  50. package/lib/esm/web/components/SideBySideEditor.d.ts +47 -0
  51. package/lib/esm/web/components/SideBySideEditor.js +168 -0
  52. package/lib/esm/web/components/SideBySideEditor.js.map +1 -0
  53. package/lib/esm/web/dockview.d.ts +2 -0
  54. package/lib/esm/web/dockview.js +3 -0
  55. package/lib/esm/web/dockview.js.map +1 -0
  56. package/lib/esm/web/index.d.ts +8 -0
  57. package/lib/esm/web/index.js +9 -0
  58. package/lib/esm/web/index.js.map +1 -0
  59. package/lib/esm/web/types/notebook.d.ts +64 -0
  60. package/lib/esm/web/types/notebook.js +50 -0
  61. package/lib/esm/web/types/notebook.js.map +1 -0
  62. package/lib/esm/web/utils/cellFactory.d.ts +16 -0
  63. package/lib/esm/web/utils/cellFactory.js +127 -0
  64. package/lib/esm/web/utils/cellFactory.js.map +1 -0
  65. package/lib/esm/web/utils/sourceSerializer.d.ts +19 -0
  66. package/lib/esm/web/utils/sourceSerializer.js +154 -0
  67. package/lib/esm/web/utils/sourceSerializer.js.map +1 -0
  68. package/package.json +41 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SideBySideEditor.js","sourceRoot":"","sources":["../../../../src/web/components/SideBySideEditor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SideBySideEditor - Manages editor and output components for side-by-side editing.\n *\n * This component handles:\n * - Creating editor textarea and NotationView output\n * - Scroll synchronization between editor and output\n * - Parsing and rendering on source changes\n *\n * Layout is NOT managed by this component - the client is responsible for\n * arranging the editor and output elements (e.g., using CSS grid, flexbox,\n * DockView, or any other layout system).\n */\n\nimport { load } from \"../../loader\";\nimport { NotationView } from \"../../carnatic\";\nimport { GridLayoutGroup } from \"../../grids\";\nimport { Notation } from \"../../notation\";\nimport { GlobalBeatLayout } from \"../../beats\";\n\n/**\n * Configuration for SideBySideEditor.\n */\nexport interface SideBySideEditorConfig {\n /**\n * Initial source text.\n */\n initialSource?: string;\n\n /**\n * Debounce delay (ms) for auto-parsing on input.\n * Set to 0 to disable auto-parsing.\n * @default 300\n */\n debounceDelay?: number;\n\n /**\n * Whether to enable scroll synchronization.\n * @default true\n */\n syncScroll?: boolean;\n\n /**\n * Callback when source changes.\n */\n onSourceChange?: (source: string) => void;\n\n /**\n * Callback when notation is successfully parsed.\n */\n onNotationParsed?: (notation: Notation, beatLayout: GlobalBeatLayout) => void;\n\n /**\n * Callback when parsing fails.\n */\n onParseError?: (errors: any[]) => void;\n\n /**\n * Optional shared GridLayoutGroup for column alignment.\n */\n sharedGridLayoutGroup?: GridLayoutGroup;\n\n /**\n * Optional markdown parser for RawBlock content.\n */\n markdownParser?: (content: string) => string;\n\n /**\n * CSS class for the editor textarea.\n */\n editorClass?: string;\n\n /**\n * CSS class for the output container.\n */\n outputClass?: string;\n}\n\nconst DEFAULT_CONFIG: SideBySideEditorConfig = {\n debounceDelay: 300,\n syncScroll: true,\n};\n\n/**\n * SideBySideEditor creates and manages editor and output components.\n *\n * Usage:\n * ```typescript\n * const editor = new SideBySideEditor({\n * initialSource: \"s r g m p\",\n * onSourceChange: (source) => console.log(\"Source:\", source),\n * });\n *\n * // Get the elements to place in your layout\n * const editorElement = editor.editorElement;\n * const outputElement = editor.outputElement;\n *\n * // Place them in your layout (e.g., dockview, grid, etc.)\n * leftPanel.appendChild(editorElement);\n * rightPanel.appendChild(outputElement);\n * ```\n */\nexport default class SideBySideEditor {\n /** The editor textarea element */\n readonly editorElement: HTMLTextAreaElement;\n\n /** The output container element (contains NotationView) */\n readonly outputElement: HTMLDivElement;\n\n /** The NotationView instance */\n readonly notationView: NotationView;\n\n /** Configuration */\n readonly config: SideBySideEditorConfig;\n\n /** Current notation (after successful parse) */\n private notation: Notation | null = null;\n\n /** Current beat layout (after successful parse) */\n private beatLayout: GlobalBeatLayout | null = null;\n\n /** Debounce timer */\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Whether scroll sync is active */\n private isScrollSyncing = false;\n\n /**\n * Creates a new SideBySideEditor.\n *\n * @param config Configuration options\n */\n constructor(config: SideBySideEditorConfig = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n\n // Create editor textarea\n this.editorElement = document.createElement(\"textarea\");\n this.editorElement.className = \"side-by-side-editor\";\n if (this.config.editorClass) {\n this.editorElement.className += \" \" + this.config.editorClass;\n }\n this.editorElement.spellcheck = false;\n this.editorElement.placeholder = \"Enter notation source...\";\n\n // Set initial source\n if (this.config.initialSource) {\n this.editorElement.value = this.config.initialSource;\n }\n\n // Create output container\n this.outputElement = document.createElement(\"div\");\n this.outputElement.className = \"side-by-side-output\";\n if (this.config.outputClass) {\n this.outputElement.className += \" \" + this.config.outputClass;\n }\n\n // Create NotationView\n this.notationView = new NotationView(this.outputElement, {\n sharedGridLayoutGroup: this.config.sharedGridLayoutGroup,\n markdownParser: this.config.markdownParser,\n });\n\n // Set up event listeners\n this.setupEventListeners();\n\n // Initial render\n if (this.config.initialSource) {\n this.render();\n }\n }\n\n /**\n * Gets the current source text.\n */\n get source(): string {\n return this.editorElement.value;\n }\n\n /**\n * Sets the source text and re-renders.\n */\n set source(value: string) {\n this.editorElement.value = value;\n this.render();\n }\n\n /**\n * Gets the current notation (null if parse failed).\n */\n getNotation(): Notation | null {\n return this.notation;\n }\n\n /**\n * Gets the current beat layout (null if parse failed).\n */\n getBeatLayout(): GlobalBeatLayout | null {\n return this.beatLayout;\n }\n\n /**\n * Parses and renders the current source.\n * Returns true if successful, false if there were errors.\n */\n render(): boolean {\n const source = this.editorElement.value;\n\n // Clear previous render\n this.notationView.clear();\n\n // Parse\n const [notation, beatLayout, errors] = load(source, { log: false });\n\n if (errors.length > 0) {\n this.notation = null;\n this.beatLayout = null;\n this.config.onParseError?.(errors);\n return false;\n }\n\n // Store results\n this.notation = notation;\n this.beatLayout = beatLayout;\n\n // Render\n this.notationView.renderNotation(notation, beatLayout);\n\n // Notify callback\n this.config.onNotationParsed?.(notation, beatLayout);\n\n return true;\n }\n\n /**\n * Sets up event listeners for the editor.\n */\n private setupEventListeners(): void {\n // Input handler with debouncing\n this.editorElement.addEventListener(\"input\", () => {\n this.config.onSourceChange?.(this.editorElement.value);\n\n if (this.config.debounceDelay && this.config.debounceDelay > 0) {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n this.render();\n }, this.config.debounceDelay);\n }\n });\n\n // Scroll synchronization\n if (this.config.syncScroll) {\n this.editorElement.addEventListener(\"scroll\", () => {\n this.syncScrollToOutput();\n });\n\n this.outputElement.addEventListener(\"scroll\", () => {\n this.syncScrollToEditor();\n });\n }\n }\n\n /**\n * Synchronizes scroll from editor to output.\n */\n private syncScrollToOutput(): void {\n if (this.isScrollSyncing) return;\n this.isScrollSyncing = true;\n\n const editor = this.editorElement;\n const output = this.outputElement;\n\n // Calculate scroll percentage\n const maxScrollTop = editor.scrollHeight - editor.clientHeight;\n if (maxScrollTop <= 0) {\n this.isScrollSyncing = false;\n return;\n }\n\n const scrollPercent = editor.scrollTop / maxScrollTop;\n\n // Apply to output\n const outputMaxScroll = output.scrollHeight - output.clientHeight;\n output.scrollTop = scrollPercent * outputMaxScroll;\n\n this.isScrollSyncing = false;\n }\n\n /**\n * Synchronizes scroll from output to editor.\n */\n private syncScrollToEditor(): void {\n if (this.isScrollSyncing) return;\n this.isScrollSyncing = true;\n\n const editor = this.editorElement;\n const output = this.outputElement;\n\n // Calculate scroll percentage\n const maxScrollTop = output.scrollHeight - output.clientHeight;\n if (maxScrollTop <= 0) {\n this.isScrollSyncing = false;\n return;\n }\n\n const scrollPercent = output.scrollTop / maxScrollTop;\n\n // Apply to editor\n const editorMaxScroll = editor.scrollHeight - editor.clientHeight;\n editor.scrollTop = scrollPercent * editorMaxScroll;\n\n this.isScrollSyncing = false;\n }\n\n /**\n * Scrolls both editor and output to the top.\n */\n scrollToTop(): void {\n this.editorElement.scrollTop = 0;\n this.outputElement.scrollTop = 0;\n }\n\n /**\n * Scrolls both editor and output to the bottom.\n */\n scrollToBottom(): void {\n this.editorElement.scrollTop = this.editorElement.scrollHeight;\n this.outputElement.scrollTop = this.outputElement.scrollHeight;\n }\n\n /**\n * Scrolls to a specific line number in the editor.\n */\n scrollToLine(lineNumber: number): void {\n const lines = this.editorElement.value.split(\"\\n\");\n const lineHeight = this.editorElement.scrollHeight / lines.length;\n this.editorElement.scrollTop = (lineNumber - 1) * lineHeight;\n this.syncScrollToOutput();\n }\n\n /**\n * Focuses the editor.\n */\n focus(): void {\n this.editorElement.focus();\n }\n\n /**\n * Inserts text at the current cursor position.\n */\n insertAtCursor(text: string): void {\n const start = this.editorElement.selectionStart;\n const end = this.editorElement.selectionEnd;\n const value = this.editorElement.value;\n\n this.editorElement.value = value.substring(0, start) + text + value.substring(end);\n\n // Move cursor after inserted text\n this.editorElement.selectionStart = start + text.length;\n this.editorElement.selectionEnd = start + text.length;\n\n // Trigger source change\n this.config.onSourceChange?.(this.editorElement.value);\n }\n\n /**\n * Gets the current selection range.\n */\n getSelection(): { start: number; end: number; text: string } {\n return {\n start: this.editorElement.selectionStart,\n end: this.editorElement.selectionEnd,\n text: this.editorElement.value.substring(this.editorElement.selectionStart, this.editorElement.selectionEnd),\n };\n }\n\n /**\n * Sets the selection range.\n */\n setSelection(start: number, end: number): void {\n this.editorElement.selectionStart = start;\n this.editorElement.selectionEnd = end;\n }\n\n /**\n * Cleans up resources.\n */\n destroy(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.editorElement.remove();\n this.outputElement.remove();\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export { default as DockViewPlayground, DockViewPlaygroundConfig } from "./components/DockViewPlayground";
2
+ export { default as SideBySideEditor, SideBySideEditorConfig } from "./components/SideBySideEditor";
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SideBySideEditor = exports.DockViewPlayground = void 0;
7
+ var DockViewPlayground_1 = require("./components/DockViewPlayground");
8
+ Object.defineProperty(exports, "DockViewPlayground", { enumerable: true, get: function () { return __importDefault(DockViewPlayground_1).default; } });
9
+ var SideBySideEditor_1 = require("./components/SideBySideEditor");
10
+ Object.defineProperty(exports, "SideBySideEditor", { enumerable: true, get: function () { return __importDefault(SideBySideEditor_1).default; } });
11
+ //# sourceMappingURL=dockview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dockview.js","sourceRoot":"","sources":["../../../src/web/dockview.ts"],"names":[],"mappings":";;;;;;AAgBA,sEAA0G;AAAjG,yIAAA,OAAO,OAAsB;AAGtC,kEAAoG;AAA3F,qIAAA,OAAO,OAAoB","sourcesContent":["/**\n * DockView-based components for notations/web.\n *\n * Import from \"notations/web/dockview\" to use these components.\n * This keeps DockView out of the main bundle for users who don't need it.\n *\n * Usage:\n * ```typescript\n * import { DockViewPlayground } from \"notations/web/dockview\";\n *\n * const playground = new DockViewPlayground(container, {\n * initialSource: \"s r g m p\",\n * });\n * ```\n */\n\nexport { default as DockViewPlayground, DockViewPlaygroundConfig } from \"./components/DockViewPlayground\";\n\n// Re-export SideBySideEditor for convenience\nexport { default as SideBySideEditor, SideBySideEditorConfig } from \"./components/SideBySideEditor\";\n"]}
@@ -0,0 +1,8 @@
1
+ export { default as NotationBlock, NotationBlockConfig } from "./components/NotationBlock";
2
+ export { default as NotebookView } from "./components/NotebookView";
3
+ export { default as NotebookCell, NotebookCellConfig } from "./components/NotebookCell";
4
+ export { default as SideBySideEditor, SideBySideEditorConfig } from "./components/SideBySideEditor";
5
+ export { default as DockViewPlayground, DockViewPlaygroundConfig } from "./components/DockViewPlayground";
6
+ export * from "./types/notebook";
7
+ export * from "./utils/cellFactory";
8
+ export * from "./utils/sourceSerializer";
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.DockViewPlayground = exports.SideBySideEditor = exports.NotebookCell = exports.NotebookView = exports.NotationBlock = void 0;
21
+ var NotationBlock_1 = require("./components/NotationBlock");
22
+ Object.defineProperty(exports, "NotationBlock", { enumerable: true, get: function () { return __importDefault(NotationBlock_1).default; } });
23
+ var NotebookView_1 = require("./components/NotebookView");
24
+ Object.defineProperty(exports, "NotebookView", { enumerable: true, get: function () { return __importDefault(NotebookView_1).default; } });
25
+ var NotebookCell_1 = require("./components/NotebookCell");
26
+ Object.defineProperty(exports, "NotebookCell", { enumerable: true, get: function () { return __importDefault(NotebookCell_1).default; } });
27
+ var SideBySideEditor_1 = require("./components/SideBySideEditor");
28
+ Object.defineProperty(exports, "SideBySideEditor", { enumerable: true, get: function () { return __importDefault(SideBySideEditor_1).default; } });
29
+ var DockViewPlayground_1 = require("./components/DockViewPlayground");
30
+ Object.defineProperty(exports, "DockViewPlayground", { enumerable: true, get: function () { return __importDefault(DockViewPlayground_1).default; } });
31
+ __exportStar(require("./types/notebook"), exports);
32
+ __exportStar(require("./utils/cellFactory"), exports);
33
+ __exportStar(require("./utils/sourceSerializer"), exports);
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/web/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA,4DAA2F;AAAlF,+HAAA,OAAO,OAAiB;AACjC,0DAAoE;AAA3D,6HAAA,OAAO,OAAgB;AAChC,0DAAwF;AAA/E,6HAAA,OAAO,OAAgB;AAChC,kEAAoG;AAA3F,qIAAA,OAAO,OAAoB;AACpC,sEAA0G;AAAjG,yIAAA,OAAO,OAAsB;AAGtC,mDAAiC;AACjC,sDAAoC;AACpC,2DAAyC","sourcesContent":["// Export all web components\nexport { default as NotationBlock, NotationBlockConfig } from \"./components/NotationBlock\";\nexport { default as NotebookView } from \"./components/NotebookView\";\nexport { default as NotebookCell, NotebookCellConfig } from \"./components/NotebookCell\";\nexport { default as SideBySideEditor, SideBySideEditorConfig } from \"./components/SideBySideEditor\";\nexport { default as DockViewPlayground, DockViewPlaygroundConfig } from \"./components/DockViewPlayground\";\n\n// Export notebook types and utilities\nexport * from \"./types/notebook\";\nexport * from \"./utils/cellFactory\";\nexport * from \"./utils/sourceSerializer\";\n"]}
@@ -0,0 +1,64 @@
1
+ import type { BlockItem } from "../../block";
2
+ import type { Notation } from "../../notation";
3
+ import type { GridLayoutGroup, LayoutChangeEvent } from "../../grids";
4
+ export type { LayoutChangeEvent };
5
+ export interface CellState {
6
+ id: string;
7
+ type: string;
8
+ depth: number;
9
+ isExpanded: boolean;
10
+ isEditing: boolean;
11
+ hasError: boolean;
12
+ errorMessage?: string;
13
+ }
14
+ export interface CellModel {
15
+ blockItem: BlockItem;
16
+ state: CellState;
17
+ source: string;
18
+ sourceRange?: {
19
+ start: number;
20
+ end: number;
21
+ };
22
+ children: CellModel[];
23
+ parent: CellModel | null;
24
+ }
25
+ export interface NotebookConfig {
26
+ maxDepth: number;
27
+ sharedGridLayoutGroup?: GridLayoutGroup;
28
+ enableReordering?: boolean;
29
+ enableAddCell?: boolean;
30
+ enableDeleteCell?: boolean;
31
+ onNotationChange?: (source: string, notation: Notation | null) => void;
32
+ onCellError?: (cellId: string, error: Error) => void;
33
+ onLayoutChange?: (event: LayoutChangeEvent) => void;
34
+ markdownParser?: (content: string) => string;
35
+ cssClasses?: NotebookCssClasses;
36
+ }
37
+ export interface NotebookCssClasses {
38
+ root?: string;
39
+ cell?: string;
40
+ cellHeader?: string;
41
+ cellContent?: string;
42
+ cellControls?: string;
43
+ addCellButton?: string;
44
+ editTextarea?: string;
45
+ errorMessage?: string;
46
+ }
47
+ export interface CellOperations {
48
+ startEdit(cellId: string): void;
49
+ applyEdit(cellId: string, newSource: string): void;
50
+ cancelEdit(cellId: string): void;
51
+ deleteCell(cellId: string): void;
52
+ moveCell(cellId: string, targetIndex: number): void;
53
+ insertCell(index: number, source: string): CellModel;
54
+ toggleExpanded(cellId: string): void;
55
+ }
56
+ export interface CellTypeBadge {
57
+ label: string;
58
+ cssClass: string;
59
+ icon?: string;
60
+ }
61
+ export declare const CELL_TYPE_BADGES: Record<string, CellTypeBadge>;
62
+ export declare function getCellTypeBadge(blockType: string): CellTypeBadge;
63
+ export declare function generateCellId(): string;
64
+ export declare function createDefaultCellState(blockItem: BlockItem, depth: number): CellState;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CELL_TYPE_BADGES = void 0;
4
+ exports.getCellTypeBadge = getCellTypeBadge;
5
+ exports.generateCellId = generateCellId;
6
+ exports.createDefaultCellState = createDefaultCellState;
7
+ exports.CELL_TYPE_BADGES = {
8
+ notation: { label: "Root", cssClass: "badge-notation" },
9
+ section: { label: "Section", cssClass: "badge-section" },
10
+ repeat: { label: "Repeat", cssClass: "badge-repeat" },
11
+ cycle: { label: "Cycle", cssClass: "badge-cycle" },
12
+ beatduration: { label: "Beat", cssClass: "badge-beatduration" },
13
+ breaks: { label: "Breaks", cssClass: "badge-breaks" },
14
+ role: { label: "Role", cssClass: "badge-role" },
15
+ group: { label: "Group", cssClass: "badge-group" },
16
+ line: { label: "Line", cssClass: "badge-line" },
17
+ rawblock: { label: "Text", cssClass: "badge-rawblock" },
18
+ };
19
+ function getCellTypeBadge(blockType) {
20
+ const badge = exports.CELL_TYPE_BADGES[blockType.toLowerCase()];
21
+ if (badge) {
22
+ return badge;
23
+ }
24
+ return {
25
+ label: blockType,
26
+ cssClass: "badge-unknown",
27
+ };
28
+ }
29
+ let cellIdCounter = 0;
30
+ function generateCellId() {
31
+ return `cell-${++cellIdCounter}`;
32
+ }
33
+ function createDefaultCellState(blockItem, depth) {
34
+ let type;
35
+ if (blockItem.TYPE === "Block") {
36
+ type = blockItem.blockType;
37
+ }
38
+ else if (blockItem.TYPE === "Line") {
39
+ type = "line";
40
+ }
41
+ else if (blockItem.TYPE === "RawBlock") {
42
+ type = "rawblock";
43
+ }
44
+ else {
45
+ type = "unknown";
46
+ }
47
+ return {
48
+ id: generateCellId(),
49
+ type,
50
+ depth,
51
+ isExpanded: true,
52
+ isEditing: false,
53
+ hasError: false,
54
+ };
55
+ }
56
+ //# sourceMappingURL=notebook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notebook.js","sourceRoot":"","sources":["../../../../src/web/types/notebook.ts"],"names":[],"mappings":";;;AAoOA,4CAUC;AAMD,wCAEC;AAOD,wDAqBC;AAjEY,QAAA,gBAAgB,GAAkC;IAC7D,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE;IACvD,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE;IACxD,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE;IACrD,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE;IAClD,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE;IAC/D,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE;IACrD,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE;IAC/C,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE;IAElD,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE;IAC/C,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE;CACxD,CAAC;AAOF,SAAgB,gBAAgB,CAAC,SAAiB;IAChD,MAAM,KAAK,GAAG,wBAAgB,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;IACxD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO;QACL,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,eAAe;KAC1B,CAAC;AACJ,CAAC;AAKD,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,SAAgB,cAAc;IAC5B,OAAO,QAAQ,EAAE,aAAa,EAAE,CAAC;AACnC,CAAC;AAOD,SAAgB,sBAAsB,CAAC,SAAoB,EAAE,KAAa;IAExE,IAAI,IAAY,CAAC;IACjB,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC/B,IAAI,GAAI,SAAmB,CAAC,SAAS,CAAC;IACxC,CAAC;SAAM,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACrC,IAAI,GAAG,MAAM,CAAC;IAChB,CAAC;SAAM,IAAI,SAAS,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACzC,IAAI,GAAG,UAAU,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,EAAE,EAAE,cAAc,EAAE;QACpB,IAAI;QACJ,KAAK;QACL,UAAU,EAAE,IAAI;QAChB,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,KAAK;KAChB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Types and interfaces for the notebook-style cell editing UI.\n *\n * The notebook UI represents a Notation as a collection of editable cells,\n * where each Block becomes a cell with preview/edit modes.\n */\n\nimport type { BlockItem, Block } from \"../../block\";\nimport type { Notation } from \"../../notation\";\nimport type { GridLayoutGroup, LayoutChangeEvent } from \"../../grids\";\n\n// Re-export for convenience\nexport type { LayoutChangeEvent };\n\n/**\n * State of a single cell in the notebook.\n */\nexport interface CellState {\n /** Unique identifier for this cell */\n id: string;\n\n /** Block type from block.blockType (e.g., \"section\", \"repeat\", \"notation\") */\n type: string;\n\n /** Nesting depth in the block hierarchy (0 = top level) */\n depth: number;\n\n /** Whether nested children are expanded (for collapsible blocks) */\n isExpanded: boolean;\n\n /** Whether the cell is currently in edit mode */\n isEditing: boolean;\n\n /** Whether parsing the cell's source resulted in an error */\n hasError: boolean;\n\n /** Error message if hasError is true */\n errorMessage?: string;\n}\n\n/**\n * Model representing a cell in the notebook.\n * Wraps a BlockItem with UI state and source tracking.\n */\nexport interface CellModel {\n /** The underlying block item from the notation */\n blockItem: BlockItem;\n\n /** UI state for this cell */\n state: CellState;\n\n /** Source text for this cell (extracted from original notation source) */\n source: string;\n\n /** Source range in original notation (for serialization back to source) */\n sourceRange?: {\n start: number;\n end: number;\n };\n\n /** Child cells (for nested blocks) */\n children: CellModel[];\n\n /** Parent cell (null for top-level cells) */\n parent: CellModel | null;\n}\n\n/**\n * Configuration options for the NotebookView component.\n */\nexport interface NotebookConfig {\n /**\n * Maximum depth of block nesting to expose as cells.\n * 0 = only show top-level blocks\n * 1 = show one level of nesting\n * Infinity = show all nesting levels\n * @default 1\n */\n maxDepth: number;\n\n /**\n * Optional shared GridLayoutGroup for column alignment across cells.\n * When provided, all NotationViews in the notebook share this layout group.\n * If not provided, a new one is created internally.\n */\n sharedGridLayoutGroup?: GridLayoutGroup;\n\n /**\n * Whether to allow reordering cells via drag-and-drop.\n * @default false\n */\n enableReordering?: boolean;\n\n /**\n * Whether to allow adding new cells.\n * @default true\n */\n enableAddCell?: boolean;\n\n /**\n * Whether to allow deleting cells.\n * @default true\n */\n enableDeleteCell?: boolean;\n\n /**\n * Callback when notation source changes (after cell edit is applied).\n * @param source The new full notation source\n * @param notation The parsed Notation object (if parsing succeeded)\n */\n onNotationChange?: (source: string, notation: Notation | null) => void;\n\n /**\n * Callback when a cell encounters a parse error.\n * @param cellId The ID of the cell with the error\n * @param error The error that occurred\n */\n onCellError?: (cellId: string, error: Error) => void;\n\n /**\n * Callback when layout changes (column widths/row heights changed).\n * Useful for external components that need to react to layout changes.\n */\n onLayoutChange?: (event: LayoutChangeEvent) => void;\n\n /**\n * Optional markdown parser for RawBlock content.\n * If not provided, markdown is rendered as plain text.\n */\n markdownParser?: (content: string) => string;\n\n /**\n * CSS classes to apply to various elements.\n */\n cssClasses?: NotebookCssClasses;\n}\n\n/**\n * CSS class overrides for notebook elements.\n */\nexport interface NotebookCssClasses {\n /** Root container class */\n root?: string;\n\n /** Individual cell container */\n cell?: string;\n\n /** Cell header (type badge, name, controls) */\n cellHeader?: string;\n\n /** Cell content area (preview or edit mode) */\n cellContent?: string;\n\n /** Cell controls (edit/delete/move buttons) */\n cellControls?: string;\n\n /** Add cell button */\n addCellButton?: string;\n\n /** Edit mode textarea */\n editTextarea?: string;\n\n /** Error message display */\n errorMessage?: string;\n}\n\n/**\n * Operations that can be performed on cells.\n */\nexport interface CellOperations {\n /** Enter edit mode for a cell */\n startEdit(cellId: string): void;\n\n /** Apply changes and exit edit mode */\n applyEdit(cellId: string, newSource: string): void;\n\n /** Cancel edit mode without applying changes */\n cancelEdit(cellId: string): void;\n\n /** Delete a cell */\n deleteCell(cellId: string): void;\n\n /** Move a cell to a new position */\n moveCell(cellId: string, targetIndex: number): void;\n\n /** Insert a new cell at the given index */\n insertCell(index: number, source: string): CellModel;\n\n /** Toggle expanded state for a cell with children */\n toggleExpanded(cellId: string): void;\n}\n\n/**\n * Badge info for displaying cell type.\n */\nexport interface CellTypeBadge {\n /** Display label for the badge */\n label: string;\n\n /** CSS class for styling (e.g., \"badge-section\", \"badge-repeat\") */\n cssClass: string;\n\n /** Optional icon (SVG string or class name) */\n icon?: string;\n}\n\n/**\n * Maps block types to badge display info.\n */\nexport const CELL_TYPE_BADGES: Record<string, CellTypeBadge> = {\n notation: { label: \"Root\", cssClass: \"badge-notation\" },\n section: { label: \"Section\", cssClass: \"badge-section\" },\n repeat: { label: \"Repeat\", cssClass: \"badge-repeat\" },\n cycle: { label: \"Cycle\", cssClass: \"badge-cycle\" },\n beatduration: { label: \"Beat\", cssClass: \"badge-beatduration\" },\n breaks: { label: \"Breaks\", cssClass: \"badge-breaks\" },\n role: { label: \"Role\", cssClass: \"badge-role\" },\n group: { label: \"Group\", cssClass: \"badge-group\" },\n // For Line and RawBlock (not blocks, but can be cells)\n line: { label: \"Line\", cssClass: \"badge-line\" },\n rawblock: { label: \"Text\", cssClass: \"badge-rawblock\" },\n};\n\n/**\n * Gets badge info for a block type.\n * @param blockType The block type string\n * @returns Badge info, or a default badge for unknown types\n */\nexport function getCellTypeBadge(blockType: string): CellTypeBadge {\n const badge = CELL_TYPE_BADGES[blockType.toLowerCase()];\n if (badge) {\n return badge;\n }\n // Default badge for unknown types\n return {\n label: blockType,\n cssClass: \"badge-unknown\",\n };\n}\n\n/**\n * Generates a unique cell ID.\n */\nlet cellIdCounter = 0;\nexport function generateCellId(): string {\n return `cell-${++cellIdCounter}`;\n}\n\n/**\n * Creates default cell state for a block item.\n * @param blockItem The block item to create state for\n * @param depth The nesting depth\n */\nexport function createDefaultCellState(blockItem: BlockItem, depth: number): CellState {\n // Determine the type string\n let type: string;\n if (blockItem.TYPE === \"Block\") {\n type = (blockItem as Block).blockType;\n } else if (blockItem.TYPE === \"Line\") {\n type = \"line\";\n } else if (blockItem.TYPE === \"RawBlock\") {\n type = \"rawblock\";\n } else {\n type = \"unknown\";\n }\n\n return {\n id: generateCellId(),\n type,\n depth,\n isExpanded: true,\n isEditing: false,\n hasError: false,\n };\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { Notation } from "../../notation";
2
+ import { CellModel, CellState } from "../types/notebook";
3
+ export interface CellFactoryOptions {
4
+ maxDepth: number;
5
+ notationSource?: string;
6
+ includeLines?: boolean;
7
+ includeRawBlocks?: boolean;
8
+ }
9
+ export declare function createCellModels(notation: Notation, options?: Partial<CellFactoryOptions>): CellModel[];
10
+ export declare function createCellModelWithRoot(notation: Notation, options?: Partial<CellFactoryOptions>): CellModel;
11
+ export declare function findCellById(cells: CellModel[], cellId: string): CellModel | null;
12
+ export declare function findCellIndex(cell: CellModel): number;
13
+ export declare function flattenCells(cells: CellModel[]): CellModel[];
14
+ export declare function getVisibleCells(cells: CellModel[]): CellModel[];
15
+ export declare function updateCellState(cell: CellModel, updates: Partial<CellState>): CellModel;
16
+ export declare function regenerateCellIds(cells: CellModel[]): CellModel[];
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCellModels = createCellModels;
4
+ exports.createCellModelWithRoot = createCellModelWithRoot;
5
+ exports.findCellById = findCellById;
6
+ exports.findCellIndex = findCellIndex;
7
+ exports.flattenCells = flattenCells;
8
+ exports.getVisibleCells = getVisibleCells;
9
+ exports.updateCellState = updateCellState;
10
+ exports.regenerateCellIds = regenerateCellIds;
11
+ const block_1 = require("../../block");
12
+ const notebook_1 = require("../types/notebook");
13
+ const DEFAULT_OPTIONS = {
14
+ maxDepth: 1,
15
+ includeLines: true,
16
+ includeRawBlocks: true,
17
+ };
18
+ function createCellModels(notation, options = {}) {
19
+ const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
20
+ const rootCell = createCellForBlock(notation, 0, opts, null);
21
+ return rootCell.children;
22
+ }
23
+ function createCellModelWithRoot(notation, options = {}) {
24
+ const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
25
+ return createCellForBlock(notation, 0, opts, null);
26
+ }
27
+ function createCellForBlock(block, depth, options, parent) {
28
+ const state = (0, notebook_1.createDefaultCellState)(block, depth);
29
+ const source = extractBlockSource(block, options.notationSource);
30
+ const cell = {
31
+ blockItem: block,
32
+ state,
33
+ source,
34
+ children: [],
35
+ parent,
36
+ };
37
+ if (depth < options.maxDepth) {
38
+ for (const child of block.blockItems) {
39
+ const childCell = createCellForBlockItem(child, depth + 1, options, cell);
40
+ if (childCell) {
41
+ cell.children.push(childCell);
42
+ }
43
+ }
44
+ }
45
+ return cell;
46
+ }
47
+ function createCellForBlockItem(item, depth, options, parent) {
48
+ if ((0, block_1.isBlock)(item)) {
49
+ return createCellForBlock(item, depth, options, parent);
50
+ }
51
+ if ((0, block_1.isLine)(item)) {
52
+ if (!options.includeLines) {
53
+ return null;
54
+ }
55
+ return createCellForLine(item, depth, options, parent);
56
+ }
57
+ if ((0, block_1.isRawBlock)(item)) {
58
+ if (!options.includeRawBlocks) {
59
+ return null;
60
+ }
61
+ return createCellForRawBlock(item, depth, options, parent);
62
+ }
63
+ return null;
64
+ }
65
+ function createCellForLine(line, depth, options, parent) {
66
+ const state = (0, notebook_1.createDefaultCellState)(line, depth);
67
+ const source = extractLineSource(line, options.notationSource);
68
+ return {
69
+ blockItem: line,
70
+ state,
71
+ source,
72
+ children: [],
73
+ parent,
74
+ };
75
+ }
76
+ function createCellForRawBlock(rawBlock, depth, options, parent) {
77
+ const state = (0, notebook_1.createDefaultCellState)(rawBlock, depth);
78
+ return {
79
+ blockItem: rawBlock,
80
+ state,
81
+ source: rawBlock.content,
82
+ children: [],
83
+ parent,
84
+ };
85
+ }
86
+ function extractBlockSource(block, notationSource) {
87
+ if (block.name) {
88
+ return `\\${block.blockType}("${block.name}") { ... }`;
89
+ }
90
+ return `\\${block.blockType} { ... }`;
91
+ }
92
+ function extractLineSource(line, notationSource) {
93
+ return "[Line content]";
94
+ }
95
+ function findCellById(cells, cellId) {
96
+ for (const cell of cells) {
97
+ if (cell.state.id === cellId) {
98
+ return cell;
99
+ }
100
+ const found = findCellById(cell.children, cellId);
101
+ if (found) {
102
+ return found;
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ function findCellIndex(cell) {
108
+ if (!cell.parent) {
109
+ return -1;
110
+ }
111
+ return cell.parent.children.indexOf(cell);
112
+ }
113
+ function flattenCells(cells) {
114
+ const result = [];
115
+ for (const cell of cells) {
116
+ result.push(cell);
117
+ result.push(...flattenCells(cell.children));
118
+ }
119
+ return result;
120
+ }
121
+ function getVisibleCells(cells) {
122
+ const result = [];
123
+ for (const cell of cells) {
124
+ result.push(cell);
125
+ if (cell.state.isExpanded) {
126
+ result.push(...getVisibleCells(cell.children));
127
+ }
128
+ }
129
+ return result;
130
+ }
131
+ function updateCellState(cell, updates) {
132
+ return Object.assign(Object.assign({}, cell), { state: Object.assign(Object.assign({}, cell.state), updates) });
133
+ }
134
+ function regenerateCellIds(cells) {
135
+ return cells.map((cell) => (Object.assign(Object.assign({}, cell), { state: Object.assign(Object.assign({}, cell.state), { id: (0, notebook_1.generateCellId)() }), children: regenerateCellIds(cell.children) })));
136
+ }
137
+ //# sourceMappingURL=cellFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cellFactory.js","sourceRoot":"","sources":["../../../../src/web/utils/cellFactory.ts"],"names":[],"mappings":";;AA0DA,4CASC;AASD,0DAGC;AA2ID,oCAWC;AAQD,sCAKC;AAQD,oCAOC;AAQD,0CASC;AASD,0CAQC;AAQD,8CASC;AA1SD,uCAA0D;AAC1D,gDAAiG;AAkCjG,MAAM,eAAe,GAAuB;IAC1C,QAAQ,EAAE,CAAC;IACX,YAAY,EAAE,IAAI;IAClB,gBAAgB,EAAE,IAAI;CACvB,CAAC;AASF,SAAgB,gBAAgB,CAAC,QAAkB,EAAE,UAAuC,EAAE;IAC5F,MAAM,IAAI,mCAAQ,eAAe,GAAK,OAAO,CAAE,CAAC;IAGhD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAI7D,OAAO,QAAQ,CAAC,QAAQ,CAAC;AAC3B,CAAC;AASD,SAAgB,uBAAuB,CAAC,QAAkB,EAAE,UAAuC,EAAE;IACnG,MAAM,IAAI,mCAAQ,eAAe,GAAK,OAAO,CAAE,CAAC;IAChD,OAAO,kBAAkB,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAKD,SAAS,kBAAkB,CACzB,KAAY,EACZ,KAAa,EACb,OAA2B,EAC3B,MAAwB;IAExB,MAAM,KAAK,GAAG,IAAA,iCAAsB,EAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAc;QACtB,SAAS,EAAE,KAAK;QAChB,KAAK;QACL,MAAM;QACN,QAAQ,EAAE,EAAE;QACZ,MAAM;KACP,CAAC;IAGF,IAAI,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1E,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAMD,SAAS,sBAAsB,CAC7B,IAAe,EACf,KAAa,EACb,OAA2B,EAC3B,MAAwB;IAExB,IAAI,IAAA,eAAO,EAAC,IAAI,CAAC,EAAE,CAAC;QAClB,OAAO,kBAAkB,CAAC,IAAa,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,IAAA,cAAM,EAAC,IAAI,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,iBAAiB,CAAC,IAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,IAAA,kBAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,qBAAqB,CAAC,IAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAKD,SAAS,iBAAiB,CACxB,IAAU,EACV,KAAa,EACb,OAA2B,EAC3B,MAAwB;IAExB,MAAM,KAAK,GAAG,IAAA,iCAAsB,EAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAE/D,OAAO;QACL,SAAS,EAAE,IAAI;QACf,KAAK;QACL,MAAM;QACN,QAAQ,EAAE,EAAE;QACZ,MAAM;KACP,CAAC;AACJ,CAAC;AAKD,SAAS,qBAAqB,CAC5B,QAAkB,EAClB,KAAa,EACb,OAA2B,EAC3B,MAAwB;IAExB,MAAM,KAAK,GAAG,IAAA,iCAAsB,EAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAEtD,OAAO;QACL,SAAS,EAAE,QAAQ;QACnB,KAAK;QACL,MAAM,EAAE,QAAQ,CAAC,OAAO;QACxB,QAAQ,EAAE,EAAE;QACZ,MAAM;KACP,CAAC;AACJ,CAAC;AAOD,SAAS,kBAAkB,CAAC,KAAY,EAAE,cAAuB;IAI/D,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,KAAK,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,IAAI,YAAY,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,KAAK,CAAC,SAAS,UAAU,CAAC;AACxC,CAAC;AAMD,SAAS,iBAAiB,CAAC,IAAU,EAAE,cAAuB;IAG5D,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AASD,SAAgB,YAAY,CAAC,KAAkB,EAAE,MAAc;IAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClD,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAQD,SAAgB,aAAa,CAAC,IAAe;IAC3C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO,CAAC,CAAC,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAQD,SAAgB,YAAY,CAAC,KAAkB;IAC7C,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAQD,SAAgB,eAAe,CAAC,KAAkB;IAChD,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AASD,SAAgB,eAAe,CAAC,IAAe,EAAE,OAA2B;IAC1E,uCACK,IAAI,KACP,KAAK,kCACA,IAAI,CAAC,KAAK,GACV,OAAO,KAEZ;AACJ,CAAC;AAQD,SAAgB,iBAAiB,CAAC,KAAkB;IAClD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iCACtB,IAAI,KACP,KAAK,kCACA,IAAI,CAAC,KAAK,KACb,EAAE,EAAE,IAAA,yBAAc,GAAE,KAEtB,QAAQ,EAAE,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAC1C,CAAC,CAAC;AACN,CAAC","sourcesContent":["/**\n * Factory for converting Block hierarchy to CellModel tree.\n *\n * The cell factory walks the notation's block structure and creates\n * a parallel tree of CellModel objects suitable for the notebook UI.\n */\n\nimport type { BlockItem, Block, RawBlock } from \"../../block\";\nimport type { Line } from \"../../core\";\nimport type { Notation } from \"../../notation\";\nimport { isBlock, isLine, isRawBlock } from \"../../block\";\nimport { CellModel, CellState, createDefaultCellState, generateCellId } from \"../types/notebook\";\n\n/**\n * Options for cell factory.\n */\nexport interface CellFactoryOptions {\n /**\n * Maximum depth of block nesting to expose as cells.\n * 0 = only show top-level blocks\n * 1 = show one level of nesting\n * Infinity = show all nesting levels\n * @default 1\n */\n maxDepth: number;\n\n /**\n * The full notation source text (for extracting cell source ranges).\n * If not provided, cell.source will be empty.\n */\n notationSource?: string;\n\n /**\n * Whether to include Line items as cells.\n * @default true\n */\n includeLines?: boolean;\n\n /**\n * Whether to include RawBlock items as cells.\n * @default true\n */\n includeRawBlocks?: boolean;\n}\n\nconst DEFAULT_OPTIONS: CellFactoryOptions = {\n maxDepth: 1,\n includeLines: true,\n includeRawBlocks: true,\n};\n\n/**\n * Creates a CellModel tree from a Notation.\n *\n * @param notation The notation to convert\n * @param options Factory options\n * @returns Array of top-level CellModel objects\n */\nexport function createCellModels(notation: Notation, options: Partial<CellFactoryOptions> = {}): CellModel[] {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Create the root cell for the notation itself\n const rootCell = createCellForBlock(notation, 0, opts, null);\n\n // Return the children of the root (top-level items)\n // The root itself represents the whole notation\n return rootCell.children;\n}\n\n/**\n * Creates a CellModel tree from a Notation, including the root cell.\n *\n * @param notation The notation to convert\n * @param options Factory options\n * @returns The root CellModel representing the entire notation\n */\nexport function createCellModelWithRoot(notation: Notation, options: Partial<CellFactoryOptions> = {}): CellModel {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n return createCellForBlock(notation, 0, opts, null);\n}\n\n/**\n * Creates a CellModel for a Block and recursively processes its children.\n */\nfunction createCellForBlock(\n block: Block,\n depth: number,\n options: CellFactoryOptions,\n parent: CellModel | null,\n): CellModel {\n const state = createDefaultCellState(block, depth);\n const source = extractBlockSource(block, options.notationSource);\n\n const cell: CellModel = {\n blockItem: block,\n state,\n source,\n children: [],\n parent,\n };\n\n // Process children if within depth limit\n if (depth < options.maxDepth) {\n for (const child of block.blockItems) {\n const childCell = createCellForBlockItem(child, depth + 1, options, cell);\n if (childCell) {\n cell.children.push(childCell);\n }\n }\n }\n\n return cell;\n}\n\n/**\n * Creates a CellModel for a BlockItem (Block, Line, or RawBlock).\n * Returns null if the item type is excluded by options.\n */\nfunction createCellForBlockItem(\n item: BlockItem,\n depth: number,\n options: CellFactoryOptions,\n parent: CellModel | null,\n): CellModel | null {\n if (isBlock(item)) {\n return createCellForBlock(item as Block, depth, options, parent);\n }\n\n if (isLine(item)) {\n if (!options.includeLines) {\n return null;\n }\n return createCellForLine(item as Line, depth, options, parent);\n }\n\n if (isRawBlock(item)) {\n if (!options.includeRawBlocks) {\n return null;\n }\n return createCellForRawBlock(item as RawBlock, depth, options, parent);\n }\n\n return null;\n}\n\n/**\n * Creates a CellModel for a Line.\n */\nfunction createCellForLine(\n line: Line,\n depth: number,\n options: CellFactoryOptions,\n parent: CellModel | null,\n): CellModel {\n const state = createDefaultCellState(line, depth);\n const source = extractLineSource(line, options.notationSource);\n\n return {\n blockItem: line,\n state,\n source,\n children: [], // Lines don't have children\n parent,\n };\n}\n\n/**\n * Creates a CellModel for a RawBlock.\n */\nfunction createCellForRawBlock(\n rawBlock: RawBlock,\n depth: number,\n options: CellFactoryOptions,\n parent: CellModel | null,\n): CellModel {\n const state = createDefaultCellState(rawBlock, depth);\n\n return {\n blockItem: rawBlock,\n state,\n source: rawBlock.content, // RawBlock has its content directly\n children: [], // RawBlocks don't have children\n parent,\n };\n}\n\n/**\n * Extracts the source text for a Block from the notation source.\n * Currently returns empty string - source range tracking would need\n * parser support to track positions.\n */\nfunction extractBlockSource(block: Block, notationSource?: string): string {\n // TODO: Implement source range tracking in parser\n // For now, we don't have position info in the AST\n // This would require the parser to track source positions\n if (block.name) {\n return `\\\\${block.blockType}(\"${block.name}\") { ... }`;\n }\n return `\\\\${block.blockType} { ... }`;\n}\n\n/**\n * Extracts the source text for a Line from the notation source.\n * Currently returns a placeholder - would need parser source tracking.\n */\nfunction extractLineSource(line: Line, notationSource?: string): string {\n // TODO: Implement source range tracking in parser\n // For now, generate a placeholder representation\n return \"[Line content]\";\n}\n\n/**\n * Finds a cell by ID in a cell tree.\n *\n * @param cells Array of cells to search\n * @param cellId The ID to find\n * @returns The cell with the given ID, or null if not found\n */\nexport function findCellById(cells: CellModel[], cellId: string): CellModel | null {\n for (const cell of cells) {\n if (cell.state.id === cellId) {\n return cell;\n }\n const found = findCellById(cell.children, cellId);\n if (found) {\n return found;\n }\n }\n return null;\n}\n\n/**\n * Finds a cell's index within its parent's children array.\n *\n * @param cell The cell to find\n * @returns The index, or -1 if the cell has no parent or is not found\n */\nexport function findCellIndex(cell: CellModel): number {\n if (!cell.parent) {\n return -1;\n }\n return cell.parent.children.indexOf(cell);\n}\n\n/**\n * Gets all cells in a flat array (pre-order traversal).\n *\n * @param cells The root cells\n * @returns Flat array of all cells\n */\nexport function flattenCells(cells: CellModel[]): CellModel[] {\n const result: CellModel[] = [];\n for (const cell of cells) {\n result.push(cell);\n result.push(...flattenCells(cell.children));\n }\n return result;\n}\n\n/**\n * Gets all visible cells (respecting isExpanded state).\n *\n * @param cells The root cells\n * @returns Flat array of visible cells\n */\nexport function getVisibleCells(cells: CellModel[]): CellModel[] {\n const result: CellModel[] = [];\n for (const cell of cells) {\n result.push(cell);\n if (cell.state.isExpanded) {\n result.push(...getVisibleCells(cell.children));\n }\n }\n return result;\n}\n\n/**\n * Updates a cell's state immutably.\n *\n * @param cell The cell to update\n * @param updates Partial state updates\n * @returns A new CellModel with updated state\n */\nexport function updateCellState(cell: CellModel, updates: Partial<CellState>): CellModel {\n return {\n ...cell,\n state: {\n ...cell.state,\n ...updates,\n },\n };\n}\n\n/**\n * Recreates cell IDs for a cell tree (useful after cloning).\n *\n * @param cells The cells to update\n * @returns New cells with fresh IDs\n */\nexport function regenerateCellIds(cells: CellModel[]): CellModel[] {\n return cells.map((cell) => ({\n ...cell,\n state: {\n ...cell.state,\n id: generateCellId(),\n },\n children: regenerateCellIds(cell.children),\n }));\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import type { BlockItem } from "../../block";
2
+ import { CellModel } from "../types/notebook";
3
+ export interface SerializerOptions {
4
+ indent?: string;
5
+ lineSeparator?: string;
6
+ preserveUnmodified?: boolean;
7
+ }
8
+ export declare function serializeCells(cells: CellModel[], options?: SerializerOptions): string;
9
+ export declare function serializeCell(cell: CellModel, depth: number, options: SerializerOptions): string;
10
+ export declare function serializeBlockItem(item: BlockItem, depth: number, options: SerializerOptions): string;
11
+ export declare function updateCellSource(cell: CellModel, newSource: string): CellModel;
12
+ export declare function validateCellSource(source: string): {
13
+ isValid: boolean;
14
+ error?: string;
15
+ };
16
+ export declare function findCellSourceRange(cell: CellModel, fullSource: string): {
17
+ start: number;
18
+ end: number;
19
+ } | null;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeCells = serializeCells;
4
+ exports.serializeCell = serializeCell;
5
+ exports.serializeBlockItem = serializeBlockItem;
6
+ exports.updateCellSource = updateCellSource;
7
+ exports.validateCellSource = validateCellSource;
8
+ exports.findCellSourceRange = findCellSourceRange;
9
+ const block_1 = require("../../block");
10
+ const DEFAULT_OPTIONS = {
11
+ indent: " ",
12
+ lineSeparator: "\n",
13
+ preserveUnmodified: true,
14
+ };
15
+ function serializeCells(cells, options = {}) {
16
+ const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
17
+ const parts = [];
18
+ for (const cell of cells) {
19
+ const serialized = serializeCell(cell, 0, opts);
20
+ if (serialized) {
21
+ parts.push(serialized);
22
+ }
23
+ }
24
+ return parts.join(opts.lineSeparator + opts.lineSeparator);
25
+ }
26
+ function serializeCell(cell, depth, options) {
27
+ const indent = options.indent.repeat(depth);
28
+ if (options.preserveUnmodified && cell.source && !cell.state.hasError) {
29
+ if (depth === 0) {
30
+ return cell.source;
31
+ }
32
+ return cell.source
33
+ .split("\n")
34
+ .map((line) => indent + line)
35
+ .join(options.lineSeparator);
36
+ }
37
+ return serializeBlockItem(cell.blockItem, depth, options);
38
+ }
39
+ function serializeBlockItem(item, depth, options) {
40
+ if ((0, block_1.isRawBlock)(item)) {
41
+ return serializeRawBlock(item, depth, options);
42
+ }
43
+ if ((0, block_1.isLine)(item)) {
44
+ return serializeLine(item, depth, options);
45
+ }
46
+ if ((0, block_1.isBlock)(item)) {
47
+ return serializeBlock(item, depth, options);
48
+ }
49
+ return "";
50
+ }
51
+ function serializeRawBlock(rawBlock, depth, options) {
52
+ const indent = options.indent.repeat(depth);
53
+ const content = rawBlock.content;
54
+ if (rawBlock.contentType === "md") {
55
+ return `${indent}---${options.lineSeparator}${content}${options.lineSeparator}${indent}---`;
56
+ }
57
+ if (rawBlock.contentType === "metadata") {
58
+ return `${indent}@@${content}`;
59
+ }
60
+ return content
61
+ .split("\n")
62
+ .map((line) => indent + line)
63
+ .join(options.lineSeparator);
64
+ }
65
+ function serializeLine(line, depth, options) {
66
+ const indent = options.indent.repeat(depth);
67
+ return `${indent}; [Line serialization not fully implemented]`;
68
+ }
69
+ function serializeBlock(block, depth, options) {
70
+ const indent = options.indent.repeat(depth);
71
+ const childIndent = options.indent.repeat(depth + 1);
72
+ const parts = [];
73
+ const header = serializeBlockHeader(block);
74
+ parts.push(`${indent}${header} {`);
75
+ for (const child of block.blockItems) {
76
+ const childSource = serializeBlockItem(child, depth + 1, options);
77
+ if (childSource) {
78
+ parts.push(childSource);
79
+ }
80
+ }
81
+ parts.push(`${indent}}`);
82
+ return parts.join(options.lineSeparator);
83
+ }
84
+ function serializeBlockHeader(block) {
85
+ var _a;
86
+ const type = block.blockType;
87
+ switch (type) {
88
+ case "section":
89
+ return `\\section("${block.name || ""}")`;
90
+ case "repeat": {
91
+ const repeatCount = (_a = block.repeatCount) !== null && _a !== void 0 ? _a : 1;
92
+ return `\\repeat(${repeatCount})`;
93
+ }
94
+ case "cycle":
95
+ if (block.localCycle) {
96
+ return `\\cycle("${block.localCycle.toString()}")`;
97
+ }
98
+ return "\\cycle()";
99
+ case "beatduration":
100
+ if (block.localAtomsPerBeat !== null) {
101
+ return `\\beatDuration(${block.localAtomsPerBeat})`;
102
+ }
103
+ return "\\beatDuration(1)";
104
+ case "breaks":
105
+ if (block.localBreaks) {
106
+ return `\\breaks(${block.localBreaks.join(", ")})`;
107
+ }
108
+ return "\\breaks()";
109
+ case "role": {
110
+ const roleName = Array.from(block.localRoles.keys())[0] || "";
111
+ return `\\role("${roleName}")`;
112
+ }
113
+ case "group":
114
+ if (block.name) {
115
+ return `\\group("${block.name}")`;
116
+ }
117
+ return "\\group()";
118
+ case "notation":
119
+ return "";
120
+ default:
121
+ return `\\${type}()`;
122
+ }
123
+ }
124
+ function updateCellSource(cell, newSource) {
125
+ return Object.assign(Object.assign({}, cell), { source: newSource });
126
+ }
127
+ function validateCellSource(source) {
128
+ let braceCount = 0;
129
+ for (const char of source) {
130
+ if (char === "{")
131
+ braceCount++;
132
+ if (char === "}")
133
+ braceCount--;
134
+ if (braceCount < 0) {
135
+ return {
136
+ isValid: false,
137
+ error: "Unmatched closing brace",
138
+ };
139
+ }
140
+ }
141
+ if (braceCount !== 0) {
142
+ return {
143
+ isValid: false,
144
+ error: "Unmatched opening brace",
145
+ };
146
+ }
147
+ return { isValid: true };
148
+ }
149
+ function findCellSourceRange(cell, fullSource) {
150
+ if ((0, block_1.isRawBlock)(cell.blockItem)) {
151
+ const rawBlock = cell.blockItem;
152
+ const start = fullSource.indexOf(rawBlock.content);
153
+ if (start >= 0) {
154
+ return {
155
+ start,
156
+ end: start + rawBlock.content.length,
157
+ };
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+ //# sourceMappingURL=sourceSerializer.js.map