jsmdcui 0.3.0 → 0.5.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/CHANGELOG.md +137 -0
- package/README.md +56 -2
- package/package.json +1 -1
- package/runmd.mjs +71 -7
- package/runtime/help/help.md +56 -2
- package/runtime/syntax/markdown.yaml +12 -0
- package/src/buffer/backup.js +8 -3
- package/src/config/config.js +3 -0
- package/src/config/defaults.js +2 -2
- package/src/cui/rpc.mjs +137 -2
- package/src/index.js +301 -47
- package/src/plugins/js-bridge.js +168 -0
- package/testapp.md +23 -0
- package/tests/backup.test.js +25 -0
- package/tests/cat-markdown.test.js +79 -0
- package/tests/config-settings.test.js +52 -0
- package/tests/demo.test.js +24 -0
package/src/cui/rpc.mjs
CHANGED
|
@@ -9,6 +9,138 @@ let apilist = new Map()
|
|
|
9
9
|
|
|
10
10
|
export const jss = JSON.stringify
|
|
11
11
|
|
|
12
|
+
function parseDollarIdentity(input, { selector = false } = {})
|
|
13
|
+
{
|
|
14
|
+
const text = String(input ?? "").trim();
|
|
15
|
+
const match = text.match(/^([A-Za-z_][\w:-]*)?(?:#([A-Za-z_][\w:-]*))?((?:\.[A-Za-z_][\w:-]*)*)$/);
|
|
16
|
+
if (!match || (!match[1] && !match[2] && !match[3])) return null;
|
|
17
|
+
if (!selector && !match[1]) return null;
|
|
18
|
+
return {
|
|
19
|
+
tag: match[1] || null,
|
|
20
|
+
id: match[2] || null,
|
|
21
|
+
classes: match[3] ? match[3].slice(1).split(".") : [],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function matchesDollarIdentity(identity, selector)
|
|
26
|
+
{
|
|
27
|
+
if (selector.tag && identity.tag !== selector.tag) return false;
|
|
28
|
+
if (selector.id && identity.id !== selector.id) return false;
|
|
29
|
+
return selector.classes.every(name => identity.classes.includes(name));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function findMarkdownCodeElement(documentObject, selector)
|
|
33
|
+
{
|
|
34
|
+
for (const code of documentObject?.querySelectorAll?.("pre > code") ?? []) {
|
|
35
|
+
for (const className of code.classList ?? []) {
|
|
36
|
+
if (!className.startsWith("language-")) continue;
|
|
37
|
+
const identity = parseDollarIdentity(className.slice("language-".length));
|
|
38
|
+
if (identity && matchesDollarIdentity(identity, selector)) return code;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function findWebDollarElement(documentObject, selectorText, selector)
|
|
45
|
+
{
|
|
46
|
+
try {
|
|
47
|
+
const direct = documentObject?.querySelector?.(String(selectorText));
|
|
48
|
+
if (direct) return direct;
|
|
49
|
+
} catch {}
|
|
50
|
+
|
|
51
|
+
for (const element of documentObject?.querySelectorAll?.("[data-mdcui-tag]") ?? []) {
|
|
52
|
+
const identity = {
|
|
53
|
+
tag: element.getAttribute?.("data-mdcui-tag") || null,
|
|
54
|
+
id: element.id || null,
|
|
55
|
+
classes: [...(element.classList ?? [])],
|
|
56
|
+
};
|
|
57
|
+
if (matchesDollarIdentity(identity, selector)) return element;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return findMarkdownCodeElement(documentObject, selector);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function webDollarValue(element)
|
|
64
|
+
{
|
|
65
|
+
if (element && "value" in element) return String(element.value ?? "");
|
|
66
|
+
return String(element?.textContent ?? "").replace(/\n$/, "");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function resizeWebTextarea(element)
|
|
70
|
+
{
|
|
71
|
+
if (!element || String(element.tagName ?? "").toLowerCase() !== "textarea") return;
|
|
72
|
+
try {
|
|
73
|
+
element.style.height = "auto";
|
|
74
|
+
element.style.height = `${element.scrollHeight}px`;
|
|
75
|
+
const lineHeight = Number.parseFloat(
|
|
76
|
+
element.ownerDocument?.defaultView?.getComputedStyle?.(element)?.lineHeight,
|
|
77
|
+
);
|
|
78
|
+
if (Number.isFinite(lineHeight) && lineHeight > 0)
|
|
79
|
+
element.rows = Math.max(1, Math.ceil(element.scrollHeight / lineHeight));
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function installWebTextareaResize(target)
|
|
84
|
+
{
|
|
85
|
+
const documentObject = target?.document;
|
|
86
|
+
if (!documentObject || documentObject.__mdcuiTextareaResizeInstalled) return;
|
|
87
|
+
documentObject.__mdcuiTextareaResizeInstalled = true;
|
|
88
|
+
const resizeAll = () => {
|
|
89
|
+
for (const element of documentObject.querySelectorAll?.("textarea[data-mdcui-tag]") ?? [])
|
|
90
|
+
resizeWebTextarea(element);
|
|
91
|
+
};
|
|
92
|
+
documentObject.addEventListener?.("input", event => {
|
|
93
|
+
if (event.target?.matches?.("textarea[data-mdcui-tag]"))
|
|
94
|
+
resizeWebTextarea(event.target);
|
|
95
|
+
});
|
|
96
|
+
target.addEventListener?.("resize", resizeAll);
|
|
97
|
+
if (documentObject.readyState === "loading")
|
|
98
|
+
documentObject.addEventListener?.("DOMContentLoaded", resizeAll, { once: true });
|
|
99
|
+
else
|
|
100
|
+
queueMicrotask(resizeAll);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createWebDollar(documentObject = globalThis.document)
|
|
104
|
+
{
|
|
105
|
+
return function $(selectorText) {
|
|
106
|
+
const selector = parseDollarIdentity(selectorText, { selector: true });
|
|
107
|
+
const selection = {
|
|
108
|
+
val(...args) {
|
|
109
|
+
try {
|
|
110
|
+
if (!selector) return args.length > 0 ? selection : "";
|
|
111
|
+
const element = findWebDollarElement(documentObject, selectorText, selector);
|
|
112
|
+
if (!element) return args.length > 0 ? selection : "";
|
|
113
|
+
if (args.length > 0) {
|
|
114
|
+
const value = String(args[0] ?? "");
|
|
115
|
+
if ("value" in element) {
|
|
116
|
+
element.value = value;
|
|
117
|
+
resizeWebTextarea(element);
|
|
118
|
+
}
|
|
119
|
+
else element.textContent = value;
|
|
120
|
+
return selection;
|
|
121
|
+
}
|
|
122
|
+
return webDollarValue(element);
|
|
123
|
+
} catch {
|
|
124
|
+
return args.length > 0 ? selection : "";
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
return selection;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function installWebDollar(target = globalThis)
|
|
133
|
+
{
|
|
134
|
+
if (!target?.document) return target?.$;
|
|
135
|
+
const $ = createWebDollar(target.document);
|
|
136
|
+
target.$ = $;
|
|
137
|
+
installWebTextareaResize(target);
|
|
138
|
+
return $;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (typeof globalThis.document !== "undefined")
|
|
142
|
+
installWebDollar(globalThis);
|
|
143
|
+
|
|
12
144
|
|
|
13
145
|
export async function evalBack(backmod, qjson)
|
|
14
146
|
{
|
|
@@ -99,8 +231,11 @@ try{
|
|
|
99
231
|
|
|
100
232
|
text = text.replace(/^javascript:/, "");
|
|
101
233
|
|
|
102
|
-
const
|
|
103
|
-
|
|
234
|
+
const entries = Object.entries(mod).filter(([name]) => name !== "$");
|
|
235
|
+
if (typeof globalThis.$ === "function")
|
|
236
|
+
entries.push(["$", globalThis.$]);
|
|
237
|
+
const names = entries.map(([name]) => name);
|
|
238
|
+
const values = entries.map(([, value]) => safeFrontValue(value));
|
|
104
239
|
|
|
105
240
|
try {
|
|
106
241
|
return await new AsyncFunction(...names, `return await (${text})`)(...values);
|