@nocturnium/svelte-ide 1.0.2 → 1.0.3

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 (74) hide show
  1. package/README.md +5 -3
  2. package/dist/components/ai/AIMessageContent.svelte +24 -14
  3. package/dist/components/ai/AIPanel.svelte +22 -0
  4. package/dist/components/editor/CollaborativeEditor.svelte +68 -5
  5. package/dist/components/editor/CollaborativeEditor.svelte.d.ts +14 -0
  6. package/dist/components/editor/CustomEditor.svelte +52 -33
  7. package/dist/components/editor/CustomEditor.svelte.d.ts +2 -2
  8. package/dist/components/editor/Editor.svelte +17 -0
  9. package/dist/components/editor/Editor.svelte.d.ts +9 -0
  10. package/dist/components/editor/EditorPane.svelte +18 -1
  11. package/dist/components/editor/EditorPane.svelte.d.ts +5 -0
  12. package/dist/components/editor/EditorSelections.svelte +27 -11
  13. package/dist/components/editor/EditorSelections.svelte.d.ts +1 -0
  14. package/dist/components/editor/core/folding.d.ts +11 -0
  15. package/dist/components/editor/core/folding.js +41 -0
  16. package/dist/components/editor/core/index.d.ts +0 -5
  17. package/dist/components/editor/core/index.js +4 -5
  18. package/dist/components/editor/core/state.d.ts +5 -0
  19. package/dist/components/editor/core/state.js +131 -12
  20. package/dist/components/editor/editor-find.d.ts +1 -0
  21. package/dist/components/editor/editor-find.js +6 -5
  22. package/dist/components/editor/editor-input.d.ts +1 -0
  23. package/dist/components/editor/editor-input.js +4 -1
  24. package/dist/components/editor/editor-scroll.d.ts +1 -0
  25. package/dist/components/editor/editor-scroll.js +2 -1
  26. package/dist/components/editor/index.d.ts +19 -3
  27. package/dist/components/editor/index.js +18 -4
  28. package/dist/components/editor/tokenizer/base.d.ts +1 -25
  29. package/dist/components/editor/tokenizer/base.js +0 -172
  30. package/dist/components/editor/tokenizer/index.d.ts +4 -0
  31. package/dist/components/editor/tokenizer/index.js +1 -1
  32. package/dist/components/editor/tokenizer/languages/html.d.ts +3 -2
  33. package/dist/components/editor/tokenizer/languages/html.js +64 -6
  34. package/dist/components/editor/tokenizer/languages/javascript.d.ts +0 -3
  35. package/dist/components/editor/tokenizer/languages/javascript.js +1 -2
  36. package/dist/components/editor/tokenizer/languages/svelte.d.ts +1 -1
  37. package/dist/components/editor/tokenizer/languages/svelte.js +6 -1
  38. package/dist/components/editor/tokenizer/types.d.ts +0 -28
  39. package/dist/crdt/awareness.d.ts +8 -2
  40. package/dist/crdt/awareness.js +11 -4
  41. package/dist/crdt/document.d.ts +10 -1
  42. package/dist/crdt/document.js +15 -7
  43. package/dist/crdt/index.d.ts +8 -2
  44. package/dist/crdt/index.js +5 -2
  45. package/dist/crdt/undo.d.ts +2 -7
  46. package/dist/crdt/undo.js +1 -8
  47. package/dist/index.d.ts +7 -9
  48. package/dist/index.js +7 -9
  49. package/dist/services/error-handling.d.ts +2 -11
  50. package/dist/services/error-handling.js +15 -4
  51. package/dist/services/lsp-client.d.ts +3 -0
  52. package/dist/services/lsp-client.js +55 -10
  53. package/dist/services/optimistic.d.ts +8 -5
  54. package/dist/services/optimistic.js +36 -10
  55. package/dist/services/vfs-client.js +11 -3
  56. package/dist/stores/agents.svelte.js +3 -2
  57. package/dist/stores/ai-persistence.svelte.js +7 -2
  58. package/dist/stores/ai.svelte.js +2 -1
  59. package/dist/stores/collaboration.svelte.d.ts +1 -1
  60. package/dist/stores/collaboration.svelte.js +3 -2
  61. package/dist/stores/editor.svelte.js +29 -5
  62. package/dist/stores/layout.svelte.js +3 -0
  63. package/dist/stores/plugin.svelte.js +9 -3
  64. package/dist/stores/vfs.svelte.js +26 -9
  65. package/dist/styles/theme.css +43 -0
  66. package/dist/types/vfs.d.ts +15 -1
  67. package/dist/types/vfs.js +9 -0
  68. package/dist/utils/language.d.ts +4 -3
  69. package/dist/utils/language.js +8 -18
  70. package/package.json +1 -1
  71. package/dist/components/editor/MinimalEditor.svelte +0 -75
  72. package/dist/components/editor/MinimalEditor.svelte.d.ts +0 -6
  73. package/dist/components/editor/MinimalEditor2.svelte +0 -84
  74. package/dist/components/editor/MinimalEditor2.svelte.d.ts +0 -6
