@bendyline/docblocks-react 2.7.1 → 2.7.2
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 +1 -1
- package/THIRD_PARTY_NOTICES.txt +98 -48
- package/dist/{ExportDialog-IMBVGRYG.js → ExportDialog-JRVZCG3J.js} +2 -2
- package/dist/{ExportToolbarControls-MUM6HM3K.js → ExportToolbarControls-2FEIHNMW.js} +15 -7
- package/dist/KeyboardShortcutsDialog-NOD4P25G.js +145 -0
- package/dist/{PresentationMaker-W2JIBHRN.js → PresentationMaker-ITNP5SDO.js} +1 -1
- package/dist/{Settings-DuXk6baH.d.ts → Settings-RDYvDDI0.d.ts} +13 -11
- package/dist/{SpeechToolbarControl-OSQS7NVY.js → SpeechToolbarControl-CLU7RB2G.js} +1 -1
- package/dist/{TextFromNarration-XYRAB6O2.js → TextFromNarration-NITJQ4IB.js} +1 -1
- package/dist/{WorkspaceSearchPanel-BKZJRPMY.js → WorkspaceSearchPanel-J6BKEZQJ.js} +1 -1
- package/dist/{chunk-UG4G7PJ4.js → chunk-5O5YTOXG.js} +1 -1
- package/dist/{chunk-VO2JYYQZ.js → chunk-6JJOGV6M.js} +5 -13
- package/dist/{chunk-X437UAF7.js → chunk-6MSHXQNB.js} +7 -2
- package/dist/{chunk-W55E6TE2.js → chunk-BLUEZG7T.js} +27 -22
- package/dist/chunk-DICE7VBJ.js +77 -0
- package/dist/{chunk-BCQZTWE5.js → chunk-HRWEGPTY.js} +18 -4
- package/dist/chunk-K7XJDJUJ.js +126 -0
- package/dist/{chunk-EPOLZNFD.js → chunk-SSB43524.js} +164 -69
- package/dist/{chunk-G4MFB3IO.js → chunk-VG434562.js} +2 -70
- package/dist/{chunk-BD54Z5XP.js → chunk-Y5OE3HJC.js} +4 -3
- package/dist/editor.js +6 -4
- package/dist/export/index.d.ts +17 -6
- package/dist/export/index.js +4 -4
- package/dist/index.d.ts +24 -6
- package/dist/index.js +395 -196
- package/dist/run-export-CS3HRNAZ.js +13 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js +1 -1
- package/package.json +9 -8
- package/src/styles/docblocks.css +465 -266
- package/dist/chunk-EA4BO6E4.js +0 -73
- package/dist/run-export-47X5FTYZ.js +0 -11
|
@@ -12,19 +12,11 @@ function useSpeechStatus(speech) {
|
|
|
12
12
|
if (live) setStatus((current) => current ?? initial);
|
|
13
13
|
},
|
|
14
14
|
() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
reason: "Speech status is unavailable. Try opening Settings."
|
|
21
|
-
},
|
|
22
|
-
tts: {
|
|
23
|
-
state: "unavailable",
|
|
24
|
-
reason: "Speech status is unavailable. Try opening Settings."
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
);
|
|
15
|
+
const readiness = {
|
|
16
|
+
state: "unavailable",
|
|
17
|
+
reason: "DocBlocks could not check speech on this computer."
|
|
18
|
+
};
|
|
19
|
+
if (live) setStatus((current) => current ?? { stt: readiness, tts: readiness });
|
|
28
20
|
}
|
|
29
21
|
);
|
|
30
22
|
return () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/Export/export-options.ts
|
|
2
|
+
import { getThemeSummaries } from "@bendyline/squisq/schemas";
|
|
2
3
|
var FORMAT_LABELS = {
|
|
3
4
|
docx: "Word Document (.docx)",
|
|
4
5
|
pdf: "PDF (.pdf)",
|
|
@@ -28,8 +29,11 @@ var DEFAULT_OPTIONS = {
|
|
|
28
29
|
};
|
|
29
30
|
function resolveExportDialogInitial(lastOptions, docThemeId, workspaceThemeId) {
|
|
30
31
|
const base = lastOptions ?? DEFAULT_OPTIONS;
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
return { ...base, themeId: docThemeId || workspaceThemeId || DEFAULT_OPTIONS.themeId };
|
|
33
|
+
}
|
|
34
|
+
function exportThemeChoices(documentThemes = []) {
|
|
35
|
+
const ids = new Set(documentThemes.map((theme) => theme.id));
|
|
36
|
+
return [...documentThemes, ...getThemeSummaries().filter((theme) => !ids.has(theme.id))];
|
|
33
37
|
}
|
|
34
38
|
function loadLastExportOptions() {
|
|
35
39
|
try {
|
|
@@ -53,6 +57,7 @@ export {
|
|
|
53
57
|
FORMAT_EXTENSIONS,
|
|
54
58
|
DEFAULT_OPTIONS,
|
|
55
59
|
resolveExportDialogInitial,
|
|
60
|
+
exportThemeChoices,
|
|
56
61
|
loadLastExportOptions,
|
|
57
62
|
saveExportOptions
|
|
58
63
|
};
|
|
@@ -307,6 +307,12 @@ function formatDownloadSize(bytes) {
|
|
|
307
307
|
if (gibibytes >= 0.95) return `${gibibytes.toFixed(gibibytes >= 10 ? 0 : 1)} GB`;
|
|
308
308
|
return `${Math.max(1, Math.round(bytes / 1024 ** 2))} MB`;
|
|
309
309
|
}
|
|
310
|
+
function downloadOptionLabel(model) {
|
|
311
|
+
const size = formatDownloadSize(model.downloadBytes);
|
|
312
|
+
const rating = model.recommended ? " \xB7 Recommended" : model.score === null ? "" : ` \xB7 ${model.score}% match`;
|
|
313
|
+
const slow = model.fit?.tier === "tight" ? ` \xB7 ${model.fit.label}` : "";
|
|
314
|
+
return `${model.label}${size ? ` \u2014 ${size}` : ""}${rating}${slow}`;
|
|
315
|
+
}
|
|
310
316
|
function AiSettingsControls({ ai }) {
|
|
311
317
|
const [status, setStatus] = useState2(null);
|
|
312
318
|
const [preferences, setPreferences] = useState2(null);
|
|
@@ -551,19 +557,14 @@ function AiSettingsControls({ ai }) {
|
|
|
551
557
|
value: downloadModelId,
|
|
552
558
|
disabled: installingModel,
|
|
553
559
|
onChange: (event) => setDownloadModelId(event.currentTarget.value),
|
|
554
|
-
children: availableModels.map((model) => {
|
|
555
|
-
const size = formatDownloadSize(model.downloadBytes);
|
|
556
|
-
return /* @__PURE__ */ jsxs2("option", { value: model.id, children: [
|
|
557
|
-
model.label,
|
|
558
|
-
size ? ` \u2014 ${size}` : ""
|
|
559
|
-
] }, model.id);
|
|
560
|
-
})
|
|
560
|
+
children: availableModels.map((model) => /* @__PURE__ */ jsx2("option", { value: model.id, children: downloadOptionLabel(model) }, model.id))
|
|
561
561
|
}
|
|
562
562
|
)
|
|
563
563
|
] }),
|
|
564
|
-
selectedDownload && /* @__PURE__ */ jsxs2("p", { className: "db-settings-hint", children: [
|
|
565
|
-
"Downloads and runs on this device",
|
|
566
|
-
formatDownloadSize(selectedDownload.downloadBytes) ? ` \xB7 ${formatDownloadSize(selectedDownload.downloadBytes)}` : ""
|
|
564
|
+
selectedDownload && /* @__PURE__ */ jsxs2("p", { className: "db-settings-hint", title: selectedDownload.fit?.detail, children: [
|
|
565
|
+
selectedDownload.recommended ? "Recommended for this device" : "Downloads and runs on this device",
|
|
566
|
+
formatDownloadSize(selectedDownload.downloadBytes) ? ` \xB7 ${formatDownloadSize(selectedDownload.downloadBytes)}` : "",
|
|
567
|
+
selectedDownload.fit ? ` \xB7 ${selectedDownload.fit.label}` : ""
|
|
567
568
|
] }),
|
|
568
569
|
/* @__PURE__ */ jsx2(
|
|
569
570
|
"button",
|
|
@@ -999,7 +1000,7 @@ import {
|
|
|
999
1000
|
|
|
1000
1001
|
// src/preferences/theme.ts
|
|
1001
1002
|
var ACCENT_COLORS = [
|
|
1002
|
-
"
|
|
1003
|
+
"rust",
|
|
1003
1004
|
"green",
|
|
1004
1005
|
"blue",
|
|
1005
1006
|
"purple",
|
|
@@ -1011,7 +1012,7 @@ var DB_CHROME_COLORS = {
|
|
|
1011
1012
|
light: "#f3eede",
|
|
1012
1013
|
dark: "#262219"
|
|
1013
1014
|
};
|
|
1014
|
-
var DEFAULT_INTERFACE_FONT = "
|
|
1015
|
+
var DEFAULT_INTERFACE_FONT = "fixed";
|
|
1015
1016
|
var THEME_STORAGE_KEY = "docblocks:themePreference";
|
|
1016
1017
|
var ACCENT_STORAGE_KEY = "docblocks:accentColor";
|
|
1017
1018
|
var INTERFACE_FONT_STORAGE_KEY = "docblocks:interfaceFont";
|
|
@@ -1029,13 +1030,17 @@ function saveThemePreference(value) {
|
|
|
1029
1030
|
} catch {
|
|
1030
1031
|
}
|
|
1031
1032
|
}
|
|
1033
|
+
var DEFAULT_ACCENT_COLOR = "rust";
|
|
1034
|
+
var LEGACY_ACCENT_COLORS = /* @__PURE__ */ new Map([["brown", "rust"]]);
|
|
1032
1035
|
function loadAccentColor() {
|
|
1033
1036
|
try {
|
|
1034
1037
|
const raw = localStorage.getItem(ACCENT_STORAGE_KEY);
|
|
1038
|
+
const renamed = raw === null ? void 0 : LEGACY_ACCENT_COLORS.get(raw);
|
|
1039
|
+
if (renamed) return renamed;
|
|
1035
1040
|
if (ACCENT_COLORS.some((color) => color === raw)) return raw;
|
|
1036
1041
|
} catch {
|
|
1037
1042
|
}
|
|
1038
|
-
return
|
|
1043
|
+
return DEFAULT_ACCENT_COLOR;
|
|
1039
1044
|
}
|
|
1040
1045
|
function saveAccentColor(value) {
|
|
1041
1046
|
try {
|
|
@@ -1189,7 +1194,7 @@ function isRecord(value) {
|
|
|
1189
1194
|
// src/Settings/Settings.tsx
|
|
1190
1195
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1191
1196
|
var ACCENT_LABELS = {
|
|
1192
|
-
|
|
1197
|
+
rust: "Rust",
|
|
1193
1198
|
green: "Green",
|
|
1194
1199
|
blue: "Blue",
|
|
1195
1200
|
purple: "Purple",
|
|
@@ -1261,12 +1266,12 @@ function InterfaceFontSettings({
|
|
|
1261
1266
|
{
|
|
1262
1267
|
type: "radio",
|
|
1263
1268
|
name,
|
|
1264
|
-
value: "
|
|
1265
|
-
checked: value !== "
|
|
1266
|
-
onChange: () => onChange("
|
|
1269
|
+
value: "fixed",
|
|
1270
|
+
checked: value !== "system",
|
|
1271
|
+
onChange: () => onChange("fixed")
|
|
1267
1272
|
}
|
|
1268
1273
|
),
|
|
1269
|
-
"
|
|
1274
|
+
"DocBlocks"
|
|
1270
1275
|
] }),
|
|
1271
1276
|
/* @__PURE__ */ jsxs4("label", { className: "db-settings-radio db-settings-radio--inline", children: [
|
|
1272
1277
|
/* @__PURE__ */ jsx4(
|
|
@@ -1274,12 +1279,12 @@ function InterfaceFontSettings({
|
|
|
1274
1279
|
{
|
|
1275
1280
|
type: "radio",
|
|
1276
1281
|
name,
|
|
1277
|
-
value: "
|
|
1278
|
-
checked: value === "
|
|
1279
|
-
onChange: () => onChange("
|
|
1282
|
+
value: "system",
|
|
1283
|
+
checked: value === "system",
|
|
1284
|
+
onChange: () => onChange("system")
|
|
1280
1285
|
}
|
|
1281
1286
|
),
|
|
1282
|
-
"
|
|
1287
|
+
"System"
|
|
1283
1288
|
] })
|
|
1284
1289
|
] })
|
|
1285
1290
|
] });
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/icons.tsx
|
|
2
|
+
import {
|
|
3
|
+
ArrowDownAZ,
|
|
4
|
+
Clock,
|
|
5
|
+
Download,
|
|
6
|
+
Ellipsis,
|
|
7
|
+
FilePlus,
|
|
8
|
+
Folder,
|
|
9
|
+
FolderOpen,
|
|
10
|
+
FolderPlus,
|
|
11
|
+
FolderTree,
|
|
12
|
+
PanelLeft,
|
|
13
|
+
Pin,
|
|
14
|
+
Search,
|
|
15
|
+
Settings2
|
|
16
|
+
} from "lucide-react";
|
|
17
|
+
import { jsx } from "react/jsx-runtime";
|
|
18
|
+
var ICON_SIZE = "1.15em";
|
|
19
|
+
var STROKE_WIDTH = 1.75;
|
|
20
|
+
function ShellIcon({ icon: Icon, size = ICON_SIZE }) {
|
|
21
|
+
return /* @__PURE__ */ jsx(Icon, { size, strokeWidth: STROKE_WIDTH, "aria-hidden": "true", focusable: "false" });
|
|
22
|
+
}
|
|
23
|
+
function NewFileIcon() {
|
|
24
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: FilePlus });
|
|
25
|
+
}
|
|
26
|
+
function NewFolderIcon() {
|
|
27
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: FolderPlus });
|
|
28
|
+
}
|
|
29
|
+
function OpenFolderIcon() {
|
|
30
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: FolderOpen });
|
|
31
|
+
}
|
|
32
|
+
function SortByNameIcon() {
|
|
33
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: ArrowDownAZ });
|
|
34
|
+
}
|
|
35
|
+
function SortByLastModifiedIcon() {
|
|
36
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Clock });
|
|
37
|
+
}
|
|
38
|
+
function FolderIcon() {
|
|
39
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Folder });
|
|
40
|
+
}
|
|
41
|
+
function MoreIcon() {
|
|
42
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Ellipsis });
|
|
43
|
+
}
|
|
44
|
+
function PinIcon() {
|
|
45
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Pin });
|
|
46
|
+
}
|
|
47
|
+
function WorkspaceIcon() {
|
|
48
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Settings2 });
|
|
49
|
+
}
|
|
50
|
+
function SplitViewIcon() {
|
|
51
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: PanelLeft, size: 16 });
|
|
52
|
+
}
|
|
53
|
+
function SearchIcon() {
|
|
54
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Search });
|
|
55
|
+
}
|
|
56
|
+
function FilesIcon() {
|
|
57
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: FolderTree });
|
|
58
|
+
}
|
|
59
|
+
function BackupIcon() {
|
|
60
|
+
return /* @__PURE__ */ jsx(ShellIcon, { icon: Download });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
NewFileIcon,
|
|
65
|
+
NewFolderIcon,
|
|
66
|
+
OpenFolderIcon,
|
|
67
|
+
SortByNameIcon,
|
|
68
|
+
SortByLastModifiedIcon,
|
|
69
|
+
FolderIcon,
|
|
70
|
+
MoreIcon,
|
|
71
|
+
PinIcon,
|
|
72
|
+
WorkspaceIcon,
|
|
73
|
+
SplitViewIcon,
|
|
74
|
+
SearchIcon,
|
|
75
|
+
FilesIcon,
|
|
76
|
+
BackupIcon
|
|
77
|
+
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FORMAT_EXTENSIONS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6MSHXQNB.js";
|
|
4
4
|
|
|
5
5
|
// src/Export/run-export.ts
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
parseFrontmatter,
|
|
8
|
+
parseMarkdown,
|
|
9
|
+
readFrontmatterThemeId,
|
|
10
|
+
splitFrontmatterBlock
|
|
11
|
+
} from "@bendyline/squisq/markdown";
|
|
7
12
|
async function pictureDiagrams(doc, overrides, kinds) {
|
|
8
13
|
try {
|
|
9
14
|
const [{ rasterizeDiagrams }, render] = await Promise.all([
|
|
@@ -40,13 +45,21 @@ async function saveExportBlob(blob, filename, saveBlob) {
|
|
|
40
45
|
}
|
|
41
46
|
downloadBlob(blob, filename);
|
|
42
47
|
}
|
|
48
|
+
function exporterThemeId(markdown, selectedThemeId) {
|
|
49
|
+
if (selectedThemeId !== "standard") return selectedThemeId;
|
|
50
|
+
const block = splitFrontmatterBlock(markdown).frontmatter;
|
|
51
|
+
const firstBreak = block ? block.indexOf("\n") : -1;
|
|
52
|
+
if (!block || firstBreak < 0) return void 0;
|
|
53
|
+
const yaml = block.slice(firstBreak + 1).replace(/\r?\n---(?:\r?\n)?$/, "");
|
|
54
|
+
return readFrontmatterThemeId(parseFrontmatter(yaml) ?? void 0) ? selectedThemeId : void 0;
|
|
55
|
+
}
|
|
43
56
|
function buildFilename(selectedFile, format) {
|
|
44
57
|
const base = selectedFile ? selectedFile.replace(/^\//, "").replace(/\.[^.]+$/, "") : "document";
|
|
45
58
|
return base + FORMAT_EXTENSIONS[format];
|
|
46
59
|
}
|
|
47
60
|
async function runExport(markdown, selectedFile, options, mediaContainer, saveBlob, converterOverrides = {}) {
|
|
48
61
|
const filename = buildFilename(selectedFile, options.format);
|
|
49
|
-
const themeId =
|
|
62
|
+
const themeId = exporterThemeId(markdown, options.themeId);
|
|
50
63
|
if (options.format === "md") {
|
|
51
64
|
await saveExportBlob(new Blob([markdown], { type: MIME_TYPES.md }), filename, saveBlob);
|
|
52
65
|
return;
|
|
@@ -129,7 +142,7 @@ async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, se
|
|
|
129
142
|
const baseName = htmlFilename.replace(/\.html$/, "");
|
|
130
143
|
const zipName = `${baseName}.zip`;
|
|
131
144
|
const singleHtmlFilename = options.entryAsIndex ? "index.html" : htmlFilename;
|
|
132
|
-
const themeId =
|
|
145
|
+
const themeId = exporterThemeId(markdown, options.themeId);
|
|
133
146
|
if (options.includeLinkedDocs && mediaContainer && selectedFile) {
|
|
134
147
|
const entryPath = basenameForBundle(selectedFile);
|
|
135
148
|
const readDocument = (path) => path === entryPath ? Promise.resolve(markdown) : readDocumentFromContainer(mediaContainer, path);
|
|
@@ -320,5 +333,6 @@ async function resolveDocImages(doc, container, collectImagePaths) {
|
|
|
320
333
|
}
|
|
321
334
|
|
|
322
335
|
export {
|
|
336
|
+
exporterThemeId,
|
|
323
337
|
runExport
|
|
324
338
|
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/DocBlocksShell/keyboard-shortcuts.ts
|
|
2
|
+
var SHELL_SHORTCUTS = Object.freeze([
|
|
3
|
+
// Must work mid-sentence; that is the entire point of a save shortcut.
|
|
4
|
+
{ id: "save", key: "s", accel: true, label: "Ctrl+S", whileTyping: true, action: "Save now" },
|
|
5
|
+
// Find is mostly invoked from inside the document being searched.
|
|
6
|
+
{
|
|
7
|
+
id: "find",
|
|
8
|
+
key: "f",
|
|
9
|
+
accel: true,
|
|
10
|
+
label: "Ctrl+F",
|
|
11
|
+
whileTyping: true,
|
|
12
|
+
action: "Find in document"
|
|
13
|
+
},
|
|
14
|
+
// Jumping elsewhere is as likely mid-sentence as from the tree.
|
|
15
|
+
{
|
|
16
|
+
id: "quick-open",
|
|
17
|
+
key: "p",
|
|
18
|
+
accel: true,
|
|
19
|
+
label: "Ctrl+P",
|
|
20
|
+
whileTyping: true,
|
|
21
|
+
action: "Go to document"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "workspace-search",
|
|
25
|
+
key: "f",
|
|
26
|
+
accel: true,
|
|
27
|
+
shift: true,
|
|
28
|
+
label: "Ctrl+Shift+F",
|
|
29
|
+
whileTyping: true,
|
|
30
|
+
action: "Search the workspace"
|
|
31
|
+
},
|
|
32
|
+
// Deliberately NOT Cmd+B — Squisq owns that for Bold. Cmd+\ is the VS Code
|
|
33
|
+
// convention for toggling the side bar.
|
|
34
|
+
{
|
|
35
|
+
id: "toggle-sidebar",
|
|
36
|
+
key: "\\",
|
|
37
|
+
accel: true,
|
|
38
|
+
label: "Ctrl+\\",
|
|
39
|
+
whileTyping: true,
|
|
40
|
+
action: "Show or hide the file pane"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "focus-files",
|
|
44
|
+
key: "e",
|
|
45
|
+
accel: true,
|
|
46
|
+
shift: true,
|
|
47
|
+
label: "Ctrl+Shift+E",
|
|
48
|
+
action: "Move focus to the files"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "new-document",
|
|
52
|
+
key: "n",
|
|
53
|
+
accel: true,
|
|
54
|
+
label: "Ctrl+N",
|
|
55
|
+
action: "New document",
|
|
56
|
+
browserReserved: true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "new-folder",
|
|
60
|
+
key: "n",
|
|
61
|
+
accel: true,
|
|
62
|
+
shift: true,
|
|
63
|
+
label: "Ctrl+Shift+N",
|
|
64
|
+
action: "New folder",
|
|
65
|
+
browserReserved: true
|
|
66
|
+
},
|
|
67
|
+
// F1 is the help key and works mid-sentence; `?` is the web convention but
|
|
68
|
+
// is ordinary text while typing. Both open the same sheet.
|
|
69
|
+
{
|
|
70
|
+
id: "shortcuts",
|
|
71
|
+
key: "F1",
|
|
72
|
+
accel: false,
|
|
73
|
+
label: "F1",
|
|
74
|
+
whileTyping: true,
|
|
75
|
+
action: "Show keyboard shortcuts"
|
|
76
|
+
},
|
|
77
|
+
{ id: "shortcuts", key: "?", accel: false, shift: true, label: "?" },
|
|
78
|
+
// Ctrl+, (settings) is deliberately absent: AppMenu owns that dialog's state
|
|
79
|
+
// internally and exposes no way to open it. Binding the key without first
|
|
80
|
+
// giving AppMenu a controlled `settingsOpen` prop would ship a dead shortcut.
|
|
81
|
+
{ id: "close-overlay", key: "Escape", accel: false, label: "Esc", whileTyping: true }
|
|
82
|
+
]);
|
|
83
|
+
function isTypingTarget(target) {
|
|
84
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
85
|
+
if (target.isContentEditable) return true;
|
|
86
|
+
const tag = target.tagName;
|
|
87
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
88
|
+
return target.closest(".monaco-editor") !== null;
|
|
89
|
+
}
|
|
90
|
+
function matchShortcut(event, _layoutMode) {
|
|
91
|
+
return SHELL_SHORTCUTS.find((binding) => bindingMatches(binding, event))?.id ?? null;
|
|
92
|
+
}
|
|
93
|
+
function bindingMatches(binding, event) {
|
|
94
|
+
if (event.altKey) return false;
|
|
95
|
+
const accel = event.ctrlKey || event.metaKey;
|
|
96
|
+
const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
|
|
97
|
+
if (binding.accel !== accel) return false;
|
|
98
|
+
if ((binding.shift ?? false) !== event.shiftKey) return false;
|
|
99
|
+
if (binding.key !== key) return false;
|
|
100
|
+
return binding.whileTyping === true || !isTypingTarget(event.target);
|
|
101
|
+
}
|
|
102
|
+
function isFindShortcut(event) {
|
|
103
|
+
const binding = SHELL_SHORTCUTS.find((candidate) => candidate.id === "find");
|
|
104
|
+
return binding !== void 0 && bindingMatches(binding, event);
|
|
105
|
+
}
|
|
106
|
+
function findShortcutLabel() {
|
|
107
|
+
return shortcutLabel("find", isApplePlatform());
|
|
108
|
+
}
|
|
109
|
+
function isApplePlatform() {
|
|
110
|
+
if (typeof navigator === "undefined") return false;
|
|
111
|
+
return /Mac|iPhone|iPad|iPod/u.test(navigator.platform || navigator.userAgent);
|
|
112
|
+
}
|
|
113
|
+
function shortcutLabel(id, isApple) {
|
|
114
|
+
const binding = SHELL_SHORTCUTS.find((candidate) => candidate.id === id);
|
|
115
|
+
if (!binding) return "";
|
|
116
|
+
return isApple ? binding.label.replace("Ctrl+", "\u2318") : binding.label;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
SHELL_SHORTCUTS,
|
|
121
|
+
matchShortcut,
|
|
122
|
+
isFindShortcut,
|
|
123
|
+
findShortcutLabel,
|
|
124
|
+
isApplePlatform,
|
|
125
|
+
shortcutLabel
|
|
126
|
+
};
|