@kerebron/extension-lsp 0.8.5 → 0.8.6

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 (51) hide show
  1. package/esm/ExtensionLsp.d.ts +1 -7
  2. package/esm/ExtensionLsp.d.ts.map +1 -1
  3. package/esm/ExtensionLsp.js +62 -79
  4. package/esm/ExtensionLsp.js.map +1 -1
  5. package/esm/LSPClient.d.ts +60 -15
  6. package/esm/LSPClient.d.ts.map +1 -1
  7. package/esm/LSPClient.js +91 -40
  8. package/esm/LSPClient.js.map +1 -1
  9. package/esm/LSPPlugin.d.ts +6 -35
  10. package/esm/LSPPlugin.d.ts.map +1 -1
  11. package/esm/LSPPlugin.js +40 -165
  12. package/esm/LSPPlugin.js.map +1 -1
  13. package/esm/LSPSync.d.ts +28 -0
  14. package/esm/LSPSync.d.ts.map +1 -0
  15. package/esm/LSPSync.js +111 -0
  16. package/esm/LSPSync.js.map +1 -0
  17. package/esm/computeIncrementalChanges.d.ts +1 -0
  18. package/esm/computeIncrementalChanges.d.ts.map +1 -1
  19. package/esm/computeIncrementalChanges.js +11 -0
  20. package/esm/computeIncrementalChanges.js.map +1 -1
  21. package/esm/mod.d.ts +0 -1
  22. package/esm/mod.d.ts.map +1 -1
  23. package/esm/mod.js +0 -1
  24. package/esm/mod.js.map +1 -1
  25. package/package.json +4 -7
  26. package/src/ExtensionLsp.ts +91 -94
  27. package/src/LSPClient.ts +217 -54
  28. package/src/LSPPlugin.ts +59 -292
  29. package/src/LSPSync.ts +169 -0
  30. package/src/computeIncrementalChanges.ts +17 -0
  31. package/src/mod.ts +0 -1
  32. package/esm/LSPWorkspace.d.ts +0 -39
  33. package/esm/LSPWorkspace.d.ts.map +0 -1
  34. package/esm/LSPWorkspace.js +0 -118
  35. package/esm/LSPWorkspace.js.map +0 -1
  36. package/esm/createLspAutocomplete.d.ts +0 -17
  37. package/esm/createLspAutocomplete.d.ts.map +0 -1
  38. package/esm/createLspAutocomplete.js +0 -68
  39. package/esm/createLspAutocomplete.js.map +0 -1
  40. package/esm/types.d.ts +0 -19
  41. package/esm/types.d.ts.map +0 -1
  42. package/esm/types.js +0 -2
  43. package/esm/types.js.map +0 -1
  44. package/esm/workspace.d.ts +0 -33
  45. package/esm/workspace.d.ts.map +0 -1
  46. package/esm/workspace.js +0 -23
  47. package/esm/workspace.js.map +0 -1
  48. package/src/LSPWorkspace.ts +0 -172
  49. package/src/createLspAutocomplete.ts +0 -98
  50. package/src/types.ts +0 -14
  51. package/src/workspace.ts +0 -53
package/src/LSPPlugin.ts CHANGED
@@ -1,323 +1,90 @@
1
1
  import { Plugin, PluginKey, Transaction } from 'prosemirror-state';
2
- import { Decoration, DecorationSet } from 'prosemirror-view';
3
- import { Diagnostic } from 'vscode-languageserver-protocol';
2
+ import { Decoration, DecorationSet, EditorView } from 'prosemirror-view';
4
3
 
5
- import { PositionMapper } from '@kerebron/extension-markdown/PositionMapper';
6
4
  import { CoreEditor } from '@kerebron/editor';
7
- import {
8
- type ExtensionMarkdown,
9
- type MarkdownResult,
10
- } from '@kerebron/extension-markdown';
11
5
 
12
- import { ExtensionLsp, LspConfig } from './ExtensionLsp.js';
6
+ import type { LspConfig } from './ExtensionLsp.js';
7
+ import { LSPSync } from './LSPSync.js';
13
8
 