@@ -4,13 +4,27 @@
4
4
  // Components
5
5
  export { default as Editor } from './Editor.svelte';
6
6
  export { default as CustomEditor } from './CustomEditor.svelte';
7
- export { default as CollaborativeEditor } from './CollaborativeEditor.svelte';
8
7
  export { default as EditorTabs } from './EditorTabs.svelte';
9
8
  export { default as EditorPane } from './EditorPane.svelte';
10
9
  export { default as FileIcon } from './FileIcon.svelte';
11
10
  export { default as FileExplorer } from './FileExplorer.svelte';
12
- // Core utilities
13
- export * from './core';
11
+ // Core utilities (explicitly excluding CRDT binding; use @nocturnium/svelte-ide/crdt)
12
+ export * from './core/state';
13
+ export * from './core/navigation';
14
+ export * from './core/keybindings';
15
+ export * from './core/search';
16
+ export * from './core/folding';
17
+ export * from './core/multi-cursor';
18
+ export * from './core/complexity-analyzer';
19
+ export * from './core/ai-awareness';
20
+ export * from './core/semantic-analyzer';
21
+ export * from './core/commands';
22
+ export * from './core/bracket-healer';
23
+ export * from './core/git-blame';
24
+ export * from './core/snippet-manager';
25
+ export * from './core/quick-actions';
26
+ export * from './core/diagnostics';
27
+ export * from './core/breakpoints';
14
28
  // Theme
15
29
  export * from './theme';
16
30
  // Languages (explicit exports to avoid conflicts with tokenizer)
@@ -18,4 +32,4 @@ export { getLanguageExtension, getLanguageConfig, getLanguageFromExtension, getL
18
32
  // Re-export from languages (these exist in both but we prefer languages versions)
19
33
  export { getSupportedLanguages, isLanguageSupported } from './languages';
20
34
  // Tokenizer (explicit exports to avoid conflicts)
21
- export { getTokenizer, tokenize, getTokenClass, tokensToHTML, PlaintextTokenizer, SimpleTokenizer, GrammarTokenizer, createToken } from './tokenizer';
35
+ export { getTokenizer, tokenize, getTokenClass, tokensToHTML, PlaintextTokenizer, createToken } from './tokenizer';
@@ -1,35 +1,11 @@
1
1
  /**
2
2
  * Base tokenizer with common functionality
3
3
  */
4
- import type { Token, TokenizedLine, TokenizerState, TokenType, LanguageGrammar, LanguageTokenizer } from './types';
4
+ import type { Token, TokenizedLine, TokenizerState, TokenType, LanguageTokenizer } from './types';
5
5
  /**
6
6
  * Create a token
7
7
  */
8
8
  export declare function createToken(type: TokenType, text: string, start: number): Token;
9
- /**
10
- * Base tokenizer class using grammar rules
11
- */
12
- export declare class GrammarTokenizer implements LanguageTokenizer {
13
- language: string;
14
- private grammar;
15
- constructor(grammar: LanguageGrammar);
16
- getInitialState(): TokenizerState;
17
- tokenizeLine(line: string, lineNumber: number, prevState?: TokenizerState): TokenizedLine;
18
- private updateState;
19
- }
20
- /**
21
- * Simple regex-based tokenizer for basic languages
22
- */
23
- export declare class SimpleTokenizer implements LanguageTokenizer {
24
- language: string;
25
- private patterns;
26
- constructor(language: string, patterns: Array<{
27
- type: TokenType;
28
- regex: RegExp;
29
- }>);
30
- getInitialState(): TokenizerState;
31
- tokenizeLine(line: string, lineNumber: number, prevState?: TokenizerState): TokenizedLine;
32
- }
33
9
  /**
34
10
  * Plaintext tokenizer - no highlighting
35
11
  */
@@ -12,178 +12,6 @@ export function createToken(type, text, start) {
12
12
  end: start + text.length
13
13
  };
14
14
  }
