@kerebron/extension-lsp 0.4.2 → 0.4.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.
- package/README.md +157 -47
- package/assets/lsp-status.css +60 -0
- package/esm/_dnt.shims.d.ts +1 -5
- package/esm/_dnt.shims.d.ts.map +1 -1
- package/esm/_dnt.shims.js +1 -5
- package/esm/extension-lsp/src/LspStatus.d.ts +35 -0
- package/esm/extension-lsp/src/LspStatus.d.ts.map +1 -0
- package/esm/extension-lsp/src/LspStatus.js +128 -0
- package/esm/extension-menu/src/CustomMenuPlugin.d.ts +65 -0
- package/esm/extension-menu/src/CustomMenuPlugin.d.ts.map +1 -0
- package/esm/extension-menu/src/CustomMenuPlugin.js +1186 -0
- package/esm/extension-menu/src/ExtensionCustomMenu.d.ts +14 -0
- package/esm/extension-menu/src/ExtensionCustomMenu.d.ts.map +1 -0
- package/esm/extension-menu/src/ExtensionCustomMenu.js +57 -0
- package/esm/extension-menu/src/buildMenu.d.ts +5 -0
- package/esm/extension-menu/src/buildMenu.d.ts.map +1 -0
- package/esm/extension-menu/src/buildMenu.js +331 -0
- package/esm/extension-menu/src/icons.d.ts +15 -0
- package/esm/extension-menu/src/icons.d.ts.map +1 -0
- package/esm/extension-menu/src/icons.js +123 -0
- package/esm/extension-menu/src/menu.d.ts +81 -0
- package/esm/extension-menu/src/menu.d.ts.map +1 -0
- package/esm/extension-menu/src/menu.js +350 -0
- package/esm/extension-menu/src/mod.d.ts +3 -0
- package/esm/extension-menu/src/mod.d.ts.map +1 -0
- package/esm/extension-menu/src/mod.js +2 -0
- package/esm/extension-menu/src/prompt.d.ts +36 -0
- package/esm/extension-menu/src/prompt.d.ts.map +1 -0
- package/esm/extension-menu/src/prompt.js +158 -0
- package/esm/extension-yjs/src/CollaborationStatus.d.ts +59 -0
- package/esm/extension-yjs/src/CollaborationStatus.d.ts.map +1 -0
- package/esm/extension-yjs/src/CollaborationStatus.js +286 -0
- package/esm/wasm/src/mod.js +2 -2
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,82 +1,192 @@
|
|
|
1
|
-
#
|
|
1
|
+
# LSP Extension
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Language Server Protocol (LSP) integration for Kerebron editors.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
<img src="https://github.com/user-attachments/assets/b63ec84a-0ed2-4f98-920c-76f6d3215168" alt="Alt Text" width="200">
|
|
7
|
-
</a>
|
|
5
|
+
## Features
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
- **LSP Client**: Connect to any LSP-compatible language server
|
|
8
|
+
- **WebSocket Transport**: Built-in WebSocket transport for browser-based connections
|
|
9
|
+
- **Autocomplete**: Get intelligent code completions from the language server
|
|
10
|
+
- **Diagnostics**: Display errors and warnings from the language server
|
|
11
|
+
- **Status Indicator**: Visual indicator showing LSP connection status in the toolbar
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Installation
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
```bash
|
|
16
|
+
npm install @kerebron/extension-lsp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Basic Setup
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { CoreEditor } from '@kerebron/editor';
|
|
25
|
+
import { ExtensionLsp, type LspTransportGetter, type Transport } from '@kerebron/extension-lsp';
|
|
26
|
+
import { LspWebSocketTransport } from '@kerebron/extension-lsp/LspWebSocketTransport';
|
|
27
|
+
|
|
28
|
+
// Import styles for the status indicator
|
|
29
|
+
import '@kerebron/extension-lsp/assets/lsp-status.css';
|
|
30
|
+
|
|
31
|
+
// Configure transport for different languages
|
|
32
|
+
const getLspTransport: LspTransportGetter = (lang: string): Transport | undefined => {
|
|
33
|
+
const baseUrl = 'ws://localhost:8000/lsp';
|
|
34
|
+
|
|
35
|
+
switch (lang) {
|
|
36
|
+
case 'markdown':
|
|
37
|
+
return new LspWebSocketTransport(`${baseUrl}/markdown`);
|
|
38
|
+
case 'typescript':
|
|
39
|
+
case 'javascript':
|
|
40
|
+
return new LspWebSocketTransport(`${baseUrl}/typescript`);
|
|
41
|
+
case 'json':
|
|
42
|
+
return new LspWebSocketTransport(`${baseUrl}/json`);
|
|
43
|
+
default:
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const editor = new CoreEditor({
|
|
49
|
+
element: document.getElementById('editor'),
|
|
50
|
+
uri: 'file:///document.md', // Required: LSP needs a file URI
|
|
51
|
+
extensions: [
|
|
52
|
+
// ... other extensions
|
|
53
|
+
new ExtensionLsp({ getLspTransport }),
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
14
57
|
|
|
15
|
-
|
|
16
|
-
incompatibilities.
|
|
58
|
+
### With Toolbar Status Indicator
|
|
17
59
|
|
|
18
|
-
|
|
19
|
-
them in sync.
|
|
60
|
+
When used with `ExtensionCustomMenu`, an LSP status indicator is automatically added to the toolbar:
|
|
20
61
|
|
|
21
|
-
|
|
22
|
-
|
|
62
|
+
```typescript
|
|
63
|
+
import { ExtensionCustomMenu } from '@kerebron/extension-menu';
|
|
64
|
+
import { ExtensionLsp } from '@kerebron/extension-lsp';
|
|
23
65
|
|
|
24
|
-
|
|
25
|
-
|
|
66
|
+
import '@kerebron/extension-menu/assets/custom-menu.css';
|
|
67
|
+
import '@kerebron/extension-lsp/assets/lsp-status.css';
|
|
26
68
|
|
|
27
|
-
|
|
69
|
+
const editor = new CoreEditor({
|
|
70
|
+
element: document.getElementById('editor'),
|
|
71
|
+
uri: 'file:///document.md',
|
|
72
|
+
extensions: [
|
|
73
|
+
// ... other extensions
|
|
74
|
+
new ExtensionLsp({ getLspTransport }),
|
|
75
|
+
new ExtensionCustomMenu(), // Must come AFTER ExtensionLsp
|
|
76
|
+
],
|
|
77
|
+
});
|
|
78
|
+
```
|
|
28
79
|
|
|
80
|
+
The status indicator shows:
|
|
29
81
|
|
|
30
|
-
|
|
82
|
+
| Status | Indicator | Description |
|
|
83
|
+
|--------|-----------|-------------|
|
|
84
|
+
| Connected | 🟢 Green dot | Successfully connected to LSP server |
|
|
85
|
+
| Connecting | 🟡 Yellow pulsing dot | Attempting to connect |
|
|
86
|
+
| Disconnected | 🔴 Red dot | Not connected to LSP server |
|
|
31
87
|
|
|
32
|
-
###
|
|
88
|
+
### Disable Auto Status Indicator
|
|
33
89
|
|
|
34
|
-
```
|
|
35
|
-
|
|
90
|
+
```typescript
|
|
91
|
+
new ExtensionCustomMenu({ autoAddLspStatus: false })
|
|
36
92
|
```
|
|
37
93
|
|
|
38
|
-
|
|
94
|
+
### Manual Status Element
|
|
39
95
|
|
|
40
|
-
|
|
96
|
+
You can manually add the LSP status element for more control:
|
|
41
97
|
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
```
|
|
98
|
+
```typescript
|
|
99
|
+
import { LspStatusElement } from '@kerebron/extension-lsp/LspStatus';
|
|
45
100
|
|
|
46
|
-
|
|
101
|
+
const lspExtension = new ExtensionLsp({ getLspTransport });
|
|
47
102
|
|
|
48
|
-
|
|
103
|
+
new ExtensionCustomMenu({
|
|
104
|
+
autoAddLspStatus: false,
|
|
105
|
+
trailingElements: [
|
|
106
|
+
new LspStatusElement({
|
|
107
|
+
lspExtension,
|
|
108
|
+
label: 'Language Server', // Custom label (default: 'LSP')
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
```
|
|
49
113
|
|
|
50
|
-
|
|
114
|
+
## Configuration
|
|
51
115
|
|
|
52
|
-
|
|
53
|
-
a hybrid npm module for ESM and CommonJS
|
|
54
|
-
- https://github.com/denoland/dnt
|
|
55
|
-
- https://gaubee.com/article/Publishing-Your-Deno-Project-as-a-Monorepo-using-dnt/
|
|
116
|
+
### LspConfig
|
|
56
117
|
|
|
57
|
-
|
|
118
|
+
| Option | Type | Required | Description |
|
|
119
|
+
|--------|------|----------|-------------|
|
|
120
|
+
| `getLspTransport` | `LspTransportGetter` | Yes | Function that returns a transport for a given language |
|
|
58
121
|
|
|
59
|
-
|
|
60
|
-
|
|
122
|
+
### LspTransportGetter
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
type LspTransportGetter = (lang: string) => Transport | undefined;
|
|
61
126
|
```
|
|
62
127
|
|
|
63
|
-
|
|
128
|
+
Returns a `Transport` instance for the specified language, or `undefined` if no LSP support is available for that language.
|
|
64
129
|
|
|
65
|
-
|
|
66
|
-
docker build . -t editor-test
|
|
67
|
-
docker run -it -p 8000:8000 -v $PWD:/usr/src/app editor-test
|
|
68
|
-
```
|
|
130
|
+
## Transports
|
|
69
131
|
|
|
70
|
-
|
|
132
|
+
### LspWebSocketTransport
|
|
71
133
|
|
|
72
|
-
|
|
134
|
+
Built-in WebSocket transport for browser-based LSP connections:
|
|
73
135
|
|
|
136
|
+
```typescript
|
|
137
|
+
import { LspWebSocketTransport } from '@kerebron/extension-lsp/LspWebSocketTransport';
|
|
138
|
+
|
|
139
|
+
const transport = new LspWebSocketTransport('ws://localhost:8000/lsp/typescript');
|
|
74
140
|
```
|
|
75
|
-
npm install -g deno
|
|
76
|
-
```
|
|
77
141
|
|
|
78
|
-
|
|
142
|
+
### Custom Transport
|
|
143
|
+
|
|
144
|
+
Implement the `Transport` interface for custom transport mechanisms:
|
|
79
145
|
|
|
146
|
+
```typescript
|
|
147
|
+
interface Transport {
|
|
148
|
+
connect(): Promise<void>;
|
|
149
|
+
disconnect(): void;
|
|
150
|
+
send(message: string): void;
|
|
151
|
+
onMessage(handler: (message: string) => void): void;
|
|
152
|
+
onError(handler: (error: Error) => void): void;
|
|
153
|
+
onClose(handler: () => void): void;
|
|
154
|
+
}
|
|
80
155
|
```
|
|
81
|
-
|
|
156
|
+
|
|
157
|
+
## Server Setup
|
|
158
|
+
|
|
159
|
+
The LSP extension requires a server that proxies WebSocket connections to LSP servers. See the `server-deno-hono` example for a reference implementation.
|
|
160
|
+
|
|
161
|
+
Example server routes:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Proxy to a markdown language server
|
|
165
|
+
app.get('/lsp/markdown', upgradeWebSocket(() => {
|
|
166
|
+
return lspAdapter.upgradeWebSocket();
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
// Proxy to TypeScript language server
|
|
170
|
+
app.get('/lsp/typescript', upgradeWebSocket(() => {
|
|
171
|
+
return proxyProcess('npx', ['typescript-language-server', '--stdio']);
|
|
172
|
+
}));
|
|
82
173
|
```
|
|
174
|
+
|
|
175
|
+
## CSS Classes
|
|
176
|
+
|
|
177
|
+
The status indicator uses the following CSS classes for customization:
|
|
178
|
+
|
|
179
|
+
| Class | Description |
|
|
180
|
+
|-------|-------------|
|
|
181
|
+
| `.kb-lsp-status` | Container element |
|
|
182
|
+
| `.kb-lsp-status__button` | Button wrapper |
|
|
183
|
+
| `.kb-lsp-status__dot` | Status dot indicator |
|
|
184
|
+
| `.kb-lsp-status__dot--connected` | Green connected state |
|
|
185
|
+
| `.kb-lsp-status__dot--connecting` | Yellow connecting state (pulsing) |
|
|
186
|
+
| `.kb-lsp-status__dot--disconnected` | Red disconnected state |
|
|
187
|
+
| `.kb-lsp-status__label` | Text label |
|
|
188
|
+
|
|
189
|
+
## Related
|
|
190
|
+
|
|
191
|
+
- [@kerebron/extension-menu](../extension-menu) - Custom menu with auto LSP status detection
|
|
192
|
+
- [@kerebron/extension-autocomplete](../extension-autocomplete) - Autocomplete functionality
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* LSP Status Menu Element */
|
|
2
|
+
.kb-lsp-status {
|
|
3
|
+
position: relative;
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.kb-lsp-status__indicator {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 0.35rem;
|
|
12
|
+
padding: 0.35rem 0.6rem;
|
|
13
|
+
border: 1px solid var(--kb-border, #444);
|
|
14
|
+
border-radius: 4px;
|
|
15
|
+
background: var(--kb-surface, #2a2a2a);
|
|
16
|
+
color: var(--kb-text, #e8e8e8);
|
|
17
|
+
font-size: 0.85rem;
|
|
18
|
+
cursor: default;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Status dot indicator */
|
|
22
|
+
.kb-lsp-status__dot {
|
|
23
|
+
width: 8px;
|
|
24
|
+
height: 8px;
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.kb-lsp-status__dot--connected {
|
|
30
|
+
background-color: #22c55e;
|
|
31
|
+
box-shadow: 0 0 4px rgba(34, 197, 94, 0.5);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.kb-lsp-status__dot--connecting {
|
|
35
|
+
background-color: #eab308;
|
|
36
|
+
box-shadow: 0 0 4px rgba(234, 179, 8, 0.5);
|
|
37
|
+
animation: kb-lsp-pulse 1.5s ease-in-out infinite;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.kb-lsp-status__dot--disconnected {
|
|
41
|
+
background-color: #ef4444;
|
|
42
|
+
box-shadow: 0 0 4px rgba(239, 68, 68, 0.5);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@keyframes kb-lsp-pulse {
|
|
46
|
+
0%, 100% {
|
|
47
|
+
opacity: 1;
|
|
48
|
+
}
|
|
49
|
+
50% {
|
|
50
|
+
opacity: 0.5;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Label */
|
|
55
|
+
.kb-lsp-status__label {
|
|
56
|
+
font-weight: 500;
|
|
57
|
+
font-size: 0.8rem;
|
|
58
|
+
text-transform: uppercase;
|
|
59
|
+
letter-spacing: 0.03em;
|
|
60
|
+
}
|
package/esm/_dnt.shims.d.ts
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export { Deno } from "@deno/shim-deno";
|
|
3
|
-
export declare const dntGlobalThis: Omit<typeof globalThis, "Deno"> & {
|
|
4
|
-
Deno: typeof Deno;
|
|
5
|
-
};
|
|
1
|
+
export declare const dntGlobalThis: Omit<typeof globalThis, never>;
|
|
6
2
|
//# sourceMappingURL=_dnt.shims.d.ts.map
|
package/esm/_dnt.shims.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,gCAA2C,CAAC"}
|
package/esm/_dnt.shims.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export { Deno } from "@deno/shim-deno";
|
|
3
|
-
const dntGlobals = {
|
|
4
|
-
Deno,
|
|
5
|
-
};
|
|
1
|
+
const dntGlobals = {};
|
|
6
2
|
export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
7
3
|
function createMergeProxy(baseObj, extObj) {
|
|
8
4
|
return new Proxy(baseObj, {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import "../../_dnt.polyfills.js";
|
|
2
|
+
import { EditorState } from 'prosemirror-state';
|
|
3
|
+
import { EditorView } from 'prosemirror-view';
|
|
4
|
+
import type { MenuElement } from '../../extension-menu/src/mod.js';
|
|
5
|
+
import type { ExtensionLsp } from './ExtensionLsp.js';
|
|
6
|
+
export type LspConnectionStatus = 'connected' | 'connecting' | 'disconnected';
|
|
7
|
+
export interface LspStatusOptions {
|
|
8
|
+
/** The LSP extension instance to monitor */
|
|
9
|
+
lspExtension: ExtensionLsp;
|
|
10
|
+
/** Optional label to show (e.g., 'LSP' or language name) */
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A menu element that displays LSP connection status.
|
|
15
|
+
* Shows a status indicator dot with an optional label.
|
|
16
|
+
*/
|
|
17
|
+
export declare class LspStatusElement implements MenuElement {
|
|
18
|
+
private lspExtension;
|
|
19
|
+
private label;
|
|
20
|
+
private dom;
|
|
21
|
+
private statusDot;
|
|
22
|
+
private labelEl;
|
|
23
|
+
private status;
|
|
24
|
+
private statusCheckInterval;
|
|
25
|
+
constructor(options: LspStatusOptions);
|
|
26
|
+
render(_view: EditorView): {
|
|
27
|
+
dom: HTMLElement;
|
|
28
|
+
update: (state: EditorState) => boolean;
|
|
29
|
+
};
|
|
30
|
+
private getClient;
|
|
31
|
+
private updateStatusFromClient;
|
|
32
|
+
private setStatus;
|
|
33
|
+
destroy(): void;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=LspStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LspStatus.d.ts","sourceRoot":"","sources":["../../../src/extension-lsp/src/LspStatus.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,YAAY,EAAE,YAAY,CAAC;IAC3B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,WAAW;IAClD,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,GAAG,CAA4B;IACvC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,mBAAmB,CAA+C;gBAE9D,OAAO,EAAE,gBAAgB;IAKrC,MAAM,CACJ,KAAK,EAAE,UAAU,GAChB;QAAE,GAAG,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;KAAE;IAyChE,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,SAAS;IAoBjB,OAAO;CAMR"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import "../../_dnt.polyfills.js";
|
|
2
|
+
const CSS_PREFIX = 'kb-lsp-status';
|
|
3
|
+
/**
|
|
4
|
+
* A menu element that displays LSP connection status.
|
|
5
|
+
* Shows a status indicator dot with an optional label.
|
|
6
|
+
*/
|
|
7
|
+
export class LspStatusElement {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
Object.defineProperty(this, "lspExtension", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: void 0
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(this, "label", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "dom", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: null
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "statusDot", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: null
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "labelEl", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: null
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(this, "status", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
value: 'disconnected'
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(this, "statusCheckInterval", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: null
|
|
50
|
+
});
|
|
51
|
+
this.lspExtension = options.lspExtension;
|
|
52
|
+
this.label = options.label ?? 'LSP';
|
|
53
|
+
}
|
|
54
|
+
render(_view) {
|
|
55
|
+
// Create main container
|
|
56
|
+
this.dom = document.createElement('div');
|
|
57
|
+
this.dom.className = CSS_PREFIX;
|
|
58
|
+
this.dom.setAttribute('role', 'status');
|
|
59
|
+
this.dom.setAttribute('aria-live', 'polite');
|
|
60
|
+
// Create status indicator
|
|
61
|
+
const indicator = document.createElement('div');
|
|
62
|
+
indicator.className = `${CSS_PREFIX}__indicator`;
|
|
63
|
+
// Status dot
|
|
64
|
+
this.statusDot = document.createElement('span');
|
|
65
|
+
this.statusDot.className =
|
|
66
|
+
`${CSS_PREFIX}__dot ${CSS_PREFIX}__dot--disconnected`;
|
|
67
|
+
indicator.appendChild(this.statusDot);
|
|
68
|
+
// Label
|
|
69
|
+
this.labelEl = document.createElement('span');
|
|
70
|
+
this.labelEl.className = `${CSS_PREFIX}__label`;
|
|
71
|
+
this.labelEl.textContent = this.label;
|
|
72
|
+
indicator.appendChild(this.labelEl);
|
|
73
|
+
this.dom.appendChild(indicator);
|
|
74
|
+
// Set initial status based on client state
|
|
75
|
+
this.updateStatusFromClient();
|
|
76
|
+
// Poll for status changes since the client is created lazily
|
|
77
|
+
this.statusCheckInterval = setInterval(() => {
|
|
78
|
+
this.updateStatusFromClient();
|
|
79
|
+
}, 1000);
|
|
80
|
+
const update = (_state) => {
|
|
81
|
+
// Always visible
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
return { dom: this.dom, update };
|
|
85
|
+
}
|
|
86
|
+
getClient() {
|
|
87
|
+
const mainLang = this.lspExtension.mainLang || 'markdown';
|
|
88
|
+
return this.lspExtension.getClient(mainLang);
|
|
89
|
+
}
|
|
90
|
+
updateStatusFromClient() {
|
|
91
|
+
const client = this.getClient();
|
|
92
|
+
// Check if the client has server capabilities (meaning it's connected and initialized)
|
|
93
|
+
if (client?.serverCapabilities) {
|
|
94
|
+
this.setStatus('connected');
|
|
95
|
+
}
|
|
96
|
+
else if (client?.active) {
|
|
97
|
+
this.setStatus('connecting');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.setStatus('disconnected');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
setStatus(status) {
|
|
104
|
+
if (this.status === status)
|
|
105
|
+
return; // No change
|
|
106
|
+
this.status = status;
|
|
107
|
+
if (this.statusDot) {
|
|
108
|
+
this.statusDot.className =
|
|
109
|
+
`${CSS_PREFIX}__dot ${CSS_PREFIX}__dot--${status}`;
|
|
110
|
+
}
|
|
111
|
+
if (this.dom) {
|
|
112
|
+
this.dom.setAttribute('data-status', status);
|
|
113
|
+
const statusLabels = {
|
|
114
|
+
connected: 'LSP Connected',
|
|
115
|
+
connecting: 'LSP Connecting...',
|
|
116
|
+
disconnected: 'LSP Disconnected',
|
|
117
|
+
};
|
|
118
|
+
this.dom.title = statusLabels[status];
|
|
119
|
+
this.dom.setAttribute('aria-label', statusLabels[status]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
destroy() {
|
|
123
|
+
if (this.statusCheckInterval) {
|
|
124
|
+
clearInterval(this.statusCheckInterval);
|
|
125
|
+
this.statusCheckInterval = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { EditorState, Plugin } from 'prosemirror-state';
|
|
2
|
+
import { EditorView } from 'prosemirror-view';
|
|
3
|
+
import type { CoreEditor } from '../../editor/src/mod.js';
|
|
4
|
+
import type { MenuElement } from './menu.js';
|
|
5
|
+
interface ToolItem {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
element: MenuElement;
|
|
9
|
+
isPinned: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class CustomMenuView {
|
|
12
|
+
readonly editorView: EditorView;
|
|
13
|
+
readonly editor: CoreEditor;
|
|
14
|
+
readonly content: readonly (readonly MenuElement[])[];
|
|
15
|
+
wrapper: HTMLElement;
|
|
16
|
+
toolbar: HTMLElement;
|
|
17
|
+
overflowMenu: HTMLElement;
|
|
18
|
+
pinnedDropdownMenu: HTMLElement | null;
|
|
19
|
+
modal: HTMLElement | null;
|
|
20
|
+
tools: ToolItem[];
|
|
21
|
+
trailingElements: MenuElement[];
|
|
22
|
+
root: Document | ShadowRoot;
|
|
23
|
+
resizeHandle: HTMLElement;
|
|
24
|
+
editorContainer: HTMLElement;
|
|
25
|
+
private closeOverflowHandler;
|
|
26
|
+
private closePinnedDropdownHandler;
|
|
27
|
+
private submenuStack;
|
|
28
|
+
private pinnedDropdownStack;
|
|
29
|
+
constructor(editorView: EditorView, editor: CoreEditor, content: readonly (readonly MenuElement[])[], trailingElements?: readonly MenuElement[]);
|
|
30
|
+
private initializeTools;
|
|
31
|
+
/**
|
|
32
|
+
* Generate a stable ID from a label by converting it to a kebab-case slug.
|
|
33
|
+
* Falls back to a hash if the label is empty or contains only special characters.
|
|
34
|
+
*/
|
|
35
|
+
private generateStableId;
|
|
36
|
+
/**
|
|
37
|
+
* Simple hash function for generating stable IDs from strings.
|
|
38
|
+
*/
|
|
39
|
+
private simpleHash;
|
|
40
|
+
private extractLabel;
|
|
41
|
+
private loadPinnedState;
|
|
42
|
+
private savePinnedState;
|
|
43
|
+
private showSubmenu;
|
|
44
|
+
private goBack;
|
|
45
|
+
private showPinnedDropdown;
|
|
46
|
+
private pinnedDropdownGoBack;
|
|
47
|
+
private pinnedDropdownShowSubmenu;
|
|
48
|
+
private renderPinnedDropdown;
|
|
49
|
+
private renderOverflowMenu;
|
|
50
|
+
private render;
|
|
51
|
+
private openManageModal;
|
|
52
|
+
private updateModalState;
|
|
53
|
+
private setupResize;
|
|
54
|
+
update(view: EditorView, prevState: EditorState): void;
|
|
55
|
+
destroy(): void;
|
|
56
|
+
}
|
|
57
|
+
export interface CustomMenuPluginOptions {
|
|
58
|
+
content: readonly (readonly MenuElement[])[];
|
|
59
|
+
trailingElements?: readonly MenuElement[];
|
|
60
|
+
}
|
|
61
|
+
export declare class CustomMenuPlugin extends Plugin {
|
|
62
|
+
constructor(editor: CoreEditor, options: CustomMenuPluginOptions);
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
65
|
+
//# sourceMappingURL=CustomMenuPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomMenuPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-menu/src/CustomMenuPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAI1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAuB7C,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,cAAc;IAmBvB,QAAQ,CAAC,UAAU,EAAE,UAAU;IAC/B,QAAQ,CAAC,MAAM,EAAE,UAAU;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE;IApBvD,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,WAAW,CAAC;IACrB,YAAY,EAAE,WAAW,CAAC;IAC1B,kBAAkB,EAAE,WAAW,GAAG,IAAI,CAAQ;IAC9C,KAAK,EAAE,WAAW,GAAG,IAAI,CAAQ;IACjC,KAAK,EAAE,QAAQ,EAAE,CAAM;IACvB,gBAAgB,EAAE,WAAW,EAAE,CAAM;IACrC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,YAAY,EAAE,WAAW,CAAC;IAC1B,eAAe,EAAE,WAAW,CAAC;IAC7B,OAAO,CAAC,oBAAoB,CAA0C;IACtE,OAAO,CAAC,0BAA0B,CAA0C;IAC5E,OAAO,CAAC,YAAY,CAAmD;IACvE,OAAO,CAAC,mBAAmB,CAEpB;gBAGI,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE,EACrD,gBAAgB,CAAC,EAAE,SAAS,WAAW,EAAE;IAmE3C,OAAO,CAAC,eAAe;IAgCvB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAyBxB;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,WAAW;IAqCnB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,kBAAkB;IA0C1B,OAAO,CAAC,oBAAoB;IAsB5B,OAAO,CAAC,yBAAyB;IAuCjC,OAAO,CAAC,oBAAoB;IA8L5B,OAAO,CAAC,kBAAkB;IA6R1B,OAAO,CAAC,MAAM;IAuMd,OAAO,CAAC,eAAe;IAyHvB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,WAAW;IAgDnB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW;IAQ/C,OAAO;CA8BR;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE,CAAC;IAC7C,gBAAgB,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CAC3C;AAED,qBAAa,gBAAiB,SAAQ,MAAM;gBAC9B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,uBAAuB;CAYjE"}
|