@autumnsgrove/groveengine 0.6.3 → 0.6.5
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/dist/components/admin/FloatingToolbar.svelte +373 -0
- package/dist/components/admin/FloatingToolbar.svelte.d.ts +17 -0
- package/dist/components/admin/MarkdownEditor.svelte +26 -347
- package/dist/components/admin/MarkdownEditor.svelte.d.ts +1 -1
- package/dist/components/admin/composables/index.d.ts +0 -2
- package/dist/components/admin/composables/index.js +0 -2
- package/dist/components/custom/MobileTOC.svelte +20 -13
- package/dist/components/quota/UpgradePrompt.svelte +1 -1
- package/dist/server/services/database.d.ts +138 -0
- package/dist/server/services/database.js +234 -0
- package/dist/server/services/index.d.ts +5 -1
- package/dist/server/services/index.js +24 -2
- package/dist/server/services/turnstile.d.ts +66 -0
- package/dist/server/services/turnstile.js +131 -0
- package/dist/server/services/users.d.ts +104 -0
- package/dist/server/services/users.js +158 -0
- package/dist/styles/README.md +50 -0
- package/dist/styles/vine-pattern.css +24 -0
- package/dist/types/turnstile.d.ts +42 -0
- package/dist/ui/components/forms/TurnstileWidget.svelte +111 -0
- package/dist/ui/components/forms/TurnstileWidget.svelte.d.ts +14 -0
- package/dist/ui/components/primitives/dialog/dialog-overlay.svelte +1 -1
- package/dist/ui/components/primitives/sheet/sheet-overlay.svelte +1 -1
- package/dist/ui/components/ui/Logo.svelte +161 -23
- package/dist/ui/components/ui/Logo.svelte.d.ts +4 -10
- package/dist/ui/tokens/fonts.d.ts +69 -0
- package/dist/ui/tokens/fonts.js +341 -0
- package/dist/ui/tokens/index.d.ts +6 -5
- package/dist/ui/tokens/index.js +7 -6
- package/package.json +1 -1
- package/static/robots.txt +487 -0
- package/dist/components/admin/composables/useCommandPalette.svelte.d.ts +0 -87
- package/dist/components/admin/composables/useCommandPalette.svelte.js +0 -158
- package/dist/components/admin/composables/useSlashCommands.svelte.d.ts +0 -104
- package/dist/components/admin/composables/useSlashCommands.svelte.js +0 -215
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command Palette Composable
|
|
3
|
-
* Manages the command palette (Cmd+K) functionality
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} PaletteAction
|
|
8
|
-
* @property {string} id
|
|
9
|
-
* @property {string} label
|
|
10
|
-
* @property {string} shortcut
|
|
11
|
-
* @property {() => void} action
|
|
12
|
-
* @property {string} [themeKey]
|
|
13
|
-
* @property {boolean} [isTheme]
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {Object} CommandPaletteState
|
|
18
|
-
* @property {boolean} open
|
|
19
|
-
* @property {string} query
|
|
20
|
-
* @property {number} selectedIndex
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @typedef {Object} CommandPaletteOptions
|
|
25
|
-
* @property {() => PaletteAction[]} [getActions] - Function to get available actions
|
|
26
|
-
* @property {() => Record<string, any>} [getThemes] - Function to get available themes
|
|
27
|
-
* @property {() => string} [getCurrentTheme] - Function to get current theme
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @typedef {Object} CommandPaletteManager
|
|
32
|
-
* @property {CommandPaletteState} state
|
|
33
|
-
* @property {boolean} isOpen
|
|
34
|
-
* @property {string} query
|
|
35
|
-
* @property {number} selectedIndex
|
|
36
|
-
* @property {() => PaletteAction[]} getAllCommands
|
|
37
|
-
* @property {() => PaletteAction[]} getFilteredCommands
|
|
38
|
-
* @property {() => void} open
|
|
39
|
-
* @property {() => void} close
|
|
40
|
-
* @property {() => void} toggle
|
|
41
|
-
* @property {(direction: 'up' | 'down') => void} navigate
|
|
42
|
-
* @property {(index: number) => PaletteAction | undefined} execute
|
|
43
|
-
* @property {(query: string) => void} setQuery
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Creates a command palette manager with Svelte 5 runes
|
|
48
|
-
* @param {CommandPaletteOptions} options - Configuration options
|
|
49
|
-
* @returns {CommandPaletteManager} Command palette state and controls
|
|
50
|
-
*/
|
|
51
|
-
export function useCommandPalette(options = {}) {
|
|
52
|
-
const { getActions, getThemes, getCurrentTheme } = options;
|
|
53
|
-
|
|
54
|
-
let state = $state({
|
|
55
|
-
open: false,
|
|
56
|
-
query: "",
|
|
57
|
-
selectedIndex: 0,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Get all commands including theme commands
|
|
61
|
-
function getAllCommands() {
|
|
62
|
-
const actions = getActions ? getActions() : [];
|
|
63
|
-
const themes = getThemes ? getThemes() : {};
|
|
64
|
-
const currentTheme = getCurrentTheme ? getCurrentTheme() : "";
|
|
65
|
-
|
|
66
|
-
const themeCommands = Object.entries(themes).map(([key, theme]) => ({
|
|
67
|
-
id: `theme-${key}`,
|
|
68
|
-
label: `Theme: ${theme.label} (${theme.desc})`,
|
|
69
|
-
shortcut: currentTheme === key ? "●" : "",
|
|
70
|
-
action: () => {
|
|
71
|
-
// Theme action is handled by the caller
|
|
72
|
-
},
|
|
73
|
-
themeKey: key,
|
|
74
|
-
isTheme: true,
|
|
75
|
-
}));
|
|
76
|
-
|
|
77
|
-
return [...actions, ...themeCommands];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Get filtered commands based on query
|
|
81
|
-
function getFilteredCommands() {
|
|
82
|
-
const allCommands = getAllCommands();
|
|
83
|
-
return allCommands.filter((cmd) =>
|
|
84
|
-
cmd.label.toLowerCase().includes(state.query.toLowerCase())
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function open() {
|
|
89
|
-
state.open = true;
|
|
90
|
-
state.query = "";
|
|
91
|
-
state.selectedIndex = 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function close() {
|
|
95
|
-
state.open = false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function toggle() {
|
|
99
|
-
if (state.open) {
|
|
100
|
-
close();
|
|
101
|
-
} else {
|
|
102
|
-
open();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** @param {'up' | 'down'} direction */
|
|
107
|
-
function navigate(direction) {
|
|
108
|
-
const filtered = getFilteredCommands();
|
|
109
|
-
const count = filtered.length;
|
|
110
|
-
if (count === 0) return;
|
|
111
|
-
|
|
112
|
-
if (direction === "down") {
|
|
113
|
-
state.selectedIndex = (state.selectedIndex + 1) % count;
|
|
114
|
-
} else if (direction === "up") {
|
|
115
|
-
state.selectedIndex = (state.selectedIndex - 1 + count) % count;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** @param {number} index */
|
|
120
|
-
function execute(index) {
|
|
121
|
-
const filtered = getFilteredCommands();
|
|
122
|
-
const cmd = filtered[index];
|
|
123
|
-
if (cmd && cmd.action) {
|
|
124
|
-
cmd.action();
|
|
125
|
-
close();
|
|
126
|
-
}
|
|
127
|
-
return cmd;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** @param {string} query */
|
|
131
|
-
function setQuery(query) {
|
|
132
|
-
state.query = query;
|
|
133
|
-
state.selectedIndex = 0;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
get state() {
|
|
138
|
-
return state;
|
|
139
|
-
},
|
|
140
|
-
get isOpen() {
|
|
141
|
-
return state.open;
|
|
142
|
-
},
|
|
143
|
-
get query() {
|
|
144
|
-
return state.query;
|
|
145
|
-
},
|
|
146
|
-
get selectedIndex() {
|
|
147
|
-
return state.selectedIndex;
|
|
148
|
-
},
|
|
149
|
-
getAllCommands,
|
|
150
|
-
getFilteredCommands,
|
|
151
|
-
open,
|
|
152
|
-
close,
|
|
153
|
-
toggle,
|
|
154
|
-
navigate,
|
|
155
|
-
execute,
|
|
156
|
-
setQuery,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a slash commands manager with Svelte 5 runes
|
|
3
|
-
* @param {SlashCommandsOptions} options - Configuration options
|
|
4
|
-
* @returns {SlashCommandsManager} Slash commands state and controls
|
|
5
|
-
*/
|
|
6
|
-
export function useSlashCommands(options?: SlashCommandsOptions): SlashCommandsManager;
|
|
7
|
-
/**
|
|
8
|
-
* Slash Commands Composable
|
|
9
|
-
* Manages the slash command menu and execution
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* @typedef {Object} SlashCommand
|
|
13
|
-
* @property {string} id
|
|
14
|
-
* @property {string} label
|
|
15
|
-
* @property {string} insert
|
|
16
|
-
* @property {number} [cursorOffset]
|
|
17
|
-
* @property {boolean} [isSnippet]
|
|
18
|
-
* @property {boolean} [isAction]
|
|
19
|
-
* @property {(() => void)} [action]
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* @typedef {Object} SlashMenuState
|
|
23
|
-
* @property {boolean} open
|
|
24
|
-
* @property {string} query
|
|
25
|
-
* @property {{x: number, y: number}} position
|
|
26
|
-
* @property {number} selectedIndex
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* @typedef {Object} SlashCommandsOptions
|
|
30
|
-
* @property {() => HTMLTextAreaElement|null} [getTextareaRef] - Function to get textarea reference
|
|
31
|
-
* @property {() => string} [getContent] - Function to get content
|
|
32
|
-
* @property {(content: string) => void} [setContent] - Function to set content
|
|
33
|
-
* @property {() => Array<{id: string, name: string, content: string}>} [getSnippets] - Function to get user snippets
|
|
34
|
-
* @property {() => void} [onOpenSnippetsModal] - Callback to open snippets modal
|
|
35
|
-
*/
|
|
36
|
-
/**
|
|
37
|
-
* @typedef {Object} SlashCommandsManager
|
|
38
|
-
* @property {SlashMenuState} menu
|
|
39
|
-
* @property {boolean} isOpen
|
|
40
|
-
* @property {() => SlashCommand[]} getAllCommands
|
|
41
|
-
* @property {() => SlashCommand[]} getFilteredCommands
|
|
42
|
-
* @property {() => void} open
|
|
43
|
-
* @property {() => void} close
|
|
44
|
-
* @property {(direction: 'up' | 'down') => void} navigate
|
|
45
|
-
* @property {(index: number) => void} execute
|
|
46
|
-
* @property {(key: string, cursorPos: number, content: string) => boolean} shouldTrigger
|
|
47
|
-
*/
|
|
48
|
-
/** @type {SlashCommand[]} */
|
|
49
|
-
export const baseSlashCommands: SlashCommand[];
|
|
50
|
-
export type SlashCommand = {
|
|
51
|
-
id: string;
|
|
52
|
-
label: string;
|
|
53
|
-
insert: string;
|
|
54
|
-
cursorOffset?: number | undefined;
|
|
55
|
-
isSnippet?: boolean | undefined;
|
|
56
|
-
isAction?: boolean | undefined;
|
|
57
|
-
action?: (() => void) | undefined;
|
|
58
|
-
};
|
|
59
|
-
export type SlashMenuState = {
|
|
60
|
-
open: boolean;
|
|
61
|
-
query: string;
|
|
62
|
-
position: {
|
|
63
|
-
x: number;
|
|
64
|
-
y: number;
|
|
65
|
-
};
|
|
66
|
-
selectedIndex: number;
|
|
67
|
-
};
|
|
68
|
-
export type SlashCommandsOptions = {
|
|
69
|
-
/**
|
|
70
|
-
* - Function to get textarea reference
|
|
71
|
-
*/
|
|
72
|
-
getTextareaRef?: (() => HTMLTextAreaElement | null) | undefined;
|
|
73
|
-
/**
|
|
74
|
-
* - Function to get content
|
|
75
|
-
*/
|
|
76
|
-
getContent?: (() => string) | undefined;
|
|
77
|
-
/**
|
|
78
|
-
* - Function to set content
|
|
79
|
-
*/
|
|
80
|
-
setContent?: ((content: string) => void) | undefined;
|
|
81
|
-
/**
|
|
82
|
-
* - Function to get user snippets
|
|
83
|
-
*/
|
|
84
|
-
getSnippets?: (() => Array<{
|
|
85
|
-
id: string;
|
|
86
|
-
name: string;
|
|
87
|
-
content: string;
|
|
88
|
-
}>) | undefined;
|
|
89
|
-
/**
|
|
90
|
-
* - Callback to open snippets modal
|
|
91
|
-
*/
|
|
92
|
-
onOpenSnippetsModal?: (() => void) | undefined;
|
|
93
|
-
};
|
|
94
|
-
export type SlashCommandsManager = {
|
|
95
|
-
menu: SlashMenuState;
|
|
96
|
-
isOpen: boolean;
|
|
97
|
-
getAllCommands: () => SlashCommand[];
|
|
98
|
-
getFilteredCommands: () => SlashCommand[];
|
|
99
|
-
open: () => void;
|
|
100
|
-
close: () => void;
|
|
101
|
-
navigate: (direction: "up" | "down") => void;
|
|
102
|
-
execute: (index: number) => void;
|
|
103
|
-
shouldTrigger: (key: string, cursorPos: number, content: string) => boolean;
|
|
104
|
-
};
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slash Commands Composable
|
|
3
|
-
* Manages the slash command menu and execution
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} SlashCommand
|
|
8
|
-
* @property {string} id
|
|
9
|
-
* @property {string} label
|
|
10
|
-
* @property {string} insert
|
|
11
|
-
* @property {number} [cursorOffset]
|
|
12
|
-
* @property {boolean} [isSnippet]
|
|
13
|
-
* @property {boolean} [isAction]
|
|
14
|
-
* @property {(() => void)} [action]
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {Object} SlashMenuState
|
|
19
|
-
* @property {boolean} open
|
|
20
|
-
* @property {string} query
|
|
21
|
-
* @property {{x: number, y: number}} position
|
|
22
|
-
* @property {number} selectedIndex
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {Object} SlashCommandsOptions
|
|
27
|
-
* @property {() => HTMLTextAreaElement|null} [getTextareaRef] - Function to get textarea reference
|
|
28
|
-
* @property {() => string} [getContent] - Function to get content
|
|
29
|
-
* @property {(content: string) => void} [setContent] - Function to set content
|
|
30
|
-
* @property {() => Array<{id: string, name: string, content: string}>} [getSnippets] - Function to get user snippets
|
|
31
|
-
* @property {() => void} [onOpenSnippetsModal] - Callback to open snippets modal
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {Object} SlashCommandsManager
|
|
36
|
-
* @property {SlashMenuState} menu
|
|
37
|
-
* @property {boolean} isOpen
|
|
38
|
-
* @property {() => SlashCommand[]} getAllCommands
|
|
39
|
-
* @property {() => SlashCommand[]} getFilteredCommands
|
|
40
|
-
* @property {() => void} open
|
|
41
|
-
* @property {() => void} close
|
|
42
|
-
* @property {(direction: 'up' | 'down') => void} navigate
|
|
43
|
-
* @property {(index: number) => void} execute
|
|
44
|
-
* @property {(key: string, cursorPos: number, content: string) => boolean} shouldTrigger
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
// Base slash commands definition
|
|
48
|
-
/** @type {SlashCommand[]} */
|
|
49
|
-
export const baseSlashCommands = [
|
|
50
|
-
{ id: "heading1", label: "Heading 1", insert: "# " },
|
|
51
|
-
{ id: "heading2", label: "Heading 2", insert: "## " },
|
|
52
|
-
{ id: "heading3", label: "Heading 3", insert: "### " },
|
|
53
|
-
{ id: "code", label: "Code Block", insert: "```\n\n```", cursorOffset: 4 },
|
|
54
|
-
{ id: "quote", label: "Quote", insert: "> " },
|
|
55
|
-
{ id: "list", label: "Bullet List", insert: "- " },
|
|
56
|
-
{ id: "numbered", label: "Numbered List", insert: "1. " },
|
|
57
|
-
{ id: "link", label: "Link", insert: "[](url)", cursorOffset: 1 },
|
|
58
|
-
{ id: "image", label: "Image", insert: "", cursorOffset: 2 },
|
|
59
|
-
{ id: "divider", label: "Divider", insert: "\n---\n" },
|
|
60
|
-
{
|
|
61
|
-
id: "anchor",
|
|
62
|
-
label: "Custom Anchor",
|
|
63
|
-
insert: "<!-- anchor:name -->\n",
|
|
64
|
-
cursorOffset: 14,
|
|
65
|
-
},
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Creates a slash commands manager with Svelte 5 runes
|
|
70
|
-
* @param {SlashCommandsOptions} options - Configuration options
|
|
71
|
-
* @returns {SlashCommandsManager} Slash commands state and controls
|
|
72
|
-
*/
|
|
73
|
-
export function useSlashCommands(options = {}) {
|
|
74
|
-
const {
|
|
75
|
-
getTextareaRef,
|
|
76
|
-
getContent,
|
|
77
|
-
setContent,
|
|
78
|
-
getSnippets,
|
|
79
|
-
onOpenSnippetsModal,
|
|
80
|
-
} = options;
|
|
81
|
-
|
|
82
|
-
let menu = $state({
|
|
83
|
-
open: false,
|
|
84
|
-
query: "",
|
|
85
|
-
position: { x: 0, y: 0 },
|
|
86
|
-
selectedIndex: 0,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Build full command list including snippets
|
|
90
|
-
/** @returns {SlashCommand[]} */
|
|
91
|
-
function getAllCommands() {
|
|
92
|
-
const snippets = getSnippets ? getSnippets() : [];
|
|
93
|
-
/** @type {SlashCommand[]} */
|
|
94
|
-
const snippetCommands = snippets.map((s) => ({
|
|
95
|
-
id: s.id,
|
|
96
|
-
label: `> ${s.name}`,
|
|
97
|
-
insert: s.content,
|
|
98
|
-
isSnippet: true,
|
|
99
|
-
}));
|
|
100
|
-
|
|
101
|
-
/** @type {SlashCommand} */
|
|
102
|
-
const newSnippetCommand = {
|
|
103
|
-
id: "newSnippet",
|
|
104
|
-
label: "Create New Snippet...",
|
|
105
|
-
insert: "",
|
|
106
|
-
isAction: true,
|
|
107
|
-
action: onOpenSnippetsModal,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
return [...baseSlashCommands, newSnippetCommand, ...snippetCommands];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Get filtered commands based on query
|
|
114
|
-
function getFilteredCommands() {
|
|
115
|
-
const allCommands = getAllCommands();
|
|
116
|
-
return allCommands.filter((cmd) =>
|
|
117
|
-
cmd.label.toLowerCase().includes(menu.query.toLowerCase())
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function open() {
|
|
122
|
-
menu.open = true;
|
|
123
|
-
menu.query = "";
|
|
124
|
-
menu.selectedIndex = 0;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function close() {
|
|
128
|
-
menu.open = false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** @param {'up' | 'down'} direction */
|
|
132
|
-
function navigate(direction) {
|
|
133
|
-
const filtered = getFilteredCommands();
|
|
134
|
-
const count = filtered.length;
|
|
135
|
-
if (count === 0) return;
|
|
136
|
-
|
|
137
|
-
if (direction === "down") {
|
|
138
|
-
menu.selectedIndex = (menu.selectedIndex + 1) % count;
|
|
139
|
-
} else if (direction === "up") {
|
|
140
|
-
menu.selectedIndex = (menu.selectedIndex - 1 + count) % count;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** @param {number} index */
|
|
145
|
-
function execute(index) {
|
|
146
|
-
const filtered = getFilteredCommands();
|
|
147
|
-
const cmd = filtered[index];
|
|
148
|
-
if (!cmd) return;
|
|
149
|
-
|
|
150
|
-
const textareaRef = getTextareaRef ? getTextareaRef() : null;
|
|
151
|
-
const content = getContent ? getContent() : '';
|
|
152
|
-
|
|
153
|
-
if (!textareaRef || !setContent) return;
|
|
154
|
-
|
|
155
|
-
// Handle action commands (like "Create New Snippet...")
|
|
156
|
-
if (cmd.isAction && cmd.action) {
|
|
157
|
-
// Remove the slash that triggered the menu
|
|
158
|
-
const pos = textareaRef.selectionStart;
|
|
159
|
-
const textBefore = content.substring(0, pos);
|
|
160
|
-
const lastSlashIndex = textBefore.lastIndexOf("/");
|
|
161
|
-
if (lastSlashIndex >= 0) {
|
|
162
|
-
setContent(content.substring(0, lastSlashIndex) + content.substring(pos));
|
|
163
|
-
}
|
|
164
|
-
menu.open = false;
|
|
165
|
-
cmd.action();
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Remove the slash that triggered the menu and insert command
|
|
170
|
-
const pos = textareaRef.selectionStart;
|
|
171
|
-
const textBefore = content.substring(0, pos);
|
|
172
|
-
const lastSlashIndex = textBefore.lastIndexOf("/");
|
|
173
|
-
|
|
174
|
-
if (lastSlashIndex >= 0) {
|
|
175
|
-
setContent(
|
|
176
|
-
content.substring(0, lastSlashIndex) + cmd.insert + content.substring(pos)
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
setTimeout(() => {
|
|
180
|
-
const newPos = lastSlashIndex + (cmd.cursorOffset || cmd.insert.length);
|
|
181
|
-
textareaRef.selectionStart = textareaRef.selectionEnd = newPos;
|
|
182
|
-
textareaRef.focus();
|
|
183
|
-
}, 0);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
menu.open = false;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* @param {string} key
|
|
191
|
-
* @param {number} cursorPos
|
|
192
|
-
* @param {string} content
|
|
193
|
-
*/
|
|
194
|
-
function shouldTrigger(key, cursorPos, content) {
|
|
195
|
-
if (key !== "/" || menu.open) return false;
|
|
196
|
-
// Only trigger at start of line or after whitespace
|
|
197
|
-
return cursorPos === 0 || /\s$/.test(content.substring(0, cursorPos));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
get menu() {
|
|
202
|
-
return menu;
|
|
203
|
-
},
|
|
204
|
-
get isOpen() {
|
|
205
|
-
return menu.open;
|
|
206
|
-
},
|
|
207
|
-
getAllCommands,
|
|
208
|
-
getFilteredCommands,
|
|
209
|
-
open,
|
|
210
|
-
close,
|
|
211
|
-
navigate,
|
|
212
|
-
execute,
|
|
213
|
-
shouldTrigger,
|
|
214
|
-
};
|
|
215
|
-
}
|