15
- /**
16
- * Base tokenizer class using grammar rules
17
- */
18
- export class GrammarTokenizer {
19
- language;
20
- grammar;
21
- constructor(grammar) {
22
- this.language = grammar.language;
23
- this.grammar = grammar;
24
- }
25
- getInitialState() {
26
- return {};
27
- }
28
- tokenizeLine(line, lineNumber, prevState) {
29
- const tokens = [];
30
- let pos = 0;
31
- const state = { ...prevState };
32
- let currentRuleSet = 'root';
33
- // Determine starting rule set based on state
34
- if (state.inBlockComment) {
35
- currentRuleSet = 'blockComment';
36
- }
37
- else if (state.inTemplateLiteral) {
38
- currentRuleSet = 'templateLiteral';
39
- }
40
- else if (state.inMultilineString) {
41
- currentRuleSet = 'multilineString';
42
- }
43
- while (pos < line.length) {
44
- const remaining = line.slice(pos);
45
- let matched = false;
46
- // Try to match rules in current rule set
47
- const rules = this.grammar.rules[currentRuleSet] || this.grammar.rules['root'] || [];
48
- for (const rule of rules) {
49
- const match = remaining.match(rule.pattern);
50
- if (match && match.index === 0) {
51
- const text = match[0];
52
- tokens.push(createToken(rule.type, text, pos));
53
- pos += text.length;
54
- matched = true;
55
- // Handle state transitions
56
- if (rule.nextState) {
57
- currentRuleSet = rule.nextState;
58
- this.updateState(state, rule.nextState, true);
59
- }
60
- if (rule.popState) {
61
- currentRuleSet = 'root';
62
- this.updateState(state, currentRuleSet, false);
63
- }
64
- break;
65
- }
66
- }
67
- // No match - consume single character as text
68
- if (!matched) {
69
- const char = line[pos];
70
- // Try to merge with previous text token
71
- const lastToken = tokens[tokens.length - 1];
72
- if (lastToken && lastToken.type === 'text' && lastToken.end === pos) {
73
- lastToken.text += char;
74
- lastToken.end += 1;
75
- }
76
- else {
77
- tokens.push(createToken('text', char, pos));
78
- }
79
- pos += 1;
80
- }
81
- }
82
- // Handle empty lines
83
- if (tokens.length === 0) {
84
- tokens.push(createToken('text', '', 0));
85
- }
86
- return {
87
- lineNumber,
88
- tokens,
89
- text: line,
90
- state: { ...state }
91
- };
92
- }
93
- updateState(state, ruleSet, entering) {
94
- switch (ruleSet) {
95
- case 'blockComment':
96
- state.inBlockComment = entering;
97
- break;
98
- case 'templateLiteral':
99
- state.inTemplateLiteral = entering;
100
- break;
101
- case 'multilineString':
102
- state.inMultilineString = entering;
103
- break;
104
- }
105
- }
106
- }
107
- /**
108
- * Simple regex-based tokenizer for basic languages
109
- */
110
- export class SimpleTokenizer {
111
- language;
112
- patterns;
113
- constructor(language, patterns) {
114
- this.language = language;
115
- this.patterns = patterns;
116
- }
117
- getInitialState() {
118
- return {};
119
- }
120
- tokenizeLine(line, lineNumber, prevState) {
121
- const tokens = [];
122
- let pos = 0;
123
- const state = { ...prevState };
124
- // Handle block comment continuation
125
- if (state.inBlockComment) {
126
- const endMatch = line.indexOf('*/');
127
- if (endMatch !== -1) {
128
- tokens.push(createToken('comment.block', line.slice(0, endMatch + 2), 0));
129
- pos = endMatch + 2;
130
- state.inBlockComment = false;
131
- }
132
- else {
133
- tokens.push(createToken('comment.block', line, 0));
134
- return { lineNumber, tokens, text: line, state };
135
- }
136
- }
137
- while (pos < line.length) {
138
- const remaining = line.slice(pos);
139
- let matched = false;
140
- // Check for block comment start
141
- if (remaining.startsWith('/*')) {
142
- const endMatch = remaining.indexOf('*/', 2);
143
- if (endMatch !== -1) {
144
- const text = remaining.slice(0, endMatch + 2);
145
- tokens.push(createToken('comment.block', text, pos));
146
- pos += text.length;
147
- }
148
- else {
149
- tokens.push(createToken('comment.block', remaining, pos));
150
- state.inBlockComment = true;
151
- pos = line.length;
152
- }
153
- matched = true;
154
- continue;
155
- }
156
- // Try each pattern
157
- for (const { type, regex } of this.patterns) {
158
- const match = remaining.match(regex);
159
- if (match && match.index === 0) {
160
- const text = match[0];
161
- tokens.push(createToken(type, text, pos));
162
- pos += text.length;
163
- matched = true;
164
- break;
165
- }
166
- }
167
- // No match - consume character
168
- if (!matched) {
169
- const char = remaining[0];
170
- const lastToken = tokens[tokens.length - 1];
171
- if (lastToken && lastToken.type === 'text' && lastToken.end === pos) {
172
- lastToken.text += char;
173
- lastToken.end += 1;
174
- }
175
- else {
176
- tokens.push(createToken('text', char, pos));
177
- }
178
- pos += 1;
179
- }
180
- }
181
- if (tokens.length === 0) {
182
- tokens.push(createToken('text', '', 0));
183
- }
184
- return { lineNumber, tokens, text: line, state };
185
- }
186
- }
187
15
  /**
188
16
  * Plaintext tokenizer - no highlighting
189
17
  */