14
- export interface LspRange {
15
- line: number;
16
- character: number;
17
- }
18
-
19
- interface DiagnosticsItem {
20
- severity: number;
21
- range: {
22
- start: LspRange;
23
- end: LspRange;
24
- };
25
- message: string;
26
- source: string;
27
- code: string;
28
- data: any; // "data":{"kind":"todo","keyword":"TODO"}},
29
- }
30
-
31
- interface LSPMeta {
32
- }
33
-
34
- interface WorkspaceMetaOpen {
35
- operation: 'openFile';
36
- uri: string;
37
- lang: string;
38
- }
39
-
40
- interface WorkspaceMetaModify {
41
- operation: 'modifyFile';
42
- uri: string;
43
- }
44
-
45
- interface WorkspaceMetaClose {
46
- operation: 'closeFile';
47
- uri: string;
48
- }
9
+ export const LSPPluginKey = new PluginKey<LSPSync>('lsp');
49
10
 
50
- type WorkspaceMeta =
51
- | WorkspaceMetaOpen
52
- | WorkspaceMetaClose
53
- | WorkspaceMetaModify;
54
-
55
- class LSPPluginState {
56
- diagnostics: DiagnosticsItem[] = [];
57
-
58
- snapshot?: {
59
- version: number;
60
- mapper: PositionMapper;
61
- markdownResult: MarkdownResult;
62
- };
63
-
64
- constructor(private editor: CoreEditor) {
65
- }
66
- }
67
-
68
- const LSPPluginKey = new PluginKey('lsp');
69
-
70
- export class LSPPlugin extends Plugin<LSPPluginState> {
71
- listenerDiag: ((event: Event) => void) | undefined;
72
- listenerDisconnect: ((event: Event) => void) | undefined;
73
- listenerChange: ((event: Event) => void) | undefined;
74
-
75
- constructor(config: LspConfig, extension: ExtensionLsp) {
11
+ export class LSPPlugin extends Plugin<LSPSync> {
12
+ constructor(config: LspConfig, editor: CoreEditor) {
76
13
  super({
77
14
  key: LSPPluginKey,
78
- view(view) {
79
- return {
80
- destroy: () => {
81
- const client = extension.getClient(extension.mainLang);
82
-
83
- if (this.listenerDiag && client) {
84
- client.removeEventListener(
85
- 'textDocument/publishDiagnostics',
86
- this.listenerDiag,
87
- );
88
- }
89
- if (this.listenerDisconnect && client) {
90
- client.removeEventListener(
91
- 'close',
92
- this.listenerDisconnect,
93
- );
94
- }
95
- // if (this.listenerChange) {
96
- // extension.getEditor().removeEventListener(
97
- // 'change',
98
- // this.listenerChange,
99
- // );
100
- // }
101
- },
102
- };
103
- },
104
15
  state: {
105
16
  init() {
106
- return new LSPPluginState(editor);
17
+ return new LSPSync(config, editor);
107
18
  },
108
- apply(
109
- tr: Transaction,
110
- value: LSPPluginState,
111
- oldState,
112
- newState,
113
- ) {
114
- const workspaceMeta: WorkspaceMeta | undefined = tr.getMeta(
115
- 'workspace',
116
- );
117
-
118
- if (workspaceMeta) {
119
- console.log(
120
- 'workspaceMeta',
121
- workspaceMeta.operation,
122
- workspaceMeta,
123
- );
124
-
125
- const uri = workspaceMeta.uri;
126
- // const lang = workspaceMeta.lang;
127
-
128
- switch (workspaceMeta.operation) {
129
- case 'openFile':
130
- {
131
- const client = extension.getClient(workspaceMeta.lang);
132
- if (client) {
133
- client.addEventListener(
134
- 'textDocument/publishDiagnostics',
135
- this.diagListener,
136
- );
137
-
138
- const go = async () => {
139
- client.connect(uri, this.source);
140
- if (uri === editor.config.uri) {
141
- await client.restart();
142
- }
143
- client.workspace.openFile(
144
- workspaceMeta.uri,
145
- workspaceMeta.lang,
146
- this.source,
147
- );
148
- };
149
-
150
- go();
151
- }
152
- }
153
- break;
154
- case 'modifyFile':
155
- break;
156
- case 'closeFile':
157
- // this.editor.view.dispatch(tr);
158
- // const client = this.getClient(this.mainLang);
159
- // if (client) {
160
- // client.disconnect(this.uri);
161
- // }
162
-
163
- break;
164
- }
165
-
166
- return value;
167
- }
168
-
169
- const meta: LSPMeta | undefined = tr.getMeta(LSPPluginKey);
170
- if (meta?.diagnostics) {
171
- value.diagnostics = meta.diagnostics;
172
- }
173
-
174
- const markdownMeta = tr.getMeta('markdown');
175
- if (markdownMeta?.snapshot) {
176
- value.snapshot = markdownMeta.snapshot;
177
- return value;
178
- }
179
- if (markdownMeta?.clearSnapshot) {
180
- value.snapshot = undefined;
181
- return value;
182
- }
183
-
184
- if (tr.docChanged) {
185
- // value.snapshot = undefined;
186
- }
187
-
188
- // let codeBlockChanged = false;
189
-
190
- // // Compare changed ranges only
191
- // tr.mapping.maps.forEach((stepMap) => {
192
- // stepMap.forEach((oldStart, oldEnd, newStart, newEnd) => {
193
- // oldState.doc.nodesBetween(oldStart, oldEnd, (node) => {
194
- // if (node.type.name === "code_block") {
195
- // codeBlockChanged = true;
196
- // }
197
- // });
198
-
199
- // newState.doc.nodesBetween(newStart, newEnd, (node) => {
200
- // if (node.type.name === "code_block") {
201
- // codeBlockChanged = true;
202
- // }
203
- // });
204
- // });
205
- // });
206
-
207
- // if (codeBlockChanged) {
208
- // console.log("Code block changed");
209
- // // trigger your logic here
210
- // }
211
-
19
+ apply(_tr: Transaction, value) {
212
20
  return value;
213
21
  },
214
22
  },
23
+ view(view) {
24
+ const pluginState = LSPPluginKey.getState(view.state);
25
+ return {
26
+ destroy() {
27
+ pluginState?.destroy();
28
+ },
29
+ };
30
+ },
215
31
  props: {
216
32
  decorations(state) {
217
33
  const decorations: Decoration[] = [];
218
34
 
219
- const pluginState = this.getState(state);
35
+ const pluginState = LSPPluginKey.getState(editor.state);
36
+
220
37
  if (pluginState) {
221
- const { diagnostics, snapshot } = pluginState;
38
+ state.doc.forEach((node, pos) => {
39
+ const dom = (editor.view as EditorView).nodeDOM(
40
+ pos,
41
+ ) as HTMLElement;
42
+ if (!dom || !dom.querySelector) {
43
+ return;
44
+ }
222
45
 
223
- if (snapshot?.version === editor.version) {
224
- for (const diag of diagnostics) {
225
- const from = snapshot.mapper.fromLineChar(
226
- diag.range.start.line,
227
- diag.range.start.character,
228
- );
229
- const end = snapshot.mapper.fromLineChar(
230
- diag.range.end.line,
231
- diag.range.end.character,
232
- );
46
+ const editorElement = dom.querySelector('[data-uri]');
47
+ const uri = (editorElement
48
+ ? editorElement.getAttribute('data-uri')
49
+ : undefined) ||
50
+ undefined;
51
+
52
+ if (uri) {
53
+ const diags = pluginState.uriToDiagnostics[uri];
54
+ if (!diags) {
55
+ return;
56
+ }
57
+ const { diagnostics, version, contentMapper } = diags;
58
+
59
+ if (version === editor.version) {
60
+ for (const diag of diagnostics) {
61
+ const from = contentMapper.fromLineChar(
62
+ diag.range.start.line,
63
+ diag.range.start.character,
64
+ );
65
+ const end = contentMapper.fromLineChar(
66
+ diag.range.end.line,
67
+ diag.range.end.character,
68
+ );
233
69
 
234
- if (from > -1 && end > -1) {
235
- const decoration = Decoration.inline(
236
- from,
237
- end,
238
- { class: 'kb-lsp__error', title: diag.message },
239
- );
240
- decorations.push(decoration);
70
+ if (from > -1 && end > -1) {
71
+ const decoration = Decoration.inline(
72
+ pos + 1 + from,
73
+ pos + 1 + end,
74
+ {},
75
+ { class: 'kb-lsp__error', title: diag.message },
76
+ );
77
+ decorations.push(decoration);
78
+ }
79
+ }
241
80
  }
242
81
  }
243
- }
82
+ });
244
83
  }
245
84
 
246
85
  return DecorationSet.create(state.doc, decorations);
247
86
  },
248
87
  },
249
88
  });
250
-
251
- let lastDiag = 0;
252
-
253
- const editor = extension.getEditor();
254
-
255
- const uri = editor.config.uri;
256
- if (uri) {
257
- const client = extension.getClient(extension.mainLang);
258
-
259
- this.listenerDiag = (event: Event) => {
260
- const detail = (event as CustomEvent).detail;
261
- if (detail.params.uri !== uri) {
262
- return;
263
- }
264
-
265
- event.preventDefault();
266
-
267
- lastDiag = +Date();
268
-
269
- if (client) {
270
- const file = client.workspace.getFile(uri);
271
- if (file) {
272
- const { mapper } = file;
273
-
274
- const diagnostics: Array<Diagnostic> = detail.params.diagnostics;
275
-
276
- // file.dia
277
-
278
- const tr = editor.view.state.tr.setMeta(LSPPluginKey, {
279
- diagnostics: detail.params.diagnostics,
280
- mapper,
281
- });
282
- editor.view.dispatch(tr);
283
- }
284
- }
285
- };
286
-
287
- if (client) {
288
- client.addEventListener(
289
- 'textDocument/publishDiagnostics',
290
- this.listenerDiag,
291
- );
292
- }
293
-
294
- this.listenerDisconnect = (event: Event) => {
295
- const tr = editor.view.state.tr.setMeta(LSPPluginKey, {
296
- diagnostics: [],
297
- mapper: undefined,
298
- });
299
- editor.view.dispatch(tr);
300
- };
301
-
302
- if (client) {
303
- client.addEventListener(
304
- 'close',
305
- this.listenerDisconnect,
306
- );
307
- }
308
-
309
- this.listenerChange = (event: Event) => {
310
- if (lastDiag === 0 && +Date() - lastDiag > 10_1000) {
311
- return;
312
- }
313
- const tr = editor.view.state.tr.setMeta(LSPPluginKey, {
314
- diagnostics: [],
315
- mapper: undefined,
316
- });
317
- editor.view.dispatch(tr);
318
- };
319
-
320
- // extension.getEditor().addEventListener('changed', this.listenerChange);
321
- }
322
89
  }
323
90
  }
package/src/LSPSync.ts ADDED
@@ -0,0 +1,169 @@
1
+ import * as lsp from 'vscode-languageserver-protocol';
2
+
3
+ import { CoreEditor } from '@kerebron/editor';
4
+ import type {
5
+ ContentMapper,
6
+ Workspace,
7
+ WorkspaceCloseParams,
8
+ WorkspaceModifyParams,
9
+ } from '@kerebron/workspace';
10
+
11
+ import type { LspConfig } from './ExtensionLsp.js';
12
+ import { LSPClient, LSPClientImpl } from './LSPClient.js';
13
+
14
+ export class LSPSync {
15
+ clients: Record<string, LSPClient> = {};
16
+
17
+ uriToClient: Record<string, LSPClient> = {};
18
+ uriToDiagnostics: Record<
19
+ string,
20
+ {
21
+ diagnostics: lsp.Diagnostic[];
22
+ version: number;
23
+ contentMapper: ContentMapper;
24
+ }
25
+ > = {};
26
+
27
+ listenerDiag: (
28
+ event: CustomEvent<{ params: lsp.PublishDiagnosticsParams }>,
29
+ ) => void;
30
+ listenerDisconnect: (event: CustomEvent<void>) => void;
31
+ listenerChange: (event: CustomEvent<WorkspaceModifyParams>) => Promise<void>;
32
+
33
+ constructor(private config: LspConfig, private editor: CoreEditor) {
34
+ this.listenerDiag = async (
35
+ event: CustomEvent<{ params: lsp.PublishDiagnosticsParams }>,
36
+ ) => {
37
+ const { uri, diagnostics, version } = event.detail.params;
38
+
39
+ const client = event.target as LSPClientImpl;
40
+
41
+ const entry = client.entries[uri];
42
+ if (!entry || !version) {
43
+ return;
44
+ }
45
+
46
+ const contentMapper = await entry.getContentMapper();
47
+
48
+ this.uriToDiagnostics[uri] = {
49
+ diagnostics,
50
+ version,
51
+ contentMapper,
52
+ };
53
+
54
+ const tr = editor.state.tr;
55
+ editor.dispatchTransaction(tr);
56
+ };
57
+
58
+ this.listenerDisconnect = (event: Event) => {
59
+ const client = event.target as LSPClient;
60
+ this.disconnected(client);
61
+ };
62
+
63
+ const workspace: Workspace = this.editor.ci.resolve('workspace');
64
+
65
+ this.listenerChange = async (event: CustomEvent<WorkspaceModifyParams>) => {
66
+ await this.changeFile(event.detail);
67
+ };
68
+
69
+ workspace.addEventListener('openFile', this.listenerChange);
70
+ workspace.addEventListener('modifyFile', this.listenerChange);
71
+ workspace.addEventListener(
72
+ 'closeFile',
73
+ (event: CustomEvent<WorkspaceCloseParams>) => {
74
+ this.closeFile(event.detail.uri);
75
+ },
76
+ );
77
+ }
78
+
79
+ async changeFile(modified: WorkspaceModifyParams) {
80
+ const langClient = this.getClient(modified.lang);
81
+ if (!langClient) {
82
+ return;
83
+ }
84
+
85
+ const client: LSPClient | undefined = this.uriToClient[modified.uri];
86
+
87
+ if (!client) {
88
+ this.uriToClient[modified.uri] = langClient;
89
+ await langClient.changeFile(modified);
90
+ return;
91
+ }
92
+
93
+ if (client.lang !== langClient.lang) {
94
+ await client.closeFile(modified.uri);
95
+ this.uriToClient[modified.uri] = langClient;
96
+ await langClient.changeFile(modified);
97
+ return;
98
+ }
99
+
100
+ await client.changeFile(modified);
101
+ }
102
+
103
+ async closeFile(uri: string) {
104
+ const client = this.uriToClient[uri];
105
+
106
+ if (client) {
107
+ delete this.uriToClient[uri];
108
+ await client.closeFile(uri);
109
+ }
110
+ }
111
+
112
+ disconnected(disconnectedClient: LSPClient): void {
113
+ for (const [uri, client] of Object.entries(this.uriToClient)) {
114
+ if (client !== disconnectedClient) {
115
+ continue;
116
+ }
117
+ this.closeFile(uri);
118
+ }
119
+ }
120
+
121
+ getClient(lang: string): LSPClient | undefined {
122
+ if (!this.clients[lang]) {
123
+ const transport = this.config.getLspTransport(lang);
124
+ if (!transport) {
125
+ console.warn(`No lsp transport for ${lang}`);
126
+ return undefined;
127
+ }
128
+ const ui = this.editor.ui;
129
+
130
+ const client: LSPClient = new LSPClientImpl(transport, {
131
+ lang,
132
+ rootUri: 'file:///',
133
+ ui,
134
+ });
135
+
136
+ client.addEventListener(
137
+ 'textDocument/publishDiagnostics',
138
+ this.listenerDiag,
139
+ );
140
+ client.addEventListener('close', this.listenerDisconnect);
141
+
142
+ this.clients[lang] = client;
143
+ }
144
+
145
+ return this.clients[lang];
146
+ }
147
+
148
+ destroy() {
149
+ const workspace: Workspace = this.editor.ci.resolve('workspace');
150
+
151
+ workspace.removeEventListener('openFile', this.listenerChange);
152
+ workspace.removeEventListener('modifyFile', this.listenerChange);
153
+
154
+ for (const client of Object.values(this.clients)) {
155
+ client.removeEventListener(
156
+ 'textDocument/publishDiagnostics',
157
+ this.listenerDiag,
158
+ );
159
+ client.removeEventListener(
160
+ 'close',
161
+ this.listenerDisconnect,
162
+ );
163
+ client.destroy();
164
+ }
165
+ this.clients = {};
166
+ this.uriToClient = {};
167
+ this.uriToDiagnostics = {};
168
+ }
169
+ }
@@ -99,3 +99,20 @@ export function computeIncrementalChanges(
99
99
 
100
100
  return changes;
101
101
  }
102
+
103
+ const enum Sync {
104
+ AlwaysIfSmaller = 1024,
105
+ }
106
+
107
+ export function contentChangesFor(
108
+ supportInc: boolean,
109
+ newContent: string,
110
+ oldContent?: string,
111
+ ): lsp.TextDocumentContentChangeEvent[] {
112
+ if (!supportInc || newContent.length < Sync.AlwaysIfSmaller || !oldContent) {
113
+ return [{ text: newContent }];
114
+ }
115
+
116
+ const changes = computeIncrementalChanges(oldContent, newContent);
117
+ return changes.reverse();
118
+ }
package/src/mod.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from './ExtensionLsp.js';
2
2
  export * from './LSPClient.js';
3
- export * from './LSPWorkspace.js';
@@ -1,39 +0,0 @@
1
- import type * as lsp from 'vscode-languageserver-protocol';
2
- import type { EditorUi } from '@kerebron/editor';
3
- import { Workspace, WorkspaceFile } from './workspace.js';
4
- import { PositionMapper } from '@kerebron/extension-markdown/PositionMapper';
5
- import { LSPClient } from './LSPClient.js';
6
- export interface LSPSource {
7
- ui: EditorUi;
8
- getMappedContent(): Promise<{
9
- content: string;
10
- mapper: PositionMapper;
11
- }>;
12
- }
13
- declare class LSPWorkspaceFile implements WorkspaceFile {
14
- readonly uri: string;
15
- readonly languageId: string;
16
- version: number;
17
- content: string;
18
- mapper: PositionMapper;
19
- source: LSPSource;
20
- syncedContent: string;
21
- diagnostics: Array<lsp.Diagnostic>;
22
- constructor(uri: string, languageId: string, version: number, content: string, mapper: PositionMapper, source: LSPSource);
23
- getUi(): EditorUi;
24
- }
25
- export declare class LSPWorkspace extends Workspace {
26
- private client;
27
- files: LSPWorkspaceFile[];
28
- private fileVersions;
29
- constructor(client: LSPClient);
30
- nextFileVersion(uri: string): number;
31
- connected(): Promise<void>;
32
- disconnected(): void;
33
- syncFiles(): void;
34
- changedFile(uri: string): Promise<void>;
35
- openFile(uri: string, languageId: string, source: LSPSource): Promise<void>;
36
- closeFile(uri: string): void;
37
- }
38
- export {};
39
- //# sourceMappingURL=LSPWorkspace.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LSPWorkspace.d.ts","sourceRoot":"","sources":["../src/LSPWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,gCAAgC,CAAC;AAG3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAE7E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,QAAQ,CAAC;IACb,gBAAgB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;CAC1E;AAED,cAAM,gBAAiB,YAAW,aAAa;IAK3C,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM;IACpB,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,cAAc;IACtB,MAAM,EAAE,SAAS;IATnB,aAAa,EAAE,MAAM,CAAM;IAC3B,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAM;gBAGpC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,SAAS;IAI1B,KAAK;CAGN;AAED,qBAAa,YAAa,SAAQ,SAAS;IAI7B,OAAO,CAAC,MAAM;IAH1B,KAAK,EAAE,gBAAgB,EAAE,CAAM;IAC/B,OAAO,CAAC,YAAY,CAAkD;gBAElD,MAAM,EAAE,SAAS;IAIrC,eAAe,CAAC,GAAG,EAAE,MAAM;IAIZ,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAOhC,YAAY,IAAI,IAAI;IAO7B,SAAS;IAUH,WAAW,CAAC,GAAG,EAAE,MAAM;IAqDvB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS;IAsBjE,SAAS,CAAC,GAAG,EAAE,MAAM;CAOtB"}