cursedbelt 5.79.3 → 5.80.0
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/looks.css +43 -5
- package/dist/react/activity-bar/ActivityBar.d.ts +18 -1
- package/dist/react/activity-bar/ActivityBar.d.ts.map +1 -1
- package/dist/react/activity-bar/ActivityBar.js +34 -5
- package/dist/react/activity-bar/ActivityBar.js.map +1 -1
- package/dist/react/activity-bar/activityBar.css +20 -0
- package/dist/react/code-editor/CodeEditor.d.ts +5 -1
- package/dist/react/code-editor/CodeEditor.d.ts.map +1 -1
- package/dist/react/code-editor/CodeEditor.js +13 -6
- package/dist/react/code-editor/CodeEditor.js.map +1 -1
- package/dist/react/code-editor/keys.d.ts +8 -0
- package/dist/react/code-editor/keys.d.ts.map +1 -0
- package/dist/react/code-editor/keys.js +16 -0
- package/dist/react/code-editor/keys.js.map +1 -0
- package/dist/react/code-editor/languages.d.ts.map +1 -1
- package/dist/react/code-editor/languages.js +4 -2
- package/dist/react/code-editor/languages.js.map +1 -1
- package/dist/react/code-editor/lineAbove.d.ts +5 -4
- package/dist/react/code-editor/lineAbove.d.ts.map +1 -1
- package/dist/react/code-editor/lineAbove.js +6 -10
- package/dist/react/code-editor/lineAbove.js.map +1 -1
- package/dist/react/code-editor/markdownRules.d.ts +8 -0
- package/dist/react/code-editor/markdownRules.d.ts.map +1 -0
- package/dist/react/code-editor/markdownRules.js +64 -0
- package/dist/react/code-editor/markdownRules.js.map +1 -0
- package/dist/react/code-editor/occurrences.d.ts +8 -0
- package/dist/react/code-editor/occurrences.d.ts.map +1 -0
- package/dist/react/code-editor/occurrences.js +63 -0
- package/dist/react/code-editor/occurrences.js.map +1 -0
- package/dist/react/code-editor/plainInput.d.ts +4 -0
- package/dist/react/code-editor/plainInput.d.ts.map +1 -0
- package/dist/react/code-editor/plainInput.js +36 -0
- package/dist/react/code-editor/plainInput.js.map +1 -0
- package/dist/react/code-editor/theme.d.ts.map +1 -1
- package/dist/react/code-editor/theme.js +5 -0
- package/dist/react/code-editor/theme.js.map +1 -1
- package/dist/react/line-keys/lineKeys.d.ts +12 -8
- package/dist/react/line-keys/lineKeys.d.ts.map +1 -1
- package/dist/react/line-keys/lineKeys.js +14 -10
- package/dist/react/line-keys/lineKeys.js.map +1 -1
- package/package.json +1 -1
- package/src/looks.css +43 -5
- package/src/looks.spec.ts +42 -3
- package/src/react/activity-bar/ActivityBar.spec.tsx +46 -0
- package/src/react/activity-bar/ActivityBar.tsx +67 -4
- package/src/react/activity-bar/activityBar.css +20 -0
- package/src/react/code-editor/CodeEditor.tsx +24 -7
- package/src/react/code-editor/keys.spec.ts +61 -0
- package/src/react/code-editor/keys.ts +17 -0
- package/src/react/code-editor/languages.ts +4 -2
- package/src/react/code-editor/lineAbove.spec.ts +1 -16
- package/src/react/code-editor/lineAbove.ts +6 -10
- package/src/react/code-editor/markdownRules.spec.ts +26 -0
- package/src/react/code-editor/markdownRules.ts +73 -0
- package/src/react/code-editor/occurrences.ts +69 -0
- package/src/react/code-editor/plainInput.spec.ts +23 -0
- package/src/react/code-editor/plainInput.ts +35 -0
- package/src/react/code-editor/theme.ts +5 -0
- package/src/react/line-keys/lineKeys.spec.tsx +19 -7
- package/src/react/line-keys/lineKeys.ts +14 -10
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { syntaxTree } from '@codemirror/language';
|
|
2
|
+
import { Decoration, EditorView, ViewPlugin } from '@codemirror/view';
|
|
3
|
+
/**
|
|
4
|
+
* A markdown rule (`---`, `***`, `___`) drawn as a rule while you write, the dashes coming back
|
|
5
|
+
* when the caret is on its line — Obsidian's live preview of the same thing. The owner, 2026-10-14
|
|
6
|
+
* (forge task 3184): "it should show as a solid line in editing and in view". Only a rule the
|
|
7
|
+
* grammar agrees is one: `---` under a line of text is that line's heading underline, and the
|
|
8
|
+
* preview reads it the same way.
|
|
9
|
+
*/
|
|
10
|
+
const rule = Decoration.line({ class: 'cb-md-rule' });
|
|
11
|
+
/** The line starts of every rule in the visible ranges, less any line a selection touches. */
|
|
12
|
+
export function ruleLines(state, ranges) {
|
|
13
|
+
const starts = [];
|
|
14
|
+
const tree = syntaxTree(state);
|
|
15
|
+
for (const { from, to } of ranges) {
|
|
16
|
+
tree.iterate({
|
|
17
|
+
from,
|
|
18
|
+
to,
|
|
19
|
+
enter: (node) => {
|
|
20
|
+
if (node.name !== 'HorizontalRule')
|
|
21
|
+
return;
|
|
22
|
+
const line = state.doc.lineAt(node.from);
|
|
23
|
+
const touched = state.selection.ranges.some((r) => r.from <= line.to && r.to >= line.from);
|
|
24
|
+
if (!touched && starts.at(-1) !== line.from)
|
|
25
|
+
starts.push(line.from);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return starts;
|
|
30
|
+
}
|
|
31
|
+
function build(view) {
|
|
32
|
+
const marks = ruleLines(view.state, view.visibleRanges).map((at) => rule.range(at));
|
|
33
|
+
return Decoration.set(marks);
|
|
34
|
+
}
|
|
35
|
+
const rulePlugin = ViewPlugin.fromClass(class {
|
|
36
|
+
decorations;
|
|
37
|
+
constructor(view) {
|
|
38
|
+
this.decorations = build(view);
|
|
39
|
+
}
|
|
40
|
+
update(update) {
|
|
41
|
+
if (update.docChanged ||
|
|
42
|
+
update.selectionSet ||
|
|
43
|
+
update.viewportChanged ||
|
|
44
|
+
syntaxTree(update.startState) !== syntaxTree(update.state))
|
|
45
|
+
this.decorations = build(update.view);
|
|
46
|
+
}
|
|
47
|
+
}, { decorations: (plugin) => plugin.decorations });
|
|
48
|
+
const ruleTheme = EditorView.baseTheme({
|
|
49
|
+
// The rule is the line's background, centred, the width of the text column; the dashes stay
|
|
50
|
+
// in the document (and under the caret's click), just not painted.
|
|
51
|
+
'.cb-md-rule': {
|
|
52
|
+
backgroundImage: 'linear-gradient(var(--border), var(--border))',
|
|
53
|
+
backgroundSize: '100% 1px',
|
|
54
|
+
backgroundPosition: 'center',
|
|
55
|
+
backgroundRepeat: 'no-repeat',
|
|
56
|
+
backgroundOrigin: 'content-box',
|
|
57
|
+
backgroundClip: 'content-box',
|
|
58
|
+
},
|
|
59
|
+
'.cb-md-rule, .cb-md-rule *': {
|
|
60
|
+
color: 'transparent !important',
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
export const markdownRules = [rulePlugin, ruleTheme];
|
|
64
|
+
//# sourceMappingURL=markdownRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownRules.js","sourceRoot":"","sources":["../../../src/react/code-editor/markdownRules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAsB,UAAU,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAE3G;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;AAEtD,8FAA8F;AAC9F,MAAM,UAAU,SAAS,CAAC,KAAkB,EAAE,MAA+C;IAC3F,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,MAAM,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC;YACX,IAAI;YACJ,EAAE;YACF,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;gBACd,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB;oBAAE,OAAO;gBAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACzC,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC3F,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI;oBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtE,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,KAAK,CAAC,IAAgB;IAC7B,MAAM,KAAK,GAAwB,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACzG,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CACrC;IACE,WAAW,CAAgB;IAC3B,YAAY,IAAgB;QAC1B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,CAAC,MAAkB;QACvB,IACE,MAAM,CAAC,UAAU;YACjB,MAAM,CAAC,YAAY;YACnB,MAAM,CAAC,eAAe;YACtB,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC;YAE1D,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,EACD,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAChD,CAAC;AAEF,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;IACrC,4FAA4F;IAC5F,mEAAmE;IACnE,aAAa,EAAE;QACb,eAAe,EAAE,+CAA+C;QAChE,cAAc,EAAE,UAAU;QAC1B,kBAAkB,EAAE,QAAQ;QAC5B,gBAAgB,EAAE,WAAW;QAC7B,gBAAgB,EAAE,aAAa;QAC/B,cAAc,EAAE,aAAa;KAC9B;IACD,4BAA4B,EAAE;QAC5B,KAAK,EAAE,wBAAwB;KAChC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAc,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type StateCommand } from '@codemirror/state';
|
|
2
|
+
import type { KeyBinding } from '@codemirror/view';
|
|
3
|
+
/** ⌘D — select the word under the caret, then add the next match of it, wrapping at the end. */
|
|
4
|
+
export declare const selectNextOccurrence: StateCommand;
|
|
5
|
+
/** ⇧⌘L — every match of the selection (or the word under the caret) selected at once. */
|
|
6
|
+
export declare const selectAllOccurrences: StateCommand;
|
|
7
|
+
export declare const occurrenceBindings: readonly KeyBinding[];
|
|
8
|
+
//# sourceMappingURL=occurrences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"occurrences.d.ts","sourceRoot":"","sources":["../../../src/react/code-editor/occurrences.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0D,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA2BnD,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,EAAE,YAsBlC,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,oBAAoB,EAAE,YASlC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,SAAS,UAAU,EAGnD,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { EditorSelection } from '@codemirror/state';
|
|
2
|
+
/**
|
|
3
|
+
* VS Code's ⌘D and ⇧⌘L — the two multi-cursor keys `defaultKeymap` leaves to `@codemirror/search`,
|
|
4
|
+
* which `CodeEditor` does not load (its find panel would fight the app's own find). Exact text,
|
|
5
|
+
* case-sensitive; a caret with nothing selected first takes the word it is in, as in VS Code.
|
|
6
|
+
*/
|
|
7
|
+
/** The text the commands look for: the main selection, or the word under an empty caret. */
|
|
8
|
+
function queryOf(state) {
|
|
9
|
+
const main = state.selection.main;
|
|
10
|
+
const range = main.empty ? state.wordAt(main.head) : main;
|
|
11
|
+
if (!range || range.empty)
|
|
12
|
+
return null;
|
|
13
|
+
return { query: state.sliceDoc(range.from, range.to), range: EditorSelection.range(range.from, range.to) };
|
|
14
|
+
}
|
|
15
|
+
/** Where `query` starts in `text`, matches never overlapping. */
|
|
16
|
+
function matchesOf(text, query) {
|
|
17
|
+
const starts = [];
|
|
18
|
+
for (let at = text.indexOf(query); at !== -1; at = text.indexOf(query, at + query.length))
|
|
19
|
+
starts.push(at);
|
|
20
|
+
return starts;
|
|
21
|
+
}
|
|
22
|
+
function isSelected(state, from, to) {
|
|
23
|
+
return state.selection.ranges.some((r) => r.from === from && r.to === to);
|
|
24
|
+
}
|
|
25
|
+
/** ⌘D — select the word under the caret, then add the next match of it, wrapping at the end. */
|
|
26
|
+
export const selectNextOccurrence = ({ state, dispatch }) => {
|
|
27
|
+
const found = queryOf(state);
|
|
28
|
+
if (!found)
|
|
29
|
+
return false;
|
|
30
|
+
const { query, range } = found;
|
|
31
|
+
if (state.selection.main.empty) {
|
|
32
|
+
dispatch(state.update({ selection: state.selection.replaceRange(range), scrollIntoView: true, userEvent: 'select' }));
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
// Every match, the ones after the selection first, then from the top — the first not yet selected.
|
|
36
|
+
const starts = matchesOf(state.doc.toString(), query);
|
|
37
|
+
const at = [...starts.filter((a) => a >= range.to), ...starts.filter((a) => a < range.to)].find((a) => !isSelected(state, a, a + query.length));
|
|
38
|
+
if (at === undefined)
|
|
39
|
+
return false;
|
|
40
|
+
dispatch(state.update({
|
|
41
|
+
selection: state.selection.addRange(EditorSelection.range(at, at + query.length)),
|
|
42
|
+
scrollIntoView: true,
|
|
43
|
+
userEvent: 'select',
|
|
44
|
+
}));
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
/** ⇧⌘L — every match of the selection (or the word under the caret) selected at once. */
|
|
48
|
+
export const selectAllOccurrences = ({ state, dispatch }) => {
|
|
49
|
+
const found = queryOf(state);
|
|
50
|
+
if (!found)
|
|
51
|
+
return false;
|
|
52
|
+
const { query, range } = found;
|
|
53
|
+
const starts = matchesOf(state.doc.toString(), query);
|
|
54
|
+
const ranges = starts.map((at) => EditorSelection.range(at, at + query.length));
|
|
55
|
+
const main = Math.max(0, starts.indexOf(range.from));
|
|
56
|
+
dispatch(state.update({ selection: EditorSelection.create(ranges, main), userEvent: 'select' }));
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
export const occurrenceBindings = [
|
|
60
|
+
{ key: 'Mod-d', run: selectNextOccurrence, preventDefault: true },
|
|
61
|
+
{ key: 'Shift-Mod-l', run: selectAllOccurrences, preventDefault: true },
|
|
62
|
+
];
|
|
63
|
+
//# sourceMappingURL=occurrences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"occurrences.js","sourceRoot":"","sources":["../../../src/react/code-editor/occurrences.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The text the keys meant, or null when `text` replacing `replaced` is not a substitution. */
|
|
2
|
+
export declare function unsmarten(replaced: string, text: string): string | null;
|
|
3
|
+
export declare const plainInput: import("@codemirror/state").Extension;
|
|
4
|
+
//# sourceMappingURL=plainInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plainInput.d.ts","sourceRoot":"","sources":["../../../src/react/code-editor/plainInput.ts"],"names":[],"mappings":"AAgBA,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvE;AAED,eAAO,MAAM,UAAU,uCAUrB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { EditorView } from '@codemirror/view';
|
|
2
|
+
/**
|
|
3
|
+
* What the OS's "smart punctuation" types into a source editor, put back to what the keys said.
|
|
4
|
+
*
|
|
5
|
+
* iOS (and a Mac with Edit ▸ Substitutions on) rewrites `--` to `—` and `"` to `“` as you type —
|
|
6
|
+
* in markdown that eats a rule's dashes and a code span's quotes (the owner, 2026-10-14, forge
|
|
7
|
+
* task 3184: "when typing three dashes … it shifts and deletes some dashes … please fix all other
|
|
8
|
+
* syntax errors that may be similar"). `CodeEditor` already asks the browser for no autocorrect;
|
|
9
|
+
* this is the half that holds where the OS does not listen. VS Code substitutes nothing, either.
|
|
10
|
+
*
|
|
11
|
+
* Only a substitution is undone: a dash replacing the `-` (or `--`) the keys just typed, or a
|
|
12
|
+
* curly quote arriving where a straight one was pressed. A `—` pasted in is a paste, never seen here.
|
|
13
|
+
*/
|
|
14
|
+
const QUOTES = { '“': '"', '”': '"', '‘': "'", '’': "'" };
|
|
15
|
+
/** The text the keys meant, or null when `text` replacing `replaced` is not a substitution. */
|
|
16
|
+
export function unsmarten(replaced, text) {
|
|
17
|
+
if ((text === '—' || text === '–') && /^-{1,2}$/.test(replaced))
|
|
18
|
+
return text === '—' && replaced === '-' ? '--' : `${replaced}-`;
|
|
19
|
+
const quote = QUOTES[text];
|
|
20
|
+
if (quote !== undefined && (replaced === '' || replaced === quote))
|
|
21
|
+
return quote;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
export const plainInput = EditorView.inputHandler.of((view, from, to, text) => {
|
|
25
|
+
const meant = unsmarten(view.state.sliceDoc(from, to), text);
|
|
26
|
+
if (meant === null)
|
|
27
|
+
return false;
|
|
28
|
+
view.dispatch({
|
|
29
|
+
changes: { from, to, insert: meant },
|
|
30
|
+
selection: { anchor: from + meant.length },
|
|
31
|
+
scrollIntoView: true,
|
|
32
|
+
userEvent: 'input.type',
|
|
33
|
+
});
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=plainInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plainInput.js","sourceRoot":"","sources":["../../../src/react/code-editor/plainInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,GAA2B,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAElF,+FAA+F;AAC/F,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAY;IACtD,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC;IACjI,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACjF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;IAC5E,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,CAAC,QAAQ,CAAC;QACZ,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;QACpC,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE;QAC1C,cAAc,EAAE,IAAI;QACpB,SAAS,EAAE,YAAY;KACxB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/react/code-editor/theme.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/react/code-editor/theme.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa,uCA6IxB,CAAC;AAiEH,eAAO,MAAM,oBAAoB,uCAAuC,CAAC"}
|
|
@@ -20,6 +20,11 @@ export const cbEditorTheme = EditorView.theme({
|
|
|
20
20
|
'.cm-content': {
|
|
21
21
|
caretColor: 'var(--foreground)',
|
|
22
22
|
padding: '0.5rem 0',
|
|
23
|
+
// Every character drawn as itself — VS Code's `editor.fontLigatures: false`. A ligature font
|
|
24
|
+
// (JetBrains Mono, desk's) joins `--` and `---` into one glyph that redraws as each dash
|
|
25
|
+
// lands, and turns `!=`, `=>` and `<=` into symbols that are not what was typed (forge task 3184).
|
|
26
|
+
fontVariantLigatures: 'none',
|
|
27
|
+
fontFeatureSettings: '"calt" 0, "liga" 0',
|
|
23
28
|
},
|
|
24
29
|
// CodeMirror's own recipe for a minimum height: give it to the content AND the gutter, so the
|
|
25
30
|
// line-number rail runs the editor's full height instead of stopping under the last line
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../../src/react/code-editor/theme.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8FAA8F;AAC9F,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;IAC5C,GAAG,EAAE;QACH,eAAe,EAAE,iBAAiB;QAClC,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,UAAU;QACpB,UAAU,EAAE,kEAAkE;QAC9E,MAAM,EAAE,MAAM;QACd,YAAY,EAAE,SAAS;KACxB;IACD,cAAc,EAAE;QACd,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,MAAM;KAClB;IACD,aAAa,EAAE;QACb,UAAU,EAAE,mBAAmB;QAC/B,OAAO,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../../src/react/code-editor/theme.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* ⌘↵ (Ctrl↵) a new line BELOW the caret's line, from anywhere on it
|
|
5
5
|
* ⇧⌘↵ (Ctrl⇧↵) a new line ABOVE it
|
|
6
6
|
* ⇧⌘K (Ctrl⇧K) delete the line (every line the selection touches)
|
|
7
|
-
*
|
|
8
|
-
* ⌥↑ ⌥↓ (Alt↑ Alt↓) the same — VS Code's own binding for it
|
|
7
|
+
* ⌥↑ ⌥↓ (Alt↑ Alt↓) move the line (every line the selection touches) up or down one
|
|
9
8
|
*
|
|
10
9
|
* The owner asked for these by name (2026-10-05, desk's context components first, "in apps with
|
|
11
|
-
* multi line text boxes"
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* multi line text boxes"). ⌘↑ / ⌘↓ is NOT a line key: it is VS Code's — and the Mac's — jump to the
|
|
11
|
+
* top or bottom, the line staying where it is (the owner, 2026-10-14, forge task 3182: "holding
|
|
12
|
+
* command and pushing up should jump to the top of the file … make all commands and keystrokes
|
|
13
|
+
* work like it does in VS code"). It moved the line from 2026-10-05 to then; the browser has it
|
|
14
|
+
* back. They are one document listener rather than an `onKeyDown` on each
|
|
15
15
|
* `<textarea>` because desk alone draws a dozen of them by hand: a prop is a thing the next box
|
|
16
16
|
* forgets, an install at boot is not.
|
|
17
17
|
*
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*
|
|
29
29
|
* CodeMirror boxes (cb's `CodeEditor`) are `contenteditable`, not `<textarea>`, and are left to
|
|
30
30
|
* CodeMirror's own keymap: `Mod-Enter` and `Shift-Mod-k` are already in its `defaultKeymap`, and
|
|
31
|
-
* `CodeEditor` adds the line above
|
|
31
|
+
* `CodeEditor` adds the line above (⌥↑ / ⌥↓ is `defaultKeymap`'s `moveLineUp`/`Down`).
|
|
32
32
|
*/
|
|
33
33
|
export type LineCommand = 'below' | 'above' | 'delete' | 'up' | 'down';
|
|
34
34
|
/**
|
|
@@ -50,7 +50,11 @@ export interface LineEdit {
|
|
|
50
50
|
export declare function lineEdit(value: string, start: number, end: number, command: 'up' | 'down'): LineEdit | null;
|
|
51
51
|
export declare function lineEdit(value: string, start: number, end: number, command: 'below' | 'above' | 'delete'): LineEdit;
|
|
52
52
|
export declare function lineEdit(value: string, start: number, end: number, command: LineCommand): LineEdit | null;
|
|
53
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Which command a key event asks for, or null. ⌘ on a Mac, Ctrl elsewhere — either is taken —
|
|
55
|
+
* except on an arrow, which moves a line on ⌥ alone: ⌘↑ / ⌘↓ stays the browser's jump to the top
|
|
56
|
+
* or bottom, as in VS Code.
|
|
57
|
+
*/
|
|
54
58
|
export declare function lineCommandOf(event: Pick<KeyboardEvent, 'key' | 'metaKey' | 'ctrlKey' | 'shiftKey' | 'altKey'>): LineCommand | null;
|
|
55
59
|
/** Writes `edit` into `field` the way typing would: undoable, and seen by React as a change. */
|
|
56
60
|
export declare function applyLineEdit(field: HTMLTextAreaElement, edit: LineEdit): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lineKeys.d.ts","sourceRoot":"","sources":["../../../src/react/line-keys/lineKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvE;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAiBD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;AAC7G,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACrH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC;AA+C3G
|
|
1
|
+
{"version":3,"file":"lineKeys.d.ts","sourceRoot":"","sources":["../../../src/react/line-keys/lineKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvE;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAiBD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;AAC7G,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACrH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC;AA+C3G;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC,GAAG,WAAW,GAAG,IAAI,CAQnI;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAgB9E;AAmBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,QAAmB,GAAG,MAAM,IAAI,CAQpE"}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* ⌘↵ (Ctrl↵) a new line BELOW the caret's line, from anywhere on it
|
|
5
5
|
* ⇧⌘↵ (Ctrl⇧↵) a new line ABOVE it
|
|
6
6
|
* ⇧⌘K (Ctrl⇧K) delete the line (every line the selection touches)
|
|
7
|
-
*
|
|
8
|
-
* ⌥↑ ⌥↓ (Alt↑ Alt↓) the same — VS Code's own binding for it
|
|
7
|
+
* ⌥↑ ⌥↓ (Alt↑ Alt↓) move the line (every line the selection touches) up or down one
|
|
9
8
|
*
|
|
10
9
|
* The owner asked for these by name (2026-10-05, desk's context components first, "in apps with
|
|
11
|
-
* multi line text boxes"
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* multi line text boxes"). ⌘↑ / ⌘↓ is NOT a line key: it is VS Code's — and the Mac's — jump to the
|
|
11
|
+
* top or bottom, the line staying where it is (the owner, 2026-10-14, forge task 3182: "holding
|
|
12
|
+
* command and pushing up should jump to the top of the file … make all commands and keystrokes
|
|
13
|
+
* work like it does in VS code"). It moved the line from 2026-10-05 to then; the browser has it
|
|
14
|
+
* back. They are one document listener rather than an `onKeyDown` on each
|
|
15
15
|
* `<textarea>` because desk alone draws a dozen of them by hand: a prop is a thing the next box
|
|
16
16
|
* forgets, an install at boot is not.
|
|
17
17
|
*
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*
|
|
29
29
|
* CodeMirror boxes (cb's `CodeEditor`) are `contenteditable`, not `<textarea>`, and are left to
|
|
30
30
|
* CodeMirror's own keymap: `Mod-Enter` and `Shift-Mod-k` are already in its `defaultKeymap`, and
|
|
31
|
-
* `CodeEditor` adds the line above
|
|
31
|
+
* `CodeEditor` adds the line above (⌥↑ / ⌥↓ is `defaultKeymap`'s `moveLineUp`/`Down`).
|
|
32
32
|
*/
|
|
33
33
|
function lineStartOf(value, at) {
|
|
34
34
|
// `lastIndexOf` clamps a negative start to 0, so at 0 it would find a leading '\n' and answer 1.
|
|
@@ -89,12 +89,16 @@ function moveLines(value, start, end, from, to, command) {
|
|
|
89
89
|
const shift = next.length + 1;
|
|
90
90
|
return { from, to: nextEnd, text: `${next}\n${block}`, caret: start + shift, end: end + shift };
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Which command a key event asks for, or null. ⌘ on a Mac, Ctrl elsewhere — either is taken —
|
|
94
|
+
* except on an arrow, which moves a line on ⌥ alone: ⌘↑ / ⌘↓ stays the browser's jump to the top
|
|
95
|
+
* or bottom, as in VS Code.
|
|
96
|
+
*/
|
|
93
97
|
export function lineCommandOf(event) {
|
|
94
98
|
const arrow = event.key === 'ArrowUp' ? 'up' : event.key === 'ArrowDown' ? 'down' : null;
|
|
95
99
|
// ⇧ with an arrow extends a selection, in every one of these chords; that stays the browser's.
|
|
96
100
|
if (arrow)
|
|
97
|
-
return event.
|
|
101
|
+
return event.altKey && !event.shiftKey && !event.metaKey && !event.ctrlKey ? arrow : null;
|
|
98
102
|
if (!(event.metaKey || event.ctrlKey) || event.altKey)
|
|
99
103
|
return null;
|
|
100
104
|
if (event.key === 'Enter')
|
|
@@ -136,7 +140,7 @@ function onKeyDown(event) {
|
|
|
136
140
|
return;
|
|
137
141
|
event.preventDefault();
|
|
138
142
|
// A move past the first or last line does nothing — and still swallows the key, as VS Code
|
|
139
|
-
// does, rather than letting
|
|
143
|
+
// does, rather than letting ⌥↑ jump the caret to the start of the paragraph.
|
|
140
144
|
const edit = lineEdit(field.value, field.selectionStart, field.selectionEnd, command);
|
|
141
145
|
if (edit)
|
|
142
146
|
applyLineEdit(field, edit);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lineKeys.js","sourceRoot":"","sources":["../../../src/react/line-keys/lineKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAgBH,SAAS,WAAW,CAAC,KAAa,EAAE,EAAU;IAC5C,iGAAiG;IACjG,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,SAAS,CAAC,KAAa,EAAE,EAAU;IAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACpC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,yFAAyF;AACzF,SAAS,QAAQ,CAAC,KAAa,EAAE,SAAiB;IAChD,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3D,CAAC;AAUD,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW,EAAE,OAAoB;IACtF,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACvC,8FAA8F;IAC9F,qEAAqE;IACrE,MAAM,IAAI,GAAG,GAAG,GAAG,KAAK,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACpE,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACnG,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;IAC9E,CAAC;IACD,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;IAC9E,CAAC;IACD,yFAAyF;IACzF,MAAM,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1D,gFAAgF;IAChF,MAAM,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW,EAAE,IAAY,EAAE,EAAU,EAAE,OAAsB;IAC5G,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,KAAK,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,KAAK,EAAE,CAAC;IACpG,CAAC;IACD,IAAI,EAAE,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9B,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,KAAK,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,KAAK,EAAE,CAAC;AAClG,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"lineKeys.js","sourceRoot":"","sources":["../../../src/react/line-keys/lineKeys.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.80.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
|
package/src/looks.css
CHANGED
|
@@ -252,10 +252,22 @@
|
|
|
252
252
|
* node_modules, which a library preview (tools/lib-preview.ts) never installs into, and which a
|
|
253
253
|
* nested install would miss. LATIN ONLY, one variable file for
|
|
254
254
|
* every weight: the fleet's copy is English, and the rest of the alphabet falls through to the
|
|
255
|
-
* system stack behind it.
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
255
|
+
* system stack behind it. cv11 is the single-storey a, ss01 the open digits — the pair Linear sets.
|
|
256
|
+
*
|
|
257
|
+
* `font-display: swap` paints at once in a fallback and swaps when the file lands — and the
|
|
258
|
+
* system face is NOT close enough to swap from: SF sets the same English 14% narrower than Inter,
|
|
259
|
+
* so on a slow load every line re-wrapped when the face came (0.0037 of layout shift at 390 in
|
|
260
|
+
* family's tree, and its TopBar moved 0.0023 under a loaded machine — forge task 3177). So the
|
|
261
|
+
* stack's second entry is 'Inter Fallback': Arial, which every Mac, iPhone and Windows box has,
|
|
262
|
+
* scaled with `size-adjust` and given Inter's own ascent and descent, the technique `next/font` and
|
|
263
|
+
* Fontaine ship. Measured in Chromium against this file with cv11 + ss01 on: within ±0.5% of
|
|
264
|
+
* Inter's width at 400, 500, 600 and 700 (SF: −14%), and the same line box. Two faces, because
|
|
265
|
+
* Arial has two weights: Regular stands for 100–549 and Bold for 550–900, each sized to the middle
|
|
266
|
+
* of its band, so no weight is synthesised. The overrides are Inter's metrics over the size-adjust
|
|
267
|
+
* (ascent 0.96875 em, descent 0.2412 em, no line gap). Same `unicode-range` as the face, so what
|
|
268
|
+
* Inter does not cover still falls through to the system stack, unscaled. Android has no Arial:
|
|
269
|
+
* the face fails there and the swap is from Roboto, as before. Re-measure if the woff2 is bumped
|
|
270
|
+
* (the family app's font browser test holds the swap's shift to nothing, with the file held back).
|
|
259
271
|
*/
|
|
260
272
|
@font-face {
|
|
261
273
|
font-family: 'Inter Variable';
|
|
@@ -267,8 +279,34 @@
|
|
|
267
279
|
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
268
280
|
}
|
|
269
281
|
|
|
282
|
+
@font-face {
|
|
283
|
+
font-family: 'Inter Fallback';
|
|
284
|
+
font-style: normal;
|
|
285
|
+
font-weight: 100 549;
|
|
286
|
+
src: local('Arial'), local('ArialMT');
|
|
287
|
+
size-adjust: 106.25%;
|
|
288
|
+
ascent-override: 91.18%;
|
|
289
|
+
descent-override: 22.7%;
|
|
290
|
+
line-gap-override: 0%;
|
|
291
|
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
|
292
|
+
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@font-face {
|
|
296
|
+
font-family: 'Inter Fallback';
|
|
297
|
+
font-style: normal;
|
|
298
|
+
font-weight: 550 900;
|
|
299
|
+
src: local('Arial Bold'), local('Arial-BoldMT');
|
|
300
|
+
size-adjust: 101.2%;
|
|
301
|
+
ascent-override: 95.73%;
|
|
302
|
+
descent-override: 23.83%;
|
|
303
|
+
line-gap-override: 0%;
|
|
304
|
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
|
305
|
+
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
306
|
+
}
|
|
307
|
+
|
|
270
308
|
.cb-font-inter {
|
|
271
|
-
--font-sans: 'Inter Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
309
|
+
--font-sans: 'Inter Variable', 'Inter Fallback', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
272
310
|
'Noto Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
|
273
311
|
/* Tailwind's preflight reads these two on <html>; re-run them here, where --font-sans changed. */
|
|
274
312
|
--default-font-family: var(--font-sans);
|
package/src/looks.spec.ts
CHANGED
|
@@ -135,7 +135,14 @@ describe('looks.css', () => {
|
|
|
135
135
|
* package without re-copying the file is red here rather than a stale face in every app.
|
|
136
136
|
*/
|
|
137
137
|
describe('cb-font-inter', () => {
|
|
138
|
-
const
|
|
138
|
+
const allFaces = [...LOOKS_CSS.replace(/\/\*[\s\S]*?\*\//g, '').matchAll(/@font-face\s*\{([^}]*)\}/g)].map((m) => m[1] ?? '');
|
|
139
|
+
const faces = allFaces.filter((face) => face.includes("'Inter Variable'"));
|
|
140
|
+
const fallbacks = allFaces.filter((face) => face.includes("'Inter Fallback'"));
|
|
141
|
+
|
|
142
|
+
/** One descriptor of a face, as written. */
|
|
143
|
+
const descriptor = (face: string, name: string): string | undefined =>
|
|
144
|
+
face.match(new RegExp(`(?:^|;)\\s*${name}:\\s*([^;]+);`))?.[1]?.replace(/\s+/g, ' ').trim();
|
|
145
|
+
const percent = (face: string, name: string): number => Number.parseFloat(descriptor(face, name) ?? 'NaN');
|
|
139
146
|
|
|
140
147
|
/** Every face's `src: url(…)`, as written. */
|
|
141
148
|
const urls = (css: string[]): string[] => css.flatMap((face) => [...face.matchAll(/url\('([^']+)'\)/g)].map((m) => m[1] as string));
|
|
@@ -171,9 +178,41 @@ describe('cb-font-inter', () => {
|
|
|
171
178
|
for (const url of urls(stale)) expect(await Bun.file(onDisk(url)).exists()).toBe(false);
|
|
172
179
|
});
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
/*
|
|
182
|
+
* The swap's fallback (forge task 3177): the system face sets English 14% narrower than Inter,
|
|
183
|
+
* so a late woff2 re-wrapped every line. The numbers were measured in Chromium (looks.css says
|
|
184
|
+
* how); this holds the shape that makes them mean anything. The family app's font browser test
|
|
185
|
+
* is what holds the swap's shift to nothing.
|
|
186
|
+
*/
|
|
187
|
+
test("the swap is from 'Inter Fallback': Arial scaled to Inter, Regular and Bold, no file", () => {
|
|
188
|
+
expect(allFaces).toHaveLength(faces.length + fallbacks.length);
|
|
189
|
+
expect(fallbacks.map((face) => descriptor(face, 'font-weight'))).toEqual(['100 549', '550 900']);
|
|
190
|
+
expect(fallbacks.map((face) => descriptor(face, 'src'))).toEqual([
|
|
191
|
+
"local('Arial'), local('ArialMT')",
|
|
192
|
+
"local('Arial Bold'), local('Arial-BoldMT')",
|
|
193
|
+
]);
|
|
194
|
+
for (const face of fallbacks) {
|
|
195
|
+
expect(face).not.toContain('url(');
|
|
196
|
+
// What Inter does not cover falls through to the system stack, not to a scaled Arial.
|
|
197
|
+
expect(descriptor(face, 'unicode-range')).toBe(descriptor(faces[0] as string, 'unicode-range'));
|
|
198
|
+
// Inter's own line box (ascent 0.96875 em, descent 0.2412 em, no gap), over the scale.
|
|
199
|
+
const scale = percent(face, 'size-adjust') / 100;
|
|
200
|
+
expect(scale).toBeGreaterThan(1);
|
|
201
|
+
expect(percent(face, 'ascent-override')).toBeCloseTo(96.875 / scale, 1);
|
|
202
|
+
expect(percent(face, 'descent-override')).toBeCloseTo(24.12 / scale, 1);
|
|
203
|
+
expect(descriptor(face, 'line-gap-override')).toBe('0%');
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('🔴 the failure path: a fallback whose overrides ignore its size-adjust is caught', () => {
|
|
208
|
+
const unscaled = (fallbacks[0] as string).replace(/ascent-override:\s*[^;]+;/, 'ascent-override: 96.88%;');
|
|
209
|
+
const scale = percent(unscaled, 'size-adjust') / 100;
|
|
210
|
+
expect(percent(unscaled, 'ascent-override')).not.toBeCloseTo(96.875 / scale, 1);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('re-binds the sans stack with Inter first, its fallback second, and the cv11 + ss01 features', () => {
|
|
175
214
|
const decls = declarationsOf(LOOKS_CSS, '.cb-font-inter');
|
|
176
|
-
expect(decls?.get('--font-sans')).toStartWith("'Inter Variable',");
|
|
215
|
+
expect(decls?.get('--font-sans')).toStartWith("'Inter Variable', 'Inter Fallback', -apple-system,");
|
|
177
216
|
expect(decls?.get('--default-font-family')).toBe('var(--font-sans)');
|
|
178
217
|
expect(decls?.get('--default-font-feature-settings')).toBe("'cv11', 'ss01'");
|
|
179
218
|
});
|
|
@@ -230,3 +230,49 @@ describe('the rail shows its words on demand (NAV-2, forge task 2748)', () => {
|
|
|
230
230
|
expect(triggers).toEqual([screen.getByLabelText('Files') as HTMLButtonElement]);
|
|
231
231
|
});
|
|
232
232
|
});
|
|
233
|
+
|
|
234
|
+
describe('the lock (forge task 3183)', () => {
|
|
235
|
+
test('no onLockedChange, no lock', () => {
|
|
236
|
+
render(<ActivityBar top={[item()]} />);
|
|
237
|
+
expect(screen.queryByLabelText('Keep the menu open')).toBeNull();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('pressing it locks the rail open, in the layout; pressing again lets it fold', () => {
|
|
241
|
+
const presses: boolean[] = [];
|
|
242
|
+
const view = render(<ActivityBar top={[item()]} locked={false} onLockedChange={(next) => presses.push(next)} />);
|
|
243
|
+
const rail = screen.getByRole('group');
|
|
244
|
+
const lock = screen.getByLabelText('Keep the menu open');
|
|
245
|
+
expect(lock.getAttribute('aria-pressed')).toBe('false');
|
|
246
|
+
expect(rail.hasAttribute('data-locked')).toBe(false);
|
|
247
|
+
fireEvent.click(lock);
|
|
248
|
+
expect(presses).toEqual([true]);
|
|
249
|
+
|
|
250
|
+
view.rerender(<ActivityBar top={[item()]} locked onLockedChange={(next) => presses.push(next)} />);
|
|
251
|
+
expect(rail.getAttribute('data-locked')).toBe('true');
|
|
252
|
+
expect(rail.getAttribute('data-expanded')).toBe('locked');
|
|
253
|
+
expect(screen.getByLabelText('Keep the menu open').getAttribute('aria-pressed')).toBe('true');
|
|
254
|
+
// Choosing a view does not fold a locked rail.
|
|
255
|
+
fireEvent.click(screen.getByLabelText('Files'));
|
|
256
|
+
expect(rail.getAttribute('data-expanded')).toBe('locked');
|
|
257
|
+
fireEvent.click(screen.getByLabelText('Keep the menu open'));
|
|
258
|
+
expect(presses).toEqual([true, false]);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('a rail that does not expand has no lock, and ignores a saved one', () => {
|
|
262
|
+
render(<ActivityBar top={[item()]} expandable={false} locked onLockedChange={() => {}} />);
|
|
263
|
+
expect(screen.queryByLabelText('Keep the menu open')).toBeNull();
|
|
264
|
+
expect(screen.getByRole('group').hasAttribute('data-locked')).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('on a phone-width screen there is no lock, and a saved one is ignored', () => {
|
|
268
|
+
const real = window.matchMedia;
|
|
269
|
+
window.matchMedia = ((query: string) => ({ ...real.call(window, query), matches: false, addEventListener() {}, removeEventListener() {} })) as typeof window.matchMedia;
|
|
270
|
+
try {
|
|
271
|
+
render(<ActivityBar top={[item()]} locked onLockedChange={() => {}} />);
|
|
272
|
+
expect(screen.queryByLabelText('Keep the menu open')).toBeNull();
|
|
273
|
+
expect(screen.getByRole('group').hasAttribute('data-locked')).toBe(false);
|
|
274
|
+
} finally {
|
|
275
|
+
window.matchMedia = real;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|