@@ -15,6 +15,10 @@ export { GoTokenizer, createGoTokenizer } from './languages/go';
15
15
  export { MarkdownTokenizer, createMarkdownTokenizer } from './languages/markdown';
16
16
  export { SvelteTokenizer, createSvelteTokenizer } from './languages/svelte';
17
17
  import type { LanguageTokenizer, Token, TokenizedLine, TokenizerState, TokenType } from './types';
18
+ /**
19
+ * Tokenizer factory functions by language
20
+ */
21
+ export declare const tokenizerFactories: Record<string, () => LanguageTokenizer>;
18
22
  /**
19
23
  * Get the canonical language name from an alias or extension
20
24
  */
@@ -78,7 +78,7 @@ const languageAliases = {
78
78
  /**
79
79
  * Tokenizer factory functions by language
80
80
  */
81
- const tokenizerFactories = {
81
+ export const tokenizerFactories = {
82
82
  javascript: createJavaScriptTokenizer,
83
83
  typescript: createTypeScriptTokenizer,
84
84
  jsx: createJSXTokenizer,
@@ -3,14 +3,15 @@
3
3
  */
4
4
  import type { TokenizedLine, TokenizerState } from '../types';
5
5
  interface HTMLTokenizerState extends TokenizerState {
6
- inTag?: boolean;
7
6
  inScript?: boolean;
8
7
  inStyle?: boolean;
9
- tagName?: string;
8
+ innerState?: TokenizerState;
10
9
  }
11
10
  export declare class HTMLTokenizer {
12
11
  language: string;
13
12
  private isXML;
13
+ private jsTokenizer;
14
+ private cssTokenizer;
14
15
  constructor(options?: {
15
16
  xml?: boolean;
16
17
  });
@@ -2,9 +2,13 @@
2
2
  * HTML/XML tokenizer
3
3
  */
4
4
  import { createToken } from '../base';
5
+ import { createCSSTokenizer } from './css';
6
+ import { createJavaScriptTokenizer } from './javascript';
5
7
  export class HTMLTokenizer {
6
8
  language;
7
9
  isXML;
10
+ jsTokenizer = createJavaScriptTokenizer();
11
+ cssTokenizer = createCSSTokenizer();
8
12
  constructor(options = {}) {
9
13
  this.isXML = options.xml ?? false;
10
14
  this.language = this.isXML ? 'xml' : 'html';
@@ -29,6 +33,54 @@ export class HTMLTokenizer {
29
33
  return { lineNumber, tokens, text: line, state };
30
34
  }
31
35
  }
36
+ if (!this.isXML && state.inScript) {
37
+ const closeScriptMatch = line.match(/<\/script>/i);
38
+ if (closeScriptMatch) {
39
+ const scriptPart = line.slice(0, closeScriptMatch.index);
40
+ if (scriptPart) {
41
+ const result = this.jsTokenizer.tokenizeLine(scriptPart, lineNumber, state.innerState ?? this.jsTokenizer.getInitialState());
42
+ tokens.push(...result.tokens);
43
+ state.innerState = result.state;
44
+ }
45
+ state.inScript = false;
46
+ state.innerState = undefined;
47
+ pos = closeScriptMatch.index;
48
+ }
49
+ else {
50
+ const result = this.jsTokenizer.tokenizeLine(line, lineNumber, state.innerState ?? this.jsTokenizer.getInitialState());
51
+ state.innerState = result.state;
52
+ return {
53
+ lineNumber,
54
+ tokens: result.tokens,
55
+ text: line,
56
+ state
57
+ };
58
+ }
59
+ }
60
+ if (!this.isXML && state.inStyle) {
61
+ const closeStyleMatch = line.match(/<\/style>/i);
62
+ if (closeStyleMatch) {
63
+ const stylePart = line.slice(0, closeStyleMatch.index);
64
+ if (stylePart) {
65
+ const result = this.cssTokenizer.tokenizeLine(stylePart, lineNumber, state.innerState ?? this.cssTokenizer.getInitialState());
66
+ tokens.push(...result.tokens);
67
+ state.innerState = result.state;
68
+ }
69
+ state.inStyle = false;
70
+ state.innerState = undefined;
71
+ pos = closeStyleMatch.index;
72
+ }
73
+ else {
74
+ const result = this.cssTokenizer.tokenizeLine(line, lineNumber, state.innerState ?? this.cssTokenizer.getInitialState());
75
+ state.innerState = result.state;
76
+ return {
77
+ lineNumber,
78
+ tokens: result.tokens,
79
+ text: line,
80
+ state
81
+ };
82
+ }
83
+ }
32
84
  while (pos < line.length) {
33
85
  const remaining = line.slice(pos);
34
86
  const token = this.getNextToken(remaining, pos, state, line);
@@ -89,10 +141,14 @@ export class HTMLTokenizer {
89
141
  const closingTagMatch = text.match(/^<\/([a-zA-Z][a-zA-Z0-9:-]*)>/);
90
142
  if (closingTagMatch) {
91
143
  const tagName = closingTagMatch[1].toLowerCase();
92
- if (tagName === 'script')
144
+ if (tagName === 'script') {
93
145
  state.inScript = false;
94
- if (tagName === 'style')
146
+ state.innerState = undefined;
147
+ }
148
+ if (tagName === 'style') {
95
149
  state.inStyle = false;
150
+ state.innerState = undefined;
151
+ }
96
152
  return createToken('tag', closingTagMatch[0], pos);
97
153
  }
98
154
  // Opening tag
@@ -121,17 +177,19 @@ export class HTMLTokenizer {
121
177
  }
122
178
  if (openingMatch) {
123
179
  const tagName = openingMatch[1].toLowerCase();
124
- if (tagName === 'script')
180
+ if (!this.isXML && tagName === 'script') {
125
181
  state.inScript = true;
126
- if (tagName === 'style')
182
+ state.innerState = this.jsTokenizer.getInitialState();
183
+ }
184
+ if (!this.isXML && tagName === 'style') {
127
185
  state.inStyle = true;
186
+ state.innerState = this.cssTokenizer.getInitialState();
187
+ }
128
188
  return createToken('tag', openingMatch[0], pos);
129
189
  }
130
190
  // Partial tag - just match the tag name part
131
191
  const partialMatch = text.match(/^<([a-zA-Z][a-zA-Z0-9:-]*)/);
132
192
  if (partialMatch) {
133
- state.inTag = true;
134
- state.tagName = partialMatch[1];
135
193
  return createToken('tag.name', partialMatch[0], pos);
136
194
  }
137
195
  return createToken('tag.punctuation', '<', pos);
@@ -4,9 +4,6 @@
4
4
  import type { TokenizedLine, TokenizerState } from '../types';
5
5
  interface JSTokenizerState extends TokenizerState {
6
6
  templateDepth?: number;
7
- jsxDepth?: number;
8
- /** Track if regex is valid in current context (for division vs regex ambiguity) */
9
- expectExpression?: boolean;
10
7
  /**
11
8
  * Last significant token text, used for regex/division disambiguation.
12
9
  * Stored per-line in the threaded state (not on the tokenizer instance) so a
@@ -234,8 +234,7 @@ export class JavaScriptTokenizer {
234
234
  }
235
235
  getInitialState() {
236
236
  return {
237
- templateDepth: 0,
238
- jsxDepth: 0
237
+ templateDepth: 0
239
238
  };
240
239
  }
241
240
  tokenizeLine(line, lineNumber, prevState) {
@@ -29,7 +29,7 @@ export declare class SvelteTokenizer implements LanguageTokenizer {
29
29
  private cssTokenizer;
30
30
  constructor();
31
31
  getInitialState(): SvelteTokenizerState;
32
- tokenizeLine(line: string, lineNumber: number, state: SvelteTokenizerState): TokenizedLine;
32
+ tokenizeLine(line: string, lineNumber: number, prevState?: SvelteTokenizerState): TokenizedLine;
33
33
  private tokenizeTemplate;
34
34
  private tokenizeTag;
35
35
  private tokenizeJSExpression;
@@ -65,9 +65,14 @@ export class SvelteTokenizer {
65
65
  tagDepth: 0
66
66
  };
67
67
  }
68
- tokenizeLine(line, lineNumber, state) {
68
+ tokenizeLine(line, lineNumber, prevState) {
69
69
  const tokens = [];
70
70
  let pos = 0;
71
+ const state = {
72
+ ...this.getInitialState(),
73
+ ...prevState,
74
+ innerState: prevState?.innerState ? { ...prevState.innerState } : undefined
75
+ };
71
76
  // Handle script context
72
77
  if (state.context === 'script') {
73
78
  const closeScriptMatch = line.match(/<\/script>/i);
@@ -56,31 +56,3 @@ export interface LanguageTokenizer {
56
56
  /** Get initial state */
57
57
  getInitialState(): TokenizerState;
58
58
  }
59
- /**
60
- * Token rule for regex-based tokenization
61
- */
62
- export interface TokenRule {
63
- /** Token type to assign */
64
- type: TokenType;
65
- /** Regex pattern to match */
66
- pattern: RegExp;
67
- /** Optional: next state after matching */
68
- nextState?: string;
69
- /** Optional: pop state after matching */
70
- popState?: boolean;
71
- }
72
- /**
73
- * Grammar definition for a language
74
- */
75
- export interface LanguageGrammar {
76
- /** Language identifier */
77
- language: string;
78
- /** File extensions */
79
- extensions: string[];
80
- /** Line comment prefix */
81
- lineComment?: string;
82
- /** Block comment start/end */
83
- blockComment?: [string, string];
84
- /** Token rules by state */
85
- rules: Record<string, TokenRule[]>;
86
- }
@@ -28,10 +28,16 @@ export interface AwarenessProtocol {
28
28
  onUsersChange(callback: (users: AwarenessUser[]) => void): () => void;
29
29
  destroy(): void;
30
30
  }
31
+ export interface CreateAwarenessProtocolOptions {
32
+ destroyAwareness?: boolean;
33
+ }
31
34
  /**
32
- * Create an awareness protocol instance
35
+ * Create an awareness protocol instance.
36
+ *
37
+ * Pass a provider-owned Awareness instance to broadcast presence through that
38
+ * provider. Passing a Y.Doc creates a standalone Awareness instance.
33
39
  */
34
- export declare function createAwarenessProtocol(doc: YDoc): AwarenessProtocol;
40
+ export declare function createAwarenessProtocol(source: YDoc | Awareness, options?: CreateAwarenessProtocolOptions): AwarenessProtocol;
35
41
  /**
36
42
  * Generate a random color for a user
37
43
  */
@@ -3,10 +3,15 @@
3
3
  */
4
4
  import { Awareness } from 'y-protocols/awareness';
5
5
  /**
6
- * Create an awareness protocol instance
6
+ * Create an awareness protocol instance.
7
+ *
8
+ * Pass a provider-owned Awareness instance to broadcast presence through that
9
+ * provider. Passing a Y.Doc creates a standalone Awareness instance.
7
10
  */
8
- export function createAwarenessProtocol(doc) {
9
- const awareness = new Awareness(doc);
11
+ export function createAwarenessProtocol(source, options = {}) {
12
+ const ownsAwareness = source instanceof Awareness;
13
+ const awareness = ownsAwareness ? source : new Awareness(source);
14
+ const shouldDestroyAwareness = options.destroyAwareness ?? !ownsAwareness;
10
15
  const userChangeCallbacks = new Set();
11
16
  // Track awareness changes
12
17
  awareness.on('change', () => {
@@ -75,7 +80,9 @@ export function createAwarenessProtocol(doc) {
75
80
  return () => userChangeCallbacks.delete(callback);
76
81
  },
77
82
  destroy() {
78
- awareness.destroy();
83
+ if (shouldDestroyAwareness) {
84
+ awareness.destroy();
85
+ }
79
86
  userChangeCallbacks.clear();
80
87
  }
81
88
  };
@@ -4,6 +4,11 @@
4
4
  */
5
5
  import * as Y from 'yjs';
6
6
  import type { DocumentOptions } from './types';
7
+ export interface CollaborativeUndoManagerOptions {
8
+ captureTimeout?: number;
9
+ trackedOrigins?: Set<unknown>;
10
+ deleteFilter?: (item: Y.Item) => boolean;
11
+ }
7
12
  export declare class CollaborativeDocument {
8
13
  readonly doc: Y.Doc;
9
14
  readonly id: string;
@@ -49,7 +54,11 @@ export declare class CollaborativeDocument {
49
54
  /**
50
55
  * Setup undo manager
51
56
  */
52
- private setupUndoManager;
57
+ getUndoManager(options?: CollaborativeUndoManagerOptions): Y.UndoManager;
58
+ /**
59
+ * Check whether this document currently owns an undo manager.
60
+ */
61
+ hasUndoManager(): boolean;
53
62
  /**
54
63
  * Track additional origins for undo
55
64
  */
@@ -18,7 +18,7 @@ export class CollaborativeDocument {
18
18
  }
19
19
  // Setup undo manager if enabled
20
20
  if (options.enableUndo !== false) {
21
- this.setupUndoManager(options.undoCaptureTimeout);
21
+ this.getUndoManager({ captureTimeout: options.undoCaptureTimeout });
22
22
  }
23
23
  }
24
24
  /**
@@ -97,20 +97,28 @@ export class CollaborativeDocument {
97
97
  /**
98
98
  * Setup undo manager
99
99
  */
100
- setupUndoManager(captureTimeout = 500) {
101
- const trackedOrigins = new Set([null, 'local']);
100
+ getUndoManager(options = {}) {
101
+ if (this.undoManager)
102
+ return this.undoManager;
103
+ const { captureTimeout = 500, trackedOrigins = new Set([null, 'local']), deleteFilter } = options;
102
104
  this.undoManager = new Y.UndoManager(this.getText(), {
103
105
  trackedOrigins,
104
- captureTimeout
106
+ captureTimeout,
107
+ deleteFilter
105
108
  });
109
+ return this.undoManager;
110
+ }
111
+ /**
112
+ * Check whether this document currently owns an undo manager.
113
+ */
114
+ hasUndoManager() {
115
+ return this.undoManager !== null;
106
116
  }
107
117
  /**
108
118
  * Track additional origins for undo
109
119
  */
110
120
  trackOrigin(origin) {
111
- if (this.undoManager) {
112
- this.undoManager.trackedOrigins.add(origin);
113
- }
121
+ this.getUndoManager().trackedOrigins.add(origin);
114
122
  }
115
123
  /**
116
124
  * Undo last change
@@ -4,6 +4,12 @@
4
4
  */
5
5
  export { CollaborativeDocument } from './document';
6
6
  export { CollaborativeProvider } from './provider';
7
- export { createAwarenessProtocol } from './awareness';
8
- export { createUndoManager } from './undo';
7
+ export { createProvider } from './provider';
8
+ export { createAwarenessProtocol, generateUserColor, getInitials } from './awareness';
9
+ export { createUndoManager, createUserUndoManager } from './undo';
10
+ export { default as CollaborativeEditor } from '../components/editor/CollaborativeEditor.svelte';
11
+ export { CRDTBinding, createCRDTBinding, createRelativePosition, resolveRelativePosition } from '../components/editor/core/crdt-binding';
9
12
  export type * from './types';
13
+ export type { AwarenessProtocol, AwarenessUser, CreateAwarenessProtocolOptions } from './awareness';
14
+ export type { UndoManagerInstance, UndoManagerOptions, UndoManagerState } from './undo';
15
+ export type { CRDTBindingConfig, CRDTPosition, RelativePosition } from '../components/editor/core/crdt-binding';
@@ -4,5 +4,8 @@
4
4
  */
5
5
  export { CollaborativeDocument } from './document';
6
6
  export { CollaborativeProvider } from './provider';
7
- export { createAwarenessProtocol } from './awareness';
8
- export { createUndoManager } from './undo';
7
+ export { createProvider } from './provider';
8
+ export { createAwarenessProtocol, generateUserColor, getInitials } from './awareness';
9
+ export { createUndoManager, createUserUndoManager } from './undo';
10
+ export { default as CollaborativeEditor } from '../components/editor/CollaborativeEditor.svelte';
11
+ export { CRDTBinding, createCRDTBinding, createRelativePosition, resolveRelativePosition } from '../components/editor/core/crdt-binding';
@@ -1,13 +1,8 @@
1
1
  /**
2
2
  * Undo manager for CRDT documents
3
3
  */
4
- import * as Y from 'yjs';
5
- import type { CollaborativeDocument } from './document';
6
- export interface UndoManagerOptions {
7
- captureTimeout?: number;
8
- trackedOrigins?: Set<unknown>;
9
- deleteFilter?: (item: Y.Item) => boolean;
10
- }
4
+ import type { CollaborativeDocument, CollaborativeUndoManagerOptions } from './document';
5
+ export type UndoManagerOptions = CollaborativeUndoManagerOptions;
11
6
  export interface UndoManagerState {
12
7
  canUndo: boolean;
13
8
  canRedo: boolean;
package/dist/crdt/undo.js CHANGED
@@ -1,18 +1,11 @@
1
1
  /**
2
2
  * Undo manager for CRDT documents
3
3
  */
4
- import * as Y from 'yjs';
5
4
  /**
6
5
  * Create an undo manager for a collaborative document
7
6
  */
8
7
  export function createUndoManager(document, options = {}) {
9
- const { captureTimeout = 500, trackedOrigins = new Set([null, 'local']), deleteFilter } = options;
10
- const text = document.getText();
11
- const manager = new Y.UndoManager(text, {
12
- captureTimeout,
13
- trackedOrigins,
14
- deleteFilter
15
- });
8
+ const manager = document.getUndoManager(options);
16
9
  const stateCallbacks = new Set();
17
10
  function getState() {
18
11
  return {