@tensorix/metahub 0.2.2 → 0.3.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 +8 -1
- package/dist/cli.js +1309 -275
- package/dist/cli.js.map +21 -17
- package/dist/core/databases.d.ts +5 -1
- package/dist/core/databases.d.ts.map +1 -1
- package/dist/core/documents.d.ts +22 -0
- package/dist/core/documents.d.ts.map +1 -1
- package/dist/core/md/grammar.d.ts +77 -0
- package/dist/core/md/grammar.d.ts.map +1 -0
- package/dist/core/md/heal.d.ts +2 -0
- package/dist/core/md/heal.d.ts.map +1 -0
- package/dist/core/md/inline.d.ts +20 -0
- package/dist/core/md/inline.d.ts.map +1 -0
- package/dist/core/schema-init.d.ts +13 -0
- package/dist/core/schema-init.d.ts.map +1 -1
- package/dist/core/schema.d.ts +1 -1
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/sync/auth.d.ts +3 -1
- package/dist/core/sync/auth.d.ts.map +1 -1
- package/dist/core/sync/share-render.d.ts +3 -1
- package/dist/core/sync/share-render.d.ts.map +1 -1
- package/dist/db-worker.js +51 -41
- package/dist/index.js +1201 -260
- package/dist/index.js.map +16 -12
- package/dist/mh-runtime.js +1 -1
- package/dist/sw.js +1 -1
- package/dist/webui/fmt/manifest.d.ts +17 -0
- package/dist/webui/fmt/manifest.d.ts.map +1 -0
- package/dist/webui/server/assets.d.ts +5 -0
- package/dist/webui/server/assets.d.ts.map +1 -1
- package/dist/webui-fmt-clang.js +1 -0
- package/dist/webui-fmt-clang.wasm +0 -0
- package/dist/webui-fmt-gofmt.js +1 -0
- package/dist/webui-fmt-gofmt.wasm +0 -0
- package/dist/webui-fmt-lua.js +2 -0
- package/dist/webui-fmt-lua.wasm +0 -0
- package/dist/webui-fmt-ruff.js +2 -0
- package/dist/webui-fmt-ruff.wasm +0 -0
- package/dist/webui-fmt-sh.js +8 -0
- package/dist/webui-fmt-taplo.js +2 -0
- package/dist/webui-fmt-taplo.wasm +0 -0
- package/dist/webui-fmt.js +425 -0
- package/dist/webui.js +148 -75
- package/package.json +15 -1
package/dist/index.js
CHANGED
|
@@ -395,7 +395,7 @@ var init_crdt = __esm(() => {
|
|
|
395
395
|
DOMAIN = {
|
|
396
396
|
databases: {
|
|
397
397
|
table: "databases",
|
|
398
|
-
cols: new Set(["name", "icon", "created_hlc", "__deleted"])
|
|
398
|
+
cols: new Set(["name", "icon", "meta", "created_hlc", "__deleted"])
|
|
399
399
|
},
|
|
400
400
|
properties: {
|
|
401
401
|
table: "properties",
|
|
@@ -576,11 +576,17 @@ var init_properties = __esm(() => {
|
|
|
576
576
|
});
|
|
577
577
|
|
|
578
578
|
// src/core/databases.ts
|
|
579
|
+
function rowOut(r) {
|
|
580
|
+
if (!r)
|
|
581
|
+
return null;
|
|
582
|
+
return { ...r, meta: r.meta === null ? null : JSON.parse(r.meta) };
|
|
583
|
+
}
|
|
579
584
|
function getDatabase(db, id) {
|
|
580
|
-
return db.query(`SELECT ${DATABASE_SELECT} FROM databases WHERE id = ? AND __deleted = 0`).get(id);
|
|
585
|
+
return rowOut(db.query(`SELECT ${DATABASE_SELECT} FROM databases WHERE id = ? AND __deleted = 0`).get(id));
|
|
581
586
|
}
|
|
582
587
|
function listDatabases(db) {
|
|
583
|
-
|
|
588
|
+
const rows = db.query(`SELECT ${DATABASE_SELECT} FROM databases WHERE __deleted = 0 ORDER BY created_hlc`).all();
|
|
589
|
+
return rows.map((r) => rowOut(r));
|
|
584
590
|
}
|
|
585
591
|
function liveChildren(db, table, col, parentId) {
|
|
586
592
|
return db.query(`SELECT id FROM ${table} WHERE ${col} = ? AND __deleted = 0`).all(parentId).map((r) => r.id);
|
|
@@ -592,7 +598,7 @@ var init_databases = __esm(() => {
|
|
|
592
598
|
init_errors();
|
|
593
599
|
init_properties();
|
|
594
600
|
init_records();
|
|
595
|
-
DATABASE_COLS = ["id", "name", "icon", "created_hlc"];
|
|
601
|
+
DATABASE_COLS = ["id", "name", "icon", "meta", "created_hlc"];
|
|
596
602
|
DATABASE_SELECT = DATABASE_COLS.join(", ");
|
|
597
603
|
createDatabase = grouped(function createDatabase2(db, opts) {
|
|
598
604
|
const id = newId("db", opts.name);
|
|
@@ -609,6 +615,11 @@ var init_databases = __esm(() => {
|
|
|
609
615
|
emit(db, "databases", id, "name", fields.name);
|
|
610
616
|
if (fields.icon !== undefined)
|
|
611
617
|
emit(db, "databases", id, "icon", fields.icon);
|
|
618
|
+
if (fields.meta !== undefined) {
|
|
619
|
+
if (fields.meta !== null && (typeof fields.meta !== "object" || Array.isArray(fields.meta)))
|
|
620
|
+
throw new MhError("invalid_input", "meta must be a JSON object or null");
|
|
621
|
+
emit(db, "databases", id, "meta", fields.meta);
|
|
622
|
+
}
|
|
612
623
|
return getDatabase(db, id);
|
|
613
624
|
});
|
|
614
625
|
duplicateDatabase = grouped(function duplicateDatabase2(db, id, opts = {}) {
|
|
@@ -619,6 +630,11 @@ var init_databases = __esm(() => {
|
|
|
619
630
|
name: opts.name ?? src.name,
|
|
620
631
|
icon: opts.icon ?? src.icon ?? undefined
|
|
621
632
|
});
|
|
633
|
+
if (src.meta !== null) {
|
|
634
|
+
const { collapsed: _hidden, ...meta } = src.meta;
|
|
635
|
+
if (Object.keys(meta).length)
|
|
636
|
+
emit(db, "databases", dup.id, "meta", meta);
|
|
637
|
+
}
|
|
622
638
|
const propIdMap = new Map;
|
|
623
639
|
for (const p of listProperties(db, id)) {
|
|
624
640
|
const config = p.config?.database === id ? { ...p.config, database: dup.id } : p.config ?? undefined;
|
|
@@ -1259,7 +1275,7 @@ function countOccurrences(haystack, needle) {
|
|
|
1259
1275
|
}
|
|
1260
1276
|
return n;
|
|
1261
1277
|
}
|
|
1262
|
-
var DOCUMENT_COLS, DOCUMENT_SELECT, DOC_SUMMARY_COLS, DOC_SUMMARY_SELECT, createDocument, ORDER_BY = "ORDER BY order_key IS NULL, order_key, created_hlc, id", backfillDocumentOrderKeys, moveDocument, duplicateDocument, updateDocument, editDocument, appendDocument, prependDocument, deleteDocument;
|
|
1278
|
+
var DOCUMENT_COLS, DOCUMENT_SELECT, DOC_SUMMARY_COLS, DOC_SUMMARY_SELECT, createDocument, ORDER_BY = "ORDER BY order_key IS NULL, order_key, created_hlc, id", backfillDocumentOrderKeys, moveDocument, duplicateDocument, updateDocument, editDocument, editDocumentBatch, appendDocument, prependDocument, deleteDocument;
|
|
1263
1279
|
var init_documents = __esm(() => {
|
|
1264
1280
|
init_ids();
|
|
1265
1281
|
init_crdt();
|
|
@@ -1376,19 +1392,52 @@ var init_documents = __esm(() => {
|
|
|
1376
1392
|
|
|
1377
1393
|
`)) {
|
|
1378
1394
|
const affected = blocks.filter((b) => b.text.includes(opts.old));
|
|
1379
|
-
const next = affected.map((b) => opts.replaceAll ? b.text.replaceAll(opts.old, opts.new) : b.text.replace(opts.old, opts.new));
|
|
1395
|
+
const next = affected.map((b) => opts.replaceAll ? b.text.replaceAll(opts.old, () => opts.new) : b.text.replace(opts.old, () => opts.new));
|
|
1380
1396
|
if (next.every((t) => t.length > 0 && !t.includes(`
|
|
1381
1397
|
|
|
1382
1398
|
`))) {
|
|
1383
1399
|
const targets = opts.replaceAll ? affected : affected.slice(0, 1);
|
|
1384
|
-
|
|
1385
|
-
|
|
1400
|
+
let wrote = false;
|
|
1401
|
+
targets.forEach((b, i) => {
|
|
1402
|
+
if (next[i] !== b.text) {
|
|
1403
|
+
emit(db, "doc_blocks", b.id, "text", next[i]);
|
|
1404
|
+
wrote = true;
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
return { id, changed: wrote, replaced: opts.replaceAll ? count : 1, version: documentVersion(db, id) };
|
|
1386
1408
|
}
|
|
1387
1409
|
}
|
|
1388
|
-
const newBody = opts.replaceAll ? body.replaceAll(opts.old, opts.new) : body.replace(opts.old, opts.new);
|
|
1410
|
+
const newBody = opts.replaceAll ? body.replaceAll(opts.old, () => opts.new) : body.replace(opts.old, () => opts.new);
|
|
1389
1411
|
reconcileBody(db, id, newBody);
|
|
1390
1412
|
return { id, changed: newBody !== body, replaced: opts.replaceAll ? count : 1, version: documentVersion(db, id) };
|
|
1391
1413
|
});
|
|
1414
|
+
editDocumentBatch = grouped(function editDocumentBatch2(db, id, opts) {
|
|
1415
|
+
if (!getDocument(db, id))
|
|
1416
|
+
throw new MhError("not_found", `no such document: ${id}`);
|
|
1417
|
+
if (opts.edits.length === 0)
|
|
1418
|
+
throw new MhError("invalid_input", "no edits given");
|
|
1419
|
+
if (opts.ifMatch !== undefined && documentVersion(db, id) !== opts.ifMatch)
|
|
1420
|
+
throw new MhError("stale", `stale: document changed since ${opts.ifMatch}; re-read first`);
|
|
1421
|
+
ensureBlocks(db, id);
|
|
1422
|
+
const orig = serializeDocBlocks(liveBlocks(db, id).map((b) => ({ text: b.text, blankAfter: b.blank_after })));
|
|
1423
|
+
let body = orig;
|
|
1424
|
+
let total = 0;
|
|
1425
|
+
opts.edits.forEach((e, i) => {
|
|
1426
|
+
if (e.old === "")
|
|
1427
|
+
throw new MhError("invalid_input", `edit[${i}]: old must not be empty`);
|
|
1428
|
+
const count = countOccurrences(body, e.old);
|
|
1429
|
+
if (count === 0)
|
|
1430
|
+
throw new MhError("invalid_input", `edit[${i}]: anchor not found: no match for old`);
|
|
1431
|
+
if (count > 1 && !e.replaceAll)
|
|
1432
|
+
throw new MhError("ambiguous", `edit[${i}]: ${count} matches for old; add surrounding context or set replaceAll`);
|
|
1433
|
+
const repl = e.new ?? "";
|
|
1434
|
+
body = e.replaceAll ? body.replaceAll(e.old, () => repl) : body.replace(e.old, () => repl);
|
|
1435
|
+
total += e.replaceAll ? count : 1;
|
|
1436
|
+
});
|
|
1437
|
+
if (body !== orig)
|
|
1438
|
+
reconcileBody(db, id, body);
|
|
1439
|
+
return { id, changed: body !== orig, replaced: total, version: documentVersion(db, id) };
|
|
1440
|
+
});
|
|
1392
1441
|
appendDocument = grouped(function appendDocument2(db, id, body) {
|
|
1393
1442
|
if (!getDocument(db, id))
|
|
1394
1443
|
throw new MhError("not_found", `no such document: ${id}`);
|
|
@@ -18759,38 +18808,382 @@ var init_shares = __esm(() => {
|
|
|
18759
18808
|
SHARE_KINDS = new Set(["doc", "database", "site"]);
|
|
18760
18809
|
});
|
|
18761
18810
|
|
|
18811
|
+
// src/core/md/grammar.ts
|
|
18812
|
+
function safeUrl(url2, opts = {}) {
|
|
18813
|
+
const probe = url2.replace(/[\u0000-\u0020]+/g, "");
|
|
18814
|
+
const m = /^([a-z][a-z0-9+.-]*):/i.exec(probe);
|
|
18815
|
+
if (!m)
|
|
18816
|
+
return url2;
|
|
18817
|
+
const scheme = m[1].toLowerCase();
|
|
18818
|
+
if (scheme === "http" || scheme === "https" || scheme === "mailto" || scheme === "blob")
|
|
18819
|
+
return url2;
|
|
18820
|
+
if (scheme === "data" && opts.allowData)
|
|
18821
|
+
return url2;
|
|
18822
|
+
return "#";
|
|
18823
|
+
}
|
|
18824
|
+
function extOf(url2) {
|
|
18825
|
+
const clean = url2.split(/[?#]/, 1)[0] ?? url2;
|
|
18826
|
+
const base = clean.slice(clean.lastIndexOf("/") + 1);
|
|
18827
|
+
const dot = base.lastIndexOf(".");
|
|
18828
|
+
return dot >= 0 ? base.slice(dot + 1).toLowerCase() : "";
|
|
18829
|
+
}
|
|
18830
|
+
function imageSyntaxKind(ext2) {
|
|
18831
|
+
if (VIDEO_EXTS.has(ext2))
|
|
18832
|
+
return "video";
|
|
18833
|
+
if (AUDIO_EXTS.has(ext2))
|
|
18834
|
+
return "audio";
|
|
18835
|
+
return "image";
|
|
18836
|
+
}
|
|
18837
|
+
function parseMediaUrl(url2) {
|
|
18838
|
+
const hash3 = url2.indexOf("#");
|
|
18839
|
+
const frag = hash3 >= 0 ? url2.slice(hash3) : "";
|
|
18840
|
+
const noFrag = hash3 >= 0 ? url2.slice(0, hash3) : url2;
|
|
18841
|
+
const q = noFrag.indexOf("?");
|
|
18842
|
+
if (q < 0)
|
|
18843
|
+
return { src: url2 };
|
|
18844
|
+
const base = noFrag.slice(0, q);
|
|
18845
|
+
let width;
|
|
18846
|
+
const kept = [];
|
|
18847
|
+
for (const p of noFrag.slice(q + 1).split("&").filter(Boolean)) {
|
|
18848
|
+
const m = p.match(/^w=(\d+)$/);
|
|
18849
|
+
if (m)
|
|
18850
|
+
width = parseInt(m[1], 10);
|
|
18851
|
+
else
|
|
18852
|
+
kept.push(p);
|
|
18853
|
+
}
|
|
18854
|
+
return { src: base + (kept.length ? "?" + kept.join("&") : "") + frag, width };
|
|
18855
|
+
}
|
|
18856
|
+
function matchMediaEmbed(line) {
|
|
18857
|
+
const t = line.trim();
|
|
18858
|
+
let m = t.match(/^!\[([^\]]*)\]\(([^\s)]+)\)$/);
|
|
18859
|
+
if (m) {
|
|
18860
|
+
const { src, width } = parseMediaUrl(m[2]);
|
|
18861
|
+
const kind = imageSyntaxKind(extOf(src));
|
|
18862
|
+
const r = { kind, src, name: m[1] };
|
|
18863
|
+
if (kind === "image" && width)
|
|
18864
|
+
r.width = width;
|
|
18865
|
+
return r;
|
|
18866
|
+
}
|
|
18867
|
+
m = t.match(/^\[([^\]]*)\]\(([^\s)]+)(?:\s+"([^"]*)")?\)$/);
|
|
18868
|
+
if (m && m[2].startsWith("/blob/")) {
|
|
18869
|
+
const r = { kind: "file", src: m[2], name: m[1] };
|
|
18870
|
+
if (m[3] != null && /^\d+$/.test(m[3]))
|
|
18871
|
+
r.size = parseInt(m[3], 10);
|
|
18872
|
+
return r;
|
|
18873
|
+
}
|
|
18874
|
+
return null;
|
|
18875
|
+
}
|
|
18876
|
+
function matchQuoteLine(text) {
|
|
18877
|
+
const m = text.match(RE.quote);
|
|
18878
|
+
return m ? m[1] : null;
|
|
18879
|
+
}
|
|
18880
|
+
function matchListLine(line, minIndent) {
|
|
18881
|
+
const indent = leadingIndent(line);
|
|
18882
|
+
if (indent < minIndent)
|
|
18883
|
+
return null;
|
|
18884
|
+
const text = stripIndent(line, indent);
|
|
18885
|
+
let m = text.match(/^[-*+]\s+\[([ xX])\][ \t](.*)$/);
|
|
18886
|
+
if (m) {
|
|
18887
|
+
return {
|
|
18888
|
+
indent,
|
|
18889
|
+
type: "todo",
|
|
18890
|
+
checked: m[1].toLowerCase() === "x",
|
|
18891
|
+
content: m[2]
|
|
18892
|
+
};
|
|
18893
|
+
}
|
|
18894
|
+
m = text.match(/^(\d+)[.)][ \t]+(.*)$/);
|
|
18895
|
+
if (m)
|
|
18896
|
+
return { indent, type: "numbered", content: m[2], num: parseInt(m[1], 10) };
|
|
18897
|
+
m = text.match(/^[-*+][ \t]+(.*)$/);
|
|
18898
|
+
if (m)
|
|
18899
|
+
return { indent, type: "bullet", content: m[1] };
|
|
18900
|
+
return null;
|
|
18901
|
+
}
|
|
18902
|
+
function leadingIndent(line) {
|
|
18903
|
+
let indent = 0;
|
|
18904
|
+
for (const ch of line) {
|
|
18905
|
+
if (ch === " ")
|
|
18906
|
+
indent++;
|
|
18907
|
+
else if (ch === "\t")
|
|
18908
|
+
indent += 4;
|
|
18909
|
+
else
|
|
18910
|
+
break;
|
|
18911
|
+
}
|
|
18912
|
+
return indent;
|
|
18913
|
+
}
|
|
18914
|
+
function stripIndent(line, columns) {
|
|
18915
|
+
let indent = 0;
|
|
18916
|
+
let i = 0;
|
|
18917
|
+
for (;i < line.length && indent < columns; i++) {
|
|
18918
|
+
const ch = line[i];
|
|
18919
|
+
if (ch === " ")
|
|
18920
|
+
indent++;
|
|
18921
|
+
else if (ch === "\t")
|
|
18922
|
+
indent += 4;
|
|
18923
|
+
else
|
|
18924
|
+
break;
|
|
18925
|
+
}
|
|
18926
|
+
return line.slice(i);
|
|
18927
|
+
}
|
|
18928
|
+
function isFenceClose(line, char, len) {
|
|
18929
|
+
const trimmed = line.trim();
|
|
18930
|
+
if (!trimmed || trimmed[0] !== char)
|
|
18931
|
+
return false;
|
|
18932
|
+
for (const ch of trimmed) {
|
|
18933
|
+
if (ch !== char)
|
|
18934
|
+
return false;
|
|
18935
|
+
}
|
|
18936
|
+
return trimmed.length >= len;
|
|
18937
|
+
}
|
|
18938
|
+
function cleanLang(lang) {
|
|
18939
|
+
return lang.trim().replace(/[^A-Za-z0-9_+.#-]/g, "");
|
|
18940
|
+
}
|
|
18941
|
+
function looksLikeTableAt(lines, i, minIndent) {
|
|
18942
|
+
const head = lines[i];
|
|
18943
|
+
const delim = lines[i + 1];
|
|
18944
|
+
if (head == null || delim == null)
|
|
18945
|
+
return false;
|
|
18946
|
+
if (leadingIndent(head) < minIndent || leadingIndent(delim) < minIndent)
|
|
18947
|
+
return false;
|
|
18948
|
+
const headText = stripIndent(head, minIndent);
|
|
18949
|
+
const delimText = stripIndent(delim, minIndent);
|
|
18950
|
+
if (!headText.includes("|") || !delimText.includes("|"))
|
|
18951
|
+
return false;
|
|
18952
|
+
const cells = splitTableRow(delimText);
|
|
18953
|
+
return cells.length > 0 && cells.every((c) => RE_DELIM_CELL.test(c));
|
|
18954
|
+
}
|
|
18955
|
+
function splitTableRow(line) {
|
|
18956
|
+
const cells = [];
|
|
18957
|
+
let cur = "";
|
|
18958
|
+
for (let i = 0;i < line.length; i++) {
|
|
18959
|
+
const ch = line[i];
|
|
18960
|
+
if (ch === "\\" && line[i + 1] === "|") {
|
|
18961
|
+
cur += "|";
|
|
18962
|
+
i++;
|
|
18963
|
+
continue;
|
|
18964
|
+
}
|
|
18965
|
+
if (ch === "|") {
|
|
18966
|
+
cells.push(cur);
|
|
18967
|
+
cur = "";
|
|
18968
|
+
continue;
|
|
18969
|
+
}
|
|
18970
|
+
cur += ch;
|
|
18971
|
+
}
|
|
18972
|
+
cells.push(cur);
|
|
18973
|
+
if (cells.length && cells[0].trim() === "")
|
|
18974
|
+
cells.shift();
|
|
18975
|
+
if (cells.length && cells[cells.length - 1].trim() === "")
|
|
18976
|
+
cells.pop();
|
|
18977
|
+
return cells.map((c) => c.trim());
|
|
18978
|
+
}
|
|
18979
|
+
var HTML_FENCE = "mh-html", RE, VIDEO_EXTS, AUDIO_EXTS, RE_DELIM_CELL;
|
|
18980
|
+
var init_grammar = __esm(() => {
|
|
18981
|
+
RE = {
|
|
18982
|
+
fenceOpen: /^\s*(`{3,}|~{3,})\s*([^\s`]*)?.*$/,
|
|
18983
|
+
h: /^(#{1,6})\s+(.*)$/,
|
|
18984
|
+
todo: /^\s*[-*+]\s+\[([ xX])\][ \t](.*)$/,
|
|
18985
|
+
bullet: /^\s*[-*+]\s+(.*)$/,
|
|
18986
|
+
numbered: /^\s*(\d+)[.)]\s+(.*)$/,
|
|
18987
|
+
quote: /^>[ \t](.*)$/,
|
|
18988
|
+
divider: /^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/
|
|
18989
|
+
};
|
|
18990
|
+
VIDEO_EXTS = new Set(["mp4", "webm", "mov", "m4v", "ogv", "mkv", "ogg"]);
|
|
18991
|
+
AUDIO_EXTS = new Set(["mp3", "wav", "m4a", "aac", "flac", "opus", "oga", "weba"]);
|
|
18992
|
+
RE_DELIM_CELL = /^\s*:?-+:?\s*$/;
|
|
18993
|
+
});
|
|
18994
|
+
|
|
18995
|
+
// src/core/md/inline.ts
|
|
18996
|
+
function tokenizeInline(text) {
|
|
18997
|
+
if (!text)
|
|
18998
|
+
return [];
|
|
18999
|
+
const cands = [];
|
|
19000
|
+
for (const p of PATTERNS) {
|
|
19001
|
+
p.re.lastIndex = 0;
|
|
19002
|
+
let m;
|
|
19003
|
+
while (m = p.re.exec(text)) {
|
|
19004
|
+
const start = m.index;
|
|
19005
|
+
const end = start + m[0].length;
|
|
19006
|
+
let innerFrom;
|
|
19007
|
+
let innerTo;
|
|
19008
|
+
let url2;
|
|
19009
|
+
let alt;
|
|
19010
|
+
if (p.kind === "link") {
|
|
19011
|
+
innerFrom = start + 1;
|
|
19012
|
+
innerTo = start + 1 + m[1].length;
|
|
19013
|
+
url2 = m[2];
|
|
19014
|
+
} else if (p.kind === "image") {
|
|
19015
|
+
innerFrom = start + 2;
|
|
19016
|
+
innerTo = start + 2 + m[1].length;
|
|
19017
|
+
url2 = m[2];
|
|
19018
|
+
alt = m[1];
|
|
19019
|
+
} else {
|
|
19020
|
+
innerFrom = start + p.delim;
|
|
19021
|
+
innerTo = end - p.delim;
|
|
19022
|
+
if (innerTo <= innerFrom)
|
|
19023
|
+
continue;
|
|
19024
|
+
}
|
|
19025
|
+
cands.push({ kind: p.kind, start, end, innerFrom, innerTo, url: url2, alt, rank: p.rank });
|
|
19026
|
+
}
|
|
19027
|
+
}
|
|
19028
|
+
cands.sort((a, b) => a.start - b.start || a.rank - b.rank || b.end - a.end);
|
|
19029
|
+
const out = [];
|
|
19030
|
+
let cursor = 0;
|
|
19031
|
+
for (const c of cands) {
|
|
19032
|
+
if (c.start < cursor)
|
|
19033
|
+
continue;
|
|
19034
|
+
cursor = c.end;
|
|
19035
|
+
out.push({
|
|
19036
|
+
kind: c.kind,
|
|
19037
|
+
start: c.start,
|
|
19038
|
+
end: c.end,
|
|
19039
|
+
innerFrom: c.innerFrom,
|
|
19040
|
+
innerTo: c.innerTo,
|
|
19041
|
+
...c.url !== undefined ? { url: c.url } : {},
|
|
19042
|
+
...c.alt !== undefined ? { alt: c.alt } : {}
|
|
19043
|
+
});
|
|
19044
|
+
}
|
|
19045
|
+
return out;
|
|
19046
|
+
}
|
|
19047
|
+
var PATTERNS;
|
|
19048
|
+
var init_inline = __esm(() => {
|
|
19049
|
+
PATTERNS = [
|
|
19050
|
+
{ kind: "code", re: /(?<!\\)`([^`\n]+)`/g, delim: 1, rank: 0 },
|
|
19051
|
+
{ kind: "image", re: /(?<!\\)!\[([^\]\n]*)\]\(([^)\s]+)\)/g, delim: 0, rank: 1 },
|
|
19052
|
+
{ kind: "strong", re: /(?<!\\)\*\*([^*\n]+?)(?<!\\)\*\*/g, delim: 2, rank: 2 },
|
|
19053
|
+
{ kind: "strong", re: /(?<!\\)__([^_\n]+?)(?<!\\)__/g, delim: 2, rank: 2 },
|
|
19054
|
+
{ kind: "del", re: /(?<!\\)~~([^~\n]+?)(?<!\\)~~/g, delim: 2, rank: 3 },
|
|
19055
|
+
{ kind: "em", re: /(?<![*\w])(?<!\\)\*([^*\n]+?)(?<!\\)\*(?!\*)/g, delim: 1, rank: 4 },
|
|
19056
|
+
{ kind: "em", re: /(?<![_\w])(?<!\\)_([^_\n]+?)(?<!\\)_(?!_)/g, delim: 1, rank: 4 },
|
|
19057
|
+
{ kind: "link", re: /(?<!\\)\[([^\]\n]+)\]\(([^)\s]+)\)/g, delim: 0, rank: 5 }
|
|
19058
|
+
];
|
|
19059
|
+
});
|
|
19060
|
+
|
|
19061
|
+
// src/core/md/heal.ts
|
|
19062
|
+
function fenceMask(lines) {
|
|
19063
|
+
const mask = new Array(lines.length).fill(false);
|
|
19064
|
+
for (let i = 0;i < lines.length; i++) {
|
|
19065
|
+
const m = lines[i].match(RE.fenceOpen);
|
|
19066
|
+
if (!m)
|
|
19067
|
+
continue;
|
|
19068
|
+
const ch = m[1][0];
|
|
19069
|
+
const len = m[1].length;
|
|
19070
|
+
let close = -1;
|
|
19071
|
+
for (let j = i + 1;j < lines.length; j++) {
|
|
19072
|
+
if (isFenceClose(lines[j], ch, len)) {
|
|
19073
|
+
close = j;
|
|
19074
|
+
break;
|
|
19075
|
+
}
|
|
19076
|
+
}
|
|
19077
|
+
if (close === -1)
|
|
19078
|
+
continue;
|
|
19079
|
+
for (let j = i;j <= close; j++)
|
|
19080
|
+
mask[j] = true;
|
|
19081
|
+
i = close;
|
|
19082
|
+
}
|
|
19083
|
+
return mask;
|
|
19084
|
+
}
|
|
19085
|
+
function healLegacyMarkdown(src) {
|
|
19086
|
+
if (!MAYBE_LEGACY.test(src))
|
|
19087
|
+
return src;
|
|
19088
|
+
const lines = src.split(`
|
|
19089
|
+
`);
|
|
19090
|
+
const inFence = fenceMask(lines);
|
|
19091
|
+
const quoteish = (i) => i >= 0 && i < lines.length && !inFence[i] && matchQuoteLine(lines[i].replace(/^[ \t]*/, "")) !== null;
|
|
19092
|
+
let changed = false;
|
|
19093
|
+
for (let i = 0;i < lines.length; i++) {
|
|
19094
|
+
if (inFence[i])
|
|
19095
|
+
continue;
|
|
19096
|
+
const line = lines[i];
|
|
19097
|
+
if (LEGACY_EMPTY_TODO.test(line)) {
|
|
19098
|
+
lines[i] = line + " ";
|
|
19099
|
+
changed = true;
|
|
19100
|
+
} else if (BARE_QUOTE.test(line) && (quoteish(i - 1) || quoteish(i + 1))) {
|
|
19101
|
+
lines[i] = line + " ";
|
|
19102
|
+
changed = true;
|
|
19103
|
+
}
|
|
19104
|
+
}
|
|
19105
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
19106
|
+
if (inFence[i])
|
|
19107
|
+
continue;
|
|
19108
|
+
const line = lines[i];
|
|
19109
|
+
if (BARE_QUOTE.test(line) && (quoteish(i - 1) || quoteish(i + 1))) {
|
|
19110
|
+
lines[i] = line + " ";
|
|
19111
|
+
changed = true;
|
|
19112
|
+
}
|
|
19113
|
+
}
|
|
19114
|
+
return changed ? lines.join(`
|
|
19115
|
+
`) : src;
|
|
19116
|
+
}
|
|
19117
|
+
var LEGACY_EMPTY_TODO, BARE_QUOTE, MAYBE_LEGACY;
|
|
19118
|
+
var init_heal = __esm(() => {
|
|
19119
|
+
init_grammar();
|
|
19120
|
+
LEGACY_EMPTY_TODO = /^[ \t]*[-*+][ \t]+\[[ xX]\]$/;
|
|
19121
|
+
BARE_QUOTE = /^[ \t]*>$/;
|
|
19122
|
+
MAYBE_LEGACY = /^[ \t]*(?:[-*+][ \t]+\[[ xX]\]|>)$/m;
|
|
19123
|
+
});
|
|
19124
|
+
|
|
18762
19125
|
// src/core/sync/share-render.ts
|
|
18763
19126
|
function escapeHtml(s) {
|
|
18764
19127
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
18765
19128
|
}
|
|
18766
19129
|
function renderInline(src, opts = {}) {
|
|
18767
|
-
const
|
|
18768
|
-
let
|
|
18769
|
-
|
|
18770
|
-
|
|
18771
|
-
|
|
18772
|
-
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
18776
|
-
|
|
18777
|
-
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
19130
|
+
const tokens = tokenizeInline(src);
|
|
19131
|
+
let out = "";
|
|
19132
|
+
let pos = 0;
|
|
19133
|
+
for (const t of tokens) {
|
|
19134
|
+
out += escapeHtml(src.slice(pos, t.start));
|
|
19135
|
+
const inner = src.slice(t.innerFrom, t.innerTo);
|
|
19136
|
+
const rawUrl = rewrite(t.url ?? "", opts);
|
|
19137
|
+
switch (t.kind) {
|
|
19138
|
+
case "code":
|
|
19139
|
+
out += `<code>${escapeHtml(inner)}</code>`;
|
|
19140
|
+
break;
|
|
19141
|
+
case "strong":
|
|
19142
|
+
out += `<strong>${escapeHtml(inner)}</strong>`;
|
|
19143
|
+
break;
|
|
19144
|
+
case "em":
|
|
19145
|
+
out += `<em>${escapeHtml(inner)}</em>`;
|
|
19146
|
+
break;
|
|
19147
|
+
case "del":
|
|
19148
|
+
out += `<del>${escapeHtml(inner)}</del>`;
|
|
19149
|
+
break;
|
|
19150
|
+
case "link": {
|
|
19151
|
+
const href = escapeHtml(safeUrl(rawUrl));
|
|
19152
|
+
out += `<a href="${href}" target="_blank" rel="noreferrer noopener">${escapeHtml(inner)}</a>`;
|
|
19153
|
+
break;
|
|
19154
|
+
}
|
|
19155
|
+
case "image": {
|
|
19156
|
+
const imgSrc = escapeHtml(safeUrl(rawUrl, { allowData: true }));
|
|
19157
|
+
out += `<img src="${imgSrc}" alt="${escapeHtml(t.alt ?? "")}" loading="lazy">`;
|
|
19158
|
+
break;
|
|
19159
|
+
}
|
|
19160
|
+
}
|
|
19161
|
+
pos = t.end;
|
|
19162
|
+
}
|
|
19163
|
+
return out + escapeHtml(src.slice(pos));
|
|
18785
19164
|
}
|
|
18786
19165
|
function rewrite(url2, opts) {
|
|
18787
19166
|
if (opts.rewriteBlob && url2.startsWith("/blob/"))
|
|
18788
19167
|
return opts.rewriteBlob(url2);
|
|
18789
19168
|
return url2;
|
|
18790
19169
|
}
|
|
19170
|
+
function renderMedia(m, opts) {
|
|
19171
|
+
const raw = rewrite(m.src, opts);
|
|
19172
|
+
if (m.kind === "video") {
|
|
19173
|
+
const src = escapeHtml(safeUrl(raw, { allowData: true }));
|
|
19174
|
+
return `<p class="mh-media"><video src="${src}" controls preload="metadata"></video></p>`;
|
|
19175
|
+
}
|
|
19176
|
+
if (m.kind === "audio") {
|
|
19177
|
+
const src = escapeHtml(safeUrl(raw, { allowData: true }));
|
|
19178
|
+
return `<p class="mh-media"><audio src="${src}" controls preload="metadata"></audio></p>`;
|
|
19179
|
+
}
|
|
19180
|
+
const href = escapeHtml(safeUrl(raw));
|
|
19181
|
+
return `<p class="mh-file"><a href="${href}" download>${escapeHtml(m.name || "file")}</a></p>`;
|
|
19182
|
+
}
|
|
18791
19183
|
function renderMarkdown(md, opts = {}) {
|
|
18792
|
-
const
|
|
18793
|
-
`)
|
|
19184
|
+
const healed = healLegacyMarkdown((md ?? "").replace(/\r\n?/g, `
|
|
19185
|
+
`));
|
|
19186
|
+
const lines = healed.split(`
|
|
18794
19187
|
`);
|
|
18795
19188
|
const out = [];
|
|
18796
19189
|
let i = 0;
|
|
@@ -18803,38 +19196,39 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18803
19196
|
};
|
|
18804
19197
|
while (i < lines.length) {
|
|
18805
19198
|
const line = lines[i];
|
|
18806
|
-
const fence = line.match(
|
|
19199
|
+
const fence = line.match(RE.fenceOpen);
|
|
18807
19200
|
if (fence) {
|
|
18808
|
-
flushPara();
|
|
18809
19201
|
const marker = fence[1][0];
|
|
18810
|
-
const
|
|
18811
|
-
|
|
18812
|
-
i
|
|
18813
|
-
|
|
18814
|
-
|
|
18815
|
-
if (m && m[1][0] === marker) {
|
|
18816
|
-
i++;
|
|
19202
|
+
const len = fence[1].length;
|
|
19203
|
+
let close = -1;
|
|
19204
|
+
for (let j = i + 1;j < lines.length; j++) {
|
|
19205
|
+
if (isFenceClose(lines[j], marker, len)) {
|
|
19206
|
+
close = j;
|
|
18817
19207
|
break;
|
|
18818
19208
|
}
|
|
18819
|
-
body.push(lines[i]);
|
|
18820
|
-
i++;
|
|
18821
19209
|
}
|
|
18822
|
-
if (
|
|
18823
|
-
|
|
19210
|
+
if (close !== -1) {
|
|
19211
|
+
flushPara();
|
|
19212
|
+
const info = cleanLang(fence[2] ?? "");
|
|
19213
|
+
const body = lines.slice(i + 1, close);
|
|
19214
|
+
i = close + 1;
|
|
19215
|
+
if (info === HTML_FENCE) {
|
|
19216
|
+
out.push(`<iframe class="mh-embed" sandbox="allow-scripts allow-popups" srcdoc="${escapeHtml(body.join(`
|
|
18824
19217
|
`))}"></iframe>`);
|
|
18825
|
-
|
|
18826
|
-
|
|
18827
|
-
|
|
19218
|
+
} else {
|
|
19219
|
+
const cls = info ? ` class="language-${escapeHtml(info)}"` : "";
|
|
19220
|
+
out.push(`<pre><code${cls}>${escapeHtml(body.join(`
|
|
18828
19221
|
`))}</code></pre>`);
|
|
19222
|
+
}
|
|
19223
|
+
continue;
|
|
18829
19224
|
}
|
|
18830
|
-
continue;
|
|
18831
19225
|
}
|
|
18832
19226
|
if (line.trim() === "") {
|
|
18833
19227
|
flushPara();
|
|
18834
19228
|
i++;
|
|
18835
19229
|
continue;
|
|
18836
19230
|
}
|
|
18837
|
-
const h = line.match(
|
|
19231
|
+
const h = stripLead(line).match(RE.h);
|
|
18838
19232
|
if (h) {
|
|
18839
19233
|
flushPara();
|
|
18840
19234
|
const level = h[1].length;
|
|
@@ -18842,30 +19236,33 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18842
19236
|
i++;
|
|
18843
19237
|
continue;
|
|
18844
19238
|
}
|
|
18845
|
-
if (
|
|
19239
|
+
if (RE.divider.test(line)) {
|
|
18846
19240
|
flushPara();
|
|
18847
19241
|
out.push("<hr>");
|
|
18848
19242
|
i++;
|
|
18849
19243
|
continue;
|
|
18850
19244
|
}
|
|
18851
|
-
if (
|
|
19245
|
+
if (matchQuoteLine(stripLead(line)) !== null) {
|
|
18852
19246
|
flushPara();
|
|
18853
19247
|
const q = [];
|
|
18854
|
-
while (i < lines.length
|
|
18855
|
-
|
|
19248
|
+
while (i < lines.length) {
|
|
19249
|
+
const content = matchQuoteLine(stripLead(lines[i]));
|
|
19250
|
+
if (content === null)
|
|
19251
|
+
break;
|
|
19252
|
+
q.push(content);
|
|
18856
19253
|
i++;
|
|
18857
19254
|
}
|
|
18858
19255
|
out.push(`<blockquote>${renderMarkdown(q.join(`
|
|
18859
19256
|
`), opts)}</blockquote>`);
|
|
18860
19257
|
continue;
|
|
18861
19258
|
}
|
|
18862
|
-
if (
|
|
19259
|
+
if (looksLikeTableAt(lines, i, 0)) {
|
|
18863
19260
|
flushPara();
|
|
18864
19261
|
const rows = [];
|
|
18865
|
-
const head =
|
|
19262
|
+
const head = splitTableRow(stripLead(lines[i]));
|
|
18866
19263
|
i += 2;
|
|
18867
19264
|
while (i < lines.length && lines[i].includes("|") && lines[i].trim() !== "") {
|
|
18868
|
-
rows.push(
|
|
19265
|
+
rows.push(splitTableRow(stripLead(lines[i])));
|
|
18869
19266
|
i++;
|
|
18870
19267
|
}
|
|
18871
19268
|
const th = head.map((c) => `<th>${renderInline(c, opts)}</th>`).join("");
|
|
@@ -18873,24 +19270,31 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18873
19270
|
out.push(`<table><thead><tr>${th}</tr></thead><tbody>${body}</tbody></table>`);
|
|
18874
19271
|
continue;
|
|
18875
19272
|
}
|
|
18876
|
-
const
|
|
18877
|
-
|
|
18878
|
-
if (ul || ol) {
|
|
19273
|
+
const first = matchListLine(line, 0);
|
|
19274
|
+
if (first) {
|
|
18879
19275
|
flushPara();
|
|
18880
|
-
const ordered =
|
|
18881
|
-
const
|
|
18882
|
-
|
|
18883
|
-
|
|
18884
|
-
|
|
19276
|
+
const ordered = first.type === "numbered";
|
|
19277
|
+
const lis = [];
|
|
19278
|
+
while (i < lines.length) {
|
|
19279
|
+
const it = matchListLine(lines[i], 0);
|
|
19280
|
+
if (!it || it.type === "numbered" !== ordered)
|
|
19281
|
+
break;
|
|
19282
|
+
if (it.type === "todo") {
|
|
19283
|
+
lis.push(`<li class="todo"><input type="checkbox" disabled${it.checked ? " checked" : ""}> ${renderInline(it.content, opts)}</li>`);
|
|
19284
|
+
} else if (it.type === "numbered" && it.num !== undefined) {
|
|
19285
|
+
lis.push(`<li value="${it.num}">${renderInline(it.content, opts)}</li>`);
|
|
19286
|
+
} else {
|
|
19287
|
+
lis.push(`<li>${renderInline(it.content, opts)}</li>`);
|
|
19288
|
+
}
|
|
18885
19289
|
i++;
|
|
18886
19290
|
}
|
|
18887
|
-
|
|
18888
|
-
out.push(ordered ? `<ol>${lis}</ol>` : `<ul>${lis}</ul>`);
|
|
19291
|
+
out.push(ordered ? `<ol>${lis.join("")}</ol>` : `<ul>${lis.join("")}</ul>`);
|
|
18889
19292
|
continue;
|
|
18890
19293
|
}
|
|
18891
|
-
|
|
19294
|
+
const media = matchMediaEmbed(line);
|
|
19295
|
+
if (media) {
|
|
18892
19296
|
flushPara();
|
|
18893
|
-
out.push(`<p class="mh-img">${renderInline(line.trim(), opts)}</p>`);
|
|
19297
|
+
out.push(media.kind === "image" ? `<p class="mh-img">${renderInline(line.trim(), opts)}</p>` : renderMedia(media, opts));
|
|
18894
19298
|
i++;
|
|
18895
19299
|
continue;
|
|
18896
19300
|
}
|
|
@@ -18901,20 +19305,13 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18901
19305
|
return out.join(`
|
|
18902
19306
|
`);
|
|
18903
19307
|
}
|
|
18904
|
-
function
|
|
18905
|
-
|
|
18906
|
-
if (s.startsWith("|"))
|
|
18907
|
-
s = s.slice(1);
|
|
18908
|
-
if (s.endsWith("|"))
|
|
18909
|
-
s = s.slice(0, -1);
|
|
18910
|
-
return s.split("|").map((c) => c.trim());
|
|
19308
|
+
function stripLead(line) {
|
|
19309
|
+
return stripIndent(line, Number.MAX_SAFE_INTEGER);
|
|
18911
19310
|
}
|
|
18912
|
-
var A, B, RESTORE, FENCE;
|
|
18913
19311
|
var init_share_render = __esm(() => {
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
FENCE = /^\s*(`{3,}|~{3,})(.*)$/;
|
|
19312
|
+
init_grammar();
|
|
19313
|
+
init_inline();
|
|
19314
|
+
init_heal();
|
|
18918
19315
|
});
|
|
18919
19316
|
|
|
18920
19317
|
// src/core/sync/share-serve.ts
|
|
@@ -19332,6 +19729,8 @@ function pageShell(title, inner, opts = {}) {
|
|
|
19332
19729
|
td[contenteditable]{outline:none} td.saving{opacity:.5} td.saved{background:rgba(46,160,67,.18)}
|
|
19333
19730
|
.tag{display:inline-block;background:var(--card);border:1px solid var(--line);border-radius:999px;padding:1px 9px;font-size:12px}
|
|
19334
19731
|
.muted{color:var(--muted)} .mh-img{text-align:center}
|
|
19732
|
+
.mh-media{text-align:center} .mh-media video,.mh-media audio{max-width:100%}
|
|
19733
|
+
.mh-file a{display:inline-block;background:var(--card);border:1px solid var(--line);border-radius:8px;padding:8px 14px;color:var(--accent);text-decoration:none}
|
|
19335
19734
|
iframe.mh-embed{width:100%;border:1px solid var(--line);border-radius:8px;min-height:240px}
|
|
19336
19735
|
textarea.mh-raw{width:100%;min-height:60vh;font:14px/1.6 ui-monospace,SFMono-Regular,Menlo,monospace;padding:14px;border:1px solid var(--line);border-radius:8px;background:var(--card);color:var(--fg)}
|
|
19337
19736
|
ul.files{list-style:none;padding:0} ul.files li{padding:4px 0} button.file{background:none;border:0;color:var(--accent);cursor:pointer;font-size:15px;padding:0}
|
|
@@ -19509,6 +19908,7 @@ CREATE TABLE IF NOT EXISTS databases (
|
|
|
19509
19908
|
id TEXT PRIMARY KEY,
|
|
19510
19909
|
name TEXT,
|
|
19511
19910
|
icon TEXT,
|
|
19911
|
+
meta TEXT, -- JSON: generic replicated metadata (e.g. UI flags), one LWW register
|
|
19512
19912
|
created_hlc TEXT,
|
|
19513
19913
|
__deleted INTEGER NOT NULL DEFAULT 0
|
|
19514
19914
|
);
|
|
@@ -19791,9 +20191,25 @@ function migrateStoragePeerUrls(db) {
|
|
|
19791
20191
|
});
|
|
19792
20192
|
tx();
|
|
19793
20193
|
}
|
|
20194
|
+
function migrateDatabases(db) {
|
|
20195
|
+
if (hasColumn(db, "databases", "meta"))
|
|
20196
|
+
return;
|
|
20197
|
+
db.exec("ALTER TABLE databases ADD COLUMN meta TEXT");
|
|
20198
|
+
db.exec(`
|
|
20199
|
+
UPDATE databases SET meta = (
|
|
20200
|
+
SELECT c.value FROM crdt_changes c
|
|
20201
|
+
WHERE c.dataset = 'databases' AND c.col = 'meta' AND c.row_id = databases.id
|
|
20202
|
+
ORDER BY c.hlc DESC LIMIT 1
|
|
20203
|
+
)
|
|
20204
|
+
WHERE id IN (
|
|
20205
|
+
SELECT DISTINCT row_id FROM crdt_changes WHERE dataset = 'databases' AND col = 'meta'
|
|
20206
|
+
)
|
|
20207
|
+
`);
|
|
20208
|
+
}
|
|
19794
20209
|
function initSchema(db) {
|
|
19795
20210
|
runSchema(db);
|
|
19796
20211
|
migrateOplog(db);
|
|
20212
|
+
migrateDatabases(db);
|
|
19797
20213
|
migrateCrdtChangesSeq(db);
|
|
19798
20214
|
migrateRecords(db);
|
|
19799
20215
|
migratePeers(db);
|
|
@@ -20033,7 +20449,7 @@ init_config();
|
|
|
20033
20449
|
// package.json
|
|
20034
20450
|
var package_default = {
|
|
20035
20451
|
name: "@tensorix/metahub",
|
|
20036
|
-
version: "0.
|
|
20452
|
+
version: "0.3.2",
|
|
20037
20453
|
description: "Metahub core library and CLI",
|
|
20038
20454
|
license: "AGPL-3.0-only",
|
|
20039
20455
|
type: "module",
|
|
@@ -20063,16 +20479,21 @@ var package_default = {
|
|
|
20063
20479
|
"build:shell": "bun run scripts/build-shell.ts",
|
|
20064
20480
|
"build:share-viewer": "bun run scripts/build-share-viewer.ts",
|
|
20065
20481
|
"build:binaries": "bun run scripts/compile-binaries.ts",
|
|
20482
|
+
"build:binaries:host": "HOST_ONLY=1 bun run scripts/compile-binaries.ts",
|
|
20066
20483
|
prepublishOnly: "bun run build",
|
|
20067
20484
|
test: "bun test",
|
|
20068
20485
|
release: "bun run scripts/tag-release.ts"
|
|
20069
20486
|
},
|
|
20070
20487
|
dependencies: {
|
|
20071
20488
|
"@clack/prompts": "^1.5.0",
|
|
20489
|
+
"@codemirror/commands": "6.10.4",
|
|
20490
|
+
"@codemirror/state": "6.7.0",
|
|
20491
|
+
"@codemirror/view": "6.43.4",
|
|
20072
20492
|
"@sqlite.org/sqlite-wasm": "^3.53.0-build1",
|
|
20073
20493
|
aws4fetch: "^1.0.20",
|
|
20074
20494
|
citty: "^0.1.6",
|
|
20075
20495
|
"highlight.js": "^11.11.1",
|
|
20496
|
+
jsqr: "^1.4.0",
|
|
20076
20497
|
preact: "^10.29.2",
|
|
20077
20498
|
"qrcode-generator": "^2.0.4",
|
|
20078
20499
|
turndown: "^7.2.4",
|
|
@@ -20081,8 +20502,17 @@ var package_default = {
|
|
|
20081
20502
|
},
|
|
20082
20503
|
devDependencies: {
|
|
20083
20504
|
"@happy-dom/global-registrator": "^20.10.2",
|
|
20505
|
+
"@prettier/plugin-php": "^0.25.0",
|
|
20084
20506
|
"@types/bun": "latest",
|
|
20085
20507
|
"@types/turndown": "^5.0.6",
|
|
20508
|
+
"@wasm-fmt/clang-format": "^22.1.8",
|
|
20509
|
+
"@wasm-fmt/gofmt": "^0.7.3",
|
|
20510
|
+
"@wasm-fmt/lua_fmt": "^0.3.3",
|
|
20511
|
+
"@wasm-fmt/ruff_fmt": "^0.15.20",
|
|
20512
|
+
"@wasm-fmt/taplo_fmt": "^0.2.5",
|
|
20513
|
+
"mvdan-sh": "^0.10.1",
|
|
20514
|
+
prettier: "^3.6",
|
|
20515
|
+
"sql-formatter": "^15",
|
|
20086
20516
|
typescript: "^5"
|
|
20087
20517
|
}
|
|
20088
20518
|
};
|
|
@@ -20208,56 +20638,196 @@ function wantsHtml(req) {
|
|
|
20208
20638
|
return req.method === "GET" && (req.headers.get("accept") ?? "").includes("text/html");
|
|
20209
20639
|
}
|
|
20210
20640
|
var HTML_HEADERS = { "content-type": "text/html; charset=utf-8" };
|
|
20641
|
+
var CUBE_SVG = '<path d="M21 7.5 12 2 3 7.5v9L12 22l9-5.5z"/><path d="m3 7.5 9 5.5 9-5.5M12 22v-9"/>';
|
|
20642
|
+
var EYE_SVG = '<path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/>';
|
|
20643
|
+
var EYE_OFF_SVG = '<path d="M4 4l16 16M9.5 9.5a3 3 0 0 0 4 4M6.5 6.7C4.5 8 3 10 2 12c2 4 6 7 10 7 1.6 0 3-.4 4.4-1.1M9.5 5.2A9.7 9.7 0 0 1 12 5c4 0 8 3 10 7-.6 1.3-1.5 2.5-2.5 3.5"/>';
|
|
20644
|
+
var UNLOCK_CSS = `
|
|
20645
|
+
:root{color-scheme:light;
|
|
20646
|
+
--bg:#ffffff;--surface:#fbfbfa;--surface-2:#f1f1ef;--fg:#2c2c30;--fg-soft:#5b5b62;
|
|
20647
|
+
--muted:#75757f;--line:#ebebe8;--line-strong:#dededb;--accent:#4a55d6;--accent-fg:#ffffff;
|
|
20648
|
+
--accent-soft:#eef0ff;--danger:#d6473b;--danger-soft:#fdeceb;--code-bg:#f8f8f6}
|
|
20649
|
+
:root[data-resolved="dark"]{color-scheme:dark;
|
|
20650
|
+
--bg:#1a1a1c;--surface:#202022;--surface-2:#2a2a2d;--fg:#e6e6e9;--fg-soft:#b4b4bb;
|
|
20651
|
+
--muted:#8b8b95;--line:#2c2c30;--line-strong:#3a3a40;--accent:#7b86ff;--accent-fg:#14141a;
|
|
20652
|
+
--accent-soft:#23243a;--danger:#f87168;--danger-soft:#341e1c;--code-bg:#26262a}
|
|
20653
|
+
*{box-sizing:border-box}
|
|
20654
|
+
html,body{margin:0;min-height:100%}
|
|
20655
|
+
body{font-family:"Hanken Grotesk",-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
|
|
20656
|
+
color:var(--fg);background:var(--bg);line-height:1.55;
|
|
20657
|
+
display:flex;align-items:center;justify-content:center;min-height:100dvh;
|
|
20658
|
+
padding:calc(24px + env(safe-area-inset-top)) calc(20px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(20px + env(safe-area-inset-left));
|
|
20659
|
+
-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;overflow:hidden}
|
|
20660
|
+
/* Ambient wash: two faint accent blooms behind the card. */
|
|
20661
|
+
body::before{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
|
|
20662
|
+
background:
|
|
20663
|
+
radial-gradient(58vmax 58vmax at 14% -8%,color-mix(in srgb,var(--accent) 13%,transparent),transparent 60%),
|
|
20664
|
+
radial-gradient(50vmax 50vmax at 108% 112%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 55%)}
|
|
20665
|
+
.card{position:relative;z-index:1;width:100%;max-width:420px;background:var(--surface);
|
|
20666
|
+
border:1px solid var(--line);border-radius:18px;padding:32px 26px 26px;
|
|
20667
|
+
box-shadow:0 24px 60px rgba(20,20,40,.14),0 4px 14px rgba(20,20,40,.06);
|
|
20668
|
+
animation:rise .45s cubic-bezier(.2,.7,.3,1) both}
|
|
20669
|
+
@keyframes rise{from{opacity:0;transform:translateY(12px) scale(.985)}to{opacity:1;transform:none}}
|
|
20670
|
+
.brand{width:52px;height:52px;border-radius:14px;display:flex;align-items:center;justify-content:center;
|
|
20671
|
+
background:var(--accent-soft);color:var(--accent);margin-bottom:18px}
|
|
20672
|
+
.brand svg{width:28px;height:28px;fill:none;stroke:currentColor;stroke-width:1.6;
|
|
20673
|
+
stroke-linecap:round;stroke-linejoin:round}
|
|
20674
|
+
.brand path{stroke-dasharray:120;stroke-dashoffset:120;animation:draw .9s .12s ease forwards}
|
|
20675
|
+
@keyframes draw{to{stroke-dashoffset:0}}
|
|
20676
|
+
h1{font-size:21px;font-weight:650;letter-spacing:-.01em;margin:0 0 8px}
|
|
20677
|
+
.sub{color:var(--muted);font-size:13.5px;margin:0 0 22px;line-height:1.6}
|
|
20678
|
+
.sub code{font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,monospace;font-size:12.5px;
|
|
20679
|
+
background:var(--code-bg);border:1px solid var(--line);border-radius:5px;padding:1px 6px;color:var(--fg-soft);
|
|
20680
|
+
white-space:nowrap}
|
|
20681
|
+
.field{position:relative;display:flex;align-items:center}
|
|
20682
|
+
input{width:100%;height:50px;padding:0 48px 0 15px;font-size:16px; /* 16px: never let iOS Safari zoom on focus */
|
|
20683
|
+
font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,monospace;letter-spacing:.01em;
|
|
20684
|
+
color:var(--fg);background:var(--bg);border:1.5px solid var(--line-strong);border-radius:12px;
|
|
20685
|
+
outline:none;transition:border-color .15s,box-shadow .15s}
|
|
20686
|
+
input::placeholder{font-family:var(--ui,inherit);letter-spacing:normal;color:var(--muted);opacity:.8}
|
|
20687
|
+
input:focus{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
|
|
20688
|
+
.card.bad input{border-color:var(--danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--danger) 20%,transparent)}
|
|
20689
|
+
.card.bad .field{animation:shake .34s cubic-bezier(.36,.07,.19,.97)}
|
|
20690
|
+
@keyframes shake{10%,90%{transform:translateX(-1px)}20%,80%{transform:translateX(2px)}
|
|
20691
|
+
30%,50%,70%{transform:translateX(-5px)}40%,60%{transform:translateX(5px)}}
|
|
20692
|
+
.peek{position:absolute;right:6px;width:40px;height:40px;border:0;background:none;cursor:pointer;
|
|
20693
|
+
color:var(--muted);display:flex;align-items:center;justify-content:center;border-radius:9px}
|
|
20694
|
+
.peek:hover{color:var(--fg-soft)}
|
|
20695
|
+
.peek svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.7;
|
|
20696
|
+
stroke-linecap:round;stroke-linejoin:round}
|
|
20697
|
+
.err{color:var(--danger);font-size:13px;font-weight:500;margin:11px 2px 0;
|
|
20698
|
+
display:flex;gap:6px;align-items:center;animation:fade .2s ease both}
|
|
20699
|
+
@keyframes fade{from{opacity:0}to{opacity:1}}
|
|
20700
|
+
button.go{width:100%;height:48px;margin-top:20px;border:0;border-radius:12px;cursor:pointer;
|
|
20701
|
+
background:var(--accent);color:var(--accent-fg);font-size:15px;font-weight:600;font-family:inherit;
|
|
20702
|
+
display:flex;align-items:center;justify-content:center;gap:9px;
|
|
20703
|
+
transition:filter .15s,transform .05s}
|
|
20704
|
+
button.go:hover{filter:brightness(1.06)}
|
|
20705
|
+
button.go:active{transform:translateY(1px)}
|
|
20706
|
+
button.go:disabled{opacity:.7;cursor:default}
|
|
20707
|
+
.spin{width:17px;height:17px;border:2px solid color-mix(in srgb,var(--accent-fg) 45%,transparent);
|
|
20708
|
+
border-top-color:var(--accent-fg);border-radius:50%;animation:spin .7s linear infinite}
|
|
20709
|
+
/* Full-page loader shown while a silent renewal is in flight. */
|
|
20710
|
+
.boot{position:fixed;inset:0;z-index:2;display:flex;align-items:center;justify-content:center;background:var(--bg)}
|
|
20711
|
+
.boot svg{width:40px;height:40px;fill:none;stroke:var(--accent);stroke-width:1.4;
|
|
20712
|
+
stroke-linecap:round;stroke-linejoin:round;animation:breathe 1.5s ease-in-out infinite}
|
|
20713
|
+
@keyframes breathe{0%,100%{opacity:.35;transform:scale(.94)}50%{opacity:1;transform:scale(1)}}
|
|
20714
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
20715
|
+
@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important}
|
|
20716
|
+
.brand path{stroke-dashoffset:0}}
|
|
20717
|
+
`;
|
|
20211
20718
|
function unlockPage() {
|
|
20212
|
-
return `<!doctype html><html lang="
|
|
20213
|
-
<meta charset="utf-8"
|
|
20214
|
-
<
|
|
20215
|
-
<
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
</
|
|
20226
|
-
<
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
<
|
|
20230
|
-
<
|
|
20719
|
+
return `<!doctype html><html lang="zh-CN"><head>
|
|
20720
|
+
<meta charset="utf-8">
|
|
20721
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
20722
|
+
<meta name="theme-color" content="#ffffff">
|
|
20723
|
+
<title>\u9A8C\u8BC1\u8BBF\u95EE\u4EE4\u724C \xB7 metahub</title>
|
|
20724
|
+
<script>
|
|
20725
|
+
// FOUC-guard theme resolution, same key/logic as src/webui/theme.ts.
|
|
20726
|
+
try {
|
|
20727
|
+
var mt = localStorage.getItem("mh-theme");
|
|
20728
|
+
var dark = mt === "dark" || (mt !== "light" && matchMedia("(prefers-color-scheme: dark)").matches);
|
|
20729
|
+
document.documentElement.dataset.resolved = dark ? "dark" : "light";
|
|
20730
|
+
document.head.querySelector('meta[name=theme-color]').setAttribute("content", dark ? "#1a1a1c" : "#ffffff");
|
|
20731
|
+
} catch (e) {}
|
|
20732
|
+
</script>
|
|
20733
|
+
<style>${UNLOCK_CSS}</style></head><body>
|
|
20734
|
+
<div class="boot" id="boot"><svg viewBox="0 0 24 24">${CUBE_SVG}</svg></div>
|
|
20735
|
+
<form class="card" id="f" style="display:none" autocomplete="on">
|
|
20736
|
+
<div class="brand"><svg viewBox="0 0 24 24">${CUBE_SVG}</svg></div>
|
|
20737
|
+
<h1>\u8F93\u5165\u8BBF\u95EE\u4EE4\u724C</h1>
|
|
20738
|
+
<p class="sub">\u6B64\u670D\u52A1\u5668\u5DF2\u5F00\u542F\u8BBF\u95EE\u4FDD\u62A4\u3002\u5728\u5DF2\u767B\u5F55\u8BBE\u5907\u7684\u300C\u8BBE\u7F6E \u2192 \u8BBE\u5907\u4E0E\u6388\u6743\u300D\u83B7\u53D6\u767B\u5F55\u94FE\u63A5\u6216\u4E8C\u7EF4\u7801,\u6216\u5728\u670D\u52A1\u5668\u4E0A\u8FD0\u884C <code>mh token show</code>\u3002</p>
|
|
20739
|
+
<div class="field">
|
|
20740
|
+
<input id="t" type="password" placeholder="\u7C98\u8D34\u8BBF\u95EE\u4EE4\u724C\u6216\u767B\u5F55\u94FE\u63A5"
|
|
20741
|
+
autocomplete="current-password" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="go">
|
|
20742
|
+
<button type="button" class="peek" id="peek" aria-label="\u663E\u793A\u4EE4\u724C"><svg viewBox="0 0 24 24">${EYE_SVG}</svg></button>
|
|
20743
|
+
</div>
|
|
20744
|
+
<p class="err" id="e" style="display:none"></p>
|
|
20745
|
+
<button type="submit" class="go" id="go">\u89E3\u9501\u5E76\u8FDB\u5165</button>
|
|
20231
20746
|
</form>
|
|
20232
20747
|
<script>
|
|
20233
20748
|
var KEY = "mh_token";
|
|
20749
|
+
var form = document.getElementById("f"), boot = document.getElementById("boot");
|
|
20750
|
+
var input = document.getElementById("t"), errEl = document.getElementById("e");
|
|
20751
|
+
var goBtn = document.getElementById("go"), peek = document.getElementById("peek");
|
|
20752
|
+
|
|
20234
20753
|
function save(t) {
|
|
20235
20754
|
localStorage.setItem(KEY, t);
|
|
20236
20755
|
document.cookie = "${COOKIE}=" + encodeURIComponent(t) + "; ${COOKIE_ATTRS}" +
|
|
20237
20756
|
(location.protocol === "https:" ? "; Secure" : "");
|
|
20238
20757
|
}
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20758
|
+
// Accept a pasted "\u2026/?token=xxx" login link, not just the bare token.
|
|
20759
|
+
function normalize(v) {
|
|
20760
|
+
v = (v || "").trim();
|
|
20761
|
+
var m = /[?&#]token=([^&\\s]+)/.exec(v);
|
|
20762
|
+
if (m) { try { return decodeURIComponent(m[1]); } catch (e) { return m[1]; } }
|
|
20763
|
+
return v;
|
|
20764
|
+
}
|
|
20765
|
+
function showForm(msg) {
|
|
20766
|
+
boot.style.display = "none";
|
|
20767
|
+
form.style.display = "block";
|
|
20768
|
+
if (msg) { errEl.textContent = msg; errEl.style.display = "flex"; }
|
|
20769
|
+
input.focus();
|
|
20770
|
+
}
|
|
20771
|
+
function fail(msg) {
|
|
20772
|
+
goBtn.disabled = false;
|
|
20773
|
+
goBtn.innerHTML = "\u89E3\u9501\u5E76\u8FDB\u5165";
|
|
20774
|
+
errEl.textContent = msg;
|
|
20775
|
+
errEl.style.display = "flex";
|
|
20776
|
+
form.classList.remove("bad");
|
|
20777
|
+
void form.offsetWidth; // reflow so the shake animation restarts
|
|
20778
|
+
form.classList.add("bad");
|
|
20779
|
+
input.focus();
|
|
20780
|
+
input.select();
|
|
20781
|
+
}
|
|
20782
|
+
peek.addEventListener("click", function () {
|
|
20783
|
+
var pw = input.type === "password";
|
|
20784
|
+
input.type = pw ? "text" : "password";
|
|
20785
|
+
peek.innerHTML = '<svg viewBox="0 0 24 24">' + (pw ? ${JSON.stringify(EYE_OFF_SVG)} : ${JSON.stringify(EYE_SVG)}) + "</svg>";
|
|
20786
|
+
peek.setAttribute("aria-label", pw ? "\u9690\u85CF\u4EE4\u724C" : "\u663E\u793A\u4EE4\u724C");
|
|
20787
|
+
input.focus();
|
|
20788
|
+
});
|
|
20789
|
+
input.addEventListener("input", function () {
|
|
20790
|
+
if (errEl.style.display !== "none") { errEl.style.display = "none"; form.classList.remove("bad"); }
|
|
20791
|
+
});
|
|
20792
|
+
|
|
20793
|
+
form.addEventListener("submit", function (ev) {
|
|
20246
20794
|
ev.preventDefault();
|
|
20247
|
-
|
|
20248
|
-
|
|
20795
|
+
var token = normalize(input.value);
|
|
20796
|
+
if (!token) { fail("\u8BF7\u8F93\u5165\u8BBF\u95EE\u4EE4\u724C\u3002"); return; }
|
|
20797
|
+
goBtn.disabled = true;
|
|
20798
|
+
goBtn.innerHTML = '<span class="spin"></span>\u9A8C\u8BC1\u4E2D\u2026';
|
|
20799
|
+
var hdr = { headers: { authorization: "Bearer " + token } };
|
|
20800
|
+
// Prefer the renewal endpoint (managed mode returns the canonical token,
|
|
20801
|
+
// also upgrading a pasted in-grace token). Fall back to a cheap
|
|
20802
|
+
// authenticated probe so we can reject a bad token *inline* instead of
|
|
20803
|
+
// storing it and reloading into another gate.
|
|
20804
|
+
fetch("${RENEW_PATH}", hdr)
|
|
20805
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
20806
|
+
.then(function (d) {
|
|
20807
|
+
if (d && d.token) { save(d.token); location.reload(); return; }
|
|
20808
|
+
return fetch("/api/version", hdr).then(function (r) {
|
|
20809
|
+
if (r.status === 401) { fail("\u4EE4\u724C\u65E0\u6548,\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002"); return; }
|
|
20810
|
+
// 200 (static-token mode) or no UI (404) \u2192 trust it and reload.
|
|
20811
|
+
save(token); location.reload();
|
|
20812
|
+
});
|
|
20813
|
+
})
|
|
20814
|
+
.catch(function () { save(token); location.reload(); }); // network hiccup \u2192 old behavior
|
|
20249
20815
|
});
|
|
20250
|
-
|
|
20251
|
-
//
|
|
20252
|
-
//
|
|
20816
|
+
|
|
20817
|
+
// Silent renewal on load: a held token may just need swapping for the current
|
|
20818
|
+
// one (rotation while this browser held the in-grace previous). Show the boot
|
|
20819
|
+
// loader meanwhile; drop to the form (with a hint) if it can't be renewed.
|
|
20253
20820
|
var stored = localStorage.getItem(KEY);
|
|
20254
20821
|
if (stored) {
|
|
20255
20822
|
fetch("${RENEW_PATH}", { headers: { authorization: "Bearer " + stored } })
|
|
20256
20823
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
20257
|
-
.then(function (d) {
|
|
20258
|
-
|
|
20824
|
+
.then(function (d) {
|
|
20825
|
+
if (d && d.token) { save(d.token); location.reload(); }
|
|
20826
|
+
else showForm("\u767B\u5F55\u5DF2\u8FC7\u671F,\u8BF7\u91CD\u65B0\u8F93\u5165\u4EE4\u724C\u3002");
|
|
20827
|
+
})
|
|
20828
|
+
.catch(function () { showForm(""); });
|
|
20259
20829
|
} else {
|
|
20260
|
-
showForm();
|
|
20830
|
+
showForm("");
|
|
20261
20831
|
}
|
|
20262
20832
|
</script>
|
|
20263
20833
|
</body></html>`;
|
|
@@ -20529,6 +21099,11 @@ var styles_default = ` :root {
|
|
|
20529
21099
|
svg { display: block; }
|
|
20530
21100
|
.ico { width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round; flex:none; }
|
|
20531
21101
|
.ico.sm { width:14px; height:14px; }
|
|
21102
|
+
/* Every window (incl. quicknote) gets the same thin translucent scrollbar:
|
|
21103
|
+
transparent track, rounded translucent thumb. In Chromium 121+ the
|
|
21104
|
+
inherited html scrollbar-width/color renders it; the ::-webkit rules
|
|
21105
|
+
cover Safari. Both force classic (layout-consuming) mode \u2014 tight layouts
|
|
21106
|
+
pair scrollbar-gutter:stable with rebalanced insets (see .qn-body). */
|
|
20532
21107
|
::-webkit-scrollbar { width:10px; height:10px; }
|
|
20533
21108
|
::-webkit-scrollbar-thumb { background:var(--line-strong); border-radius:8px; border:3px solid transparent; background-clip:padding-box; }
|
|
20534
21109
|
::-webkit-scrollbar-thumb:hover { background:var(--muted); background-clip:padding-box; border:3px solid transparent; }
|
|
@@ -20577,12 +21152,45 @@ var styles_default = ` :root {
|
|
|
20577
21152
|
.sb-search input { border:0; background:none; outline:none; color:var(--fg); width:100%; }
|
|
20578
21153
|
.sb-search kbd { font-family:var(--mono); font-size:10px; color:var(--muted); border:1px solid var(--line-strong); border-radius:4px; padding:1px 4px; }
|
|
20579
21154
|
.sb-scroll { flex:1; overflow-y:auto; padding:4px 8px 24px; }
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
.sb-
|
|
20585
|
-
.sb-
|
|
21155
|
+
/* Notion-style list switcher: the active tab is an icon+label pill, inactive
|
|
21156
|
+
tabs are icon-only. One absolutely-positioned indicator slides & stretches
|
|
21157
|
+
under the active button; JS keeps it in sync via ResizeObserver so it
|
|
21158
|
+
chases the label's 0fr\u21921fr expansion instead of jumping to a stale width. */
|
|
21159
|
+
.sb-tabs { position:relative; display:flex; align-items:center; gap:2px; margin:2px 12px 8px; }
|
|
21160
|
+
.sb-tab { position:relative; z-index:1; display:flex; align-items:center;
|
|
21161
|
+
padding:5px 7px; border-radius:var(--radius-sm); color:var(--muted); cursor:pointer;
|
|
21162
|
+
font-size:var(--fs-ui); transition:color .18s; }
|
|
21163
|
+
.sb-tab:hover { color:var(--fg-soft); }
|
|
21164
|
+
.sb-tab.on { color:var(--fg); font-weight:600; }
|
|
21165
|
+
/* Label expansion: animating the grid track times correctly for any label
|
|
21166
|
+
width (max-width can't); the inner span clips during the ride. The
|
|
21167
|
+
icon-text gap rides the same curve so collapsing leaves no seam. */
|
|
21168
|
+
.sb-tab .tab-label { display:grid; grid-template-columns:0fr; opacity:0;
|
|
21169
|
+
transition:grid-template-columns .24s cubic-bezier(.22,1,.36,1), opacity .18s ease; }
|
|
21170
|
+
.sb-tab .tab-label > span { overflow:hidden; white-space:nowrap; min-width:0;
|
|
21171
|
+
transition:padding-left .24s cubic-bezier(.22,1,.36,1); }
|
|
21172
|
+
.sb-tab.on .tab-label { grid-template-columns:1fr; opacity:1; }
|
|
21173
|
+
.sb-tab.on .tab-label > span { padding-left:6px; }
|
|
21174
|
+
.sb-tab-ind { position:absolute; left:0; top:0; bottom:0; z-index:0; width:0;
|
|
21175
|
+
border-radius:var(--radius-sm); background:var(--hover-2);
|
|
21176
|
+
transition:transform .24s cubic-bezier(.22,1,.36,1), width .24s cubic-bezier(.22,1,.36,1); }
|
|
21177
|
+
.sb-tabs .add { margin-left:auto; padding:3px; border-radius:5px; color:var(--muted); }
|
|
21178
|
+
.sb-tabs .add:hover { background:var(--hover-2); color:var(--fg); }
|
|
21179
|
+
/* Pane swap: the entering list slides in from the side its tab lives on. */
|
|
21180
|
+
.sb-pane { margin-top:4px; }
|
|
21181
|
+
.sb-pane.in-r { animation:sb-pane-r .2s cubic-bezier(.22,1,.36,1); }
|
|
21182
|
+
.sb-pane.in-l { animation:sb-pane-l .2s cubic-bezier(.22,1,.36,1); }
|
|
21183
|
+
@keyframes sb-pane-r { from { opacity:0; transform:translateX(14px); } }
|
|
21184
|
+
@keyframes sb-pane-l { from { opacity:0; transform:translateX(-14px); } }
|
|
21185
|
+
.sb-subfold .chev { transition:transform .12s; }
|
|
21186
|
+
.sb-subfold .chev.open { transform:rotate(90deg); }
|
|
21187
|
+
/* Collapsed-databases group: a quiet tail row inside the \u6570\u636E\u8868 list for
|
|
21188
|
+
tables flagged meta.collapsed (site-facing / rarely browsed). */
|
|
21189
|
+
.sb-subfold { display:flex; align-items:center; gap:2px; width:100%; text-align:left;
|
|
21190
|
+
padding:4px 6px; margin-top:2px; border-radius:var(--radius-sm);
|
|
21191
|
+
color:var(--muted); font-size:var(--fs-micro); cursor:pointer; }
|
|
21192
|
+
.sb-subfold:hover { background:var(--hover-2); color:var(--fg-soft); }
|
|
21193
|
+
.navitem.dim { opacity:.62; }
|
|
20586
21194
|
.navitem { display:flex; align-items:center; gap:3px; width:100%; text-align:left; padding:5px 6px; margin-bottom:2px; border-radius:var(--radius-sm);
|
|
20587
21195
|
color:var(--fg-soft); font-size:var(--fs-ui); position:relative; white-space:nowrap; transition:background .1s; }
|
|
20588
21196
|
.navitem:hover { background:var(--hover-2); }
|
|
@@ -20672,6 +21280,23 @@ var styles_default = ` :root {
|
|
|
20672
21280
|
.set-block-title { font-size:14px; font-weight:650; color:var(--fg); letter-spacing:-.005em; }
|
|
20673
21281
|
.set-block-desc { color:var(--fg-soft); font-size:var(--fs-sm); line-height:1.55; margin-bottom:14px; max-width:56ch; }
|
|
20674
21282
|
.set-block-desc:last-child { margin-bottom:0; }
|
|
21283
|
+
/* title+desc on the left, a control (e.g. a switch) pinned right \u2014 used for
|
|
21284
|
+
single boolean settings where a whole card of chrome would be overkill */
|
|
21285
|
+
.set-toggle-row { display:flex; align-items:center; justify-content:space-between; gap:24px; }
|
|
21286
|
+
.set-toggle-text { min-width:0; }
|
|
21287
|
+
.set-toggle-text .set-block-head { margin-bottom:4px; }
|
|
21288
|
+
.set-toggle-text .set-block-desc { margin-bottom:0; }
|
|
21289
|
+
/* iOS-style switch: a hidden checkbox drives a sliding track+thumb */
|
|
21290
|
+
.set-switch { position:relative; flex:none; display:inline-flex; align-items:center; cursor:pointer; }
|
|
21291
|
+
.set-switch input { position:absolute; width:1px; height:1px; opacity:0; margin:0; }
|
|
21292
|
+
.set-switch-track { display:inline-flex; align-items:center; width:40px; height:23px; padding:0 2px;
|
|
21293
|
+
border-radius:999px; background:var(--line-strong); transition:background .2s ease; }
|
|
21294
|
+
.set-switch-thumb { width:19px; height:19px; border-radius:50%; background:#fff;
|
|
21295
|
+
box-shadow:0 1px 3px rgba(20,20,40,.28); transition:transform .2s cubic-bezier(.4,0,.2,1); }
|
|
21296
|
+
.set-switch input:checked + .set-switch-track { background:var(--accent); }
|
|
21297
|
+
.set-switch input:checked + .set-switch-track .set-switch-thumb { transform:translateX(17px); }
|
|
21298
|
+
.set-switch:hover .set-switch-track { filter:brightness(.97); }
|
|
21299
|
+
.set-switch input:focus-visible + .set-switch-track { outline:2px solid var(--accent); outline-offset:2px; }
|
|
20675
21300
|
/* two-fact meta line under a block title (who publishes \xB7 where creds live) \u2014
|
|
20676
21301
|
answers the questions hiding under "is this bucket for server or browser?" */
|
|
20677
21302
|
.set-meta { display:flex; flex-wrap:wrap; gap:8px 16px; margin:0 0 12px; }
|
|
@@ -20871,10 +21496,19 @@ var styles_default = ` :root {
|
|
|
20871
21496
|
.sidebar.collapsed { overflow:hidden; }
|
|
20872
21497
|
.sidebar.collapsed .sb-resizer { pointer-events:none; }
|
|
20873
21498
|
.crumb { display:flex; align-items:center; gap:7px; flex:1; min-width:0; font-size:var(--fs-ui); color:var(--fg); font-weight:500; }
|
|
20874
|
-
|
|
21499
|
+
/* Long titles ellipsize on one line \u2014 never wrap and grow the topbar. */
|
|
21500
|
+
.crumb > span:not(.emoji) { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
21501
|
+
.crumb .emoji { font-size:15px; flex:none; } .crumb .sub { color:var(--muted); font-weight:400; }
|
|
20875
21502
|
.iconbtn { width:30px; height:30px; display:grid; place-items:center; border-radius:var(--radius-sm); color:var(--fg-soft); transition:background .12s; }
|
|
20876
21503
|
.iconbtn:hover { background:var(--hover-2); color:var(--fg); }
|
|
20877
|
-
|
|
21504
|
+
/* Reserve the classic-scrollbar gutter up front: view width never changes
|
|
21505
|
+
when content grows past one screen \u2014 no jump / rewrap flicker. stable
|
|
21506
|
+
ONLY (never both-edges): a left-edge gutter desyncs Chromium's composited
|
|
21507
|
+
scroll cull rect by the gutter width, leaving a permanent unpainted
|
|
21508
|
+
stripe over the rightmost 15px of the scrollport (content reaching into
|
|
21509
|
+
it gets visibly sliced \u2014 first seen slicing the quicknote file card).
|
|
21510
|
+
Inert on mobile, where .content becomes overflow:visible. */
|
|
21511
|
+
.content { flex:1; overflow:auto; scrollbar-gutter:stable; }
|
|
20878
21512
|
.btn { display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border-radius:var(--radius-sm); font-size:13px; font-weight:500; transition:all .12s; white-space:nowrap; }
|
|
20879
21513
|
.btn-ghost { color:var(--fg-soft); } .btn-ghost:hover { background:var(--hover-2); color:var(--fg); }
|
|
20880
21514
|
.btn-secondary { background:var(--bg); border:1px solid var(--line-strong); color:var(--fg); box-shadow:var(--shadow-sm); }
|
|
@@ -20939,9 +21573,9 @@ var styles_default = ` :root {
|
|
|
20939
21573
|
.doc-conflict { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:10px 14px; margin-bottom:16px; border:1px solid var(--danger); border-radius:var(--radius-sm); background:var(--danger-soft); font-size:13px; }
|
|
20940
21574
|
.doc-conflict-msg { color:var(--danger); font-weight:600; flex:1; min-width:180px; }
|
|
20941
21575
|
.doc.source-mode { min-height:100%; }
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
21576
|
+
/* wide mode (\u22EF menu): lift the column cap; the 60px side padding stays as the
|
|
21577
|
+
reading gutter and keeps the fixed .doc-toc ticks off the text. */
|
|
21578
|
+
.doc.wide-mode { max-width:none; }
|
|
20945
21579
|
/* floating table of contents: a column of indented tick marks in the right
|
|
20946
21580
|
gutter that expands to a labelled list on hover (Notion / Vercel-docs style).
|
|
20947
21581
|
Fixed to the viewport so it never disturbs the centred .doc column. */
|
|
@@ -20980,49 +21614,33 @@ var styles_default = ` :root {
|
|
|
20980
21614
|
/* not enough room for the gutter \u2192 hide entirely (also covers mobile) */
|
|
20981
21615
|
@media (max-width:1100px) { .doc-toc { display:none; } }
|
|
20982
21616
|
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20993
|
-
|
|
20994
|
-
.
|
|
20995
|
-
|
|
20996
|
-
.
|
|
20997
|
-
|
|
20998
|
-
.
|
|
20999
|
-
|
|
21000
|
-
|
|
21001
|
-
.
|
|
21002
|
-
|
|
21003
|
-
|
|
21004
|
-
.
|
|
21005
|
-
.
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
code block's own gutter (it would overlap the list marker on the same row). */
|
|
21009
|
-
.block.list-code-host + .block-wrap.nested .gutter { display:none; }
|
|
21010
|
-
.editable { outline:none; flex:1; min-width:0; padding:3px 2px; line-height:var(--note-lh); }
|
|
21011
|
-
.editable:empty::before { content:attr(data-ph); color:var(--muted); pointer-events:none; }
|
|
21012
|
-
/* The generic "/" prompt only shows on the focused line; idle blank lines (paragraphs and list items, any depth) stay blank. */
|
|
21013
|
-
.editable[data-ph-hint="slash"]:empty:not(:focus)::before { content:""; }
|
|
21014
|
-
.b-h1 .editable { font-size:28px; font-weight:700; letter-spacing:-.02em; padding-top:12px; }
|
|
21015
|
-
.b-h2 .editable { font-size:22px; font-weight:650; letter-spacing:-.01em; padding-top:8px; }
|
|
21016
|
-
.b-h3 .editable { font-size:18px; font-weight:600; padding-top:4px; }
|
|
21017
|
-
.b-h4 .editable { font-size:16px; font-weight:600; padding-top:2px; }
|
|
21018
|
-
.b-h5 .editable { font-size:14px; font-weight:600; }
|
|
21019
|
-
.b-h6 .editable { font-size:13px; font-weight:600; color:var(--fg-soft); }
|
|
21020
|
-
.b-h1 .gutter { top:22px; } .b-h2 .gutter { top:14px; } .b-h3 .gutter { top:7px; }
|
|
21021
|
-
.b-h4 .gutter { top:4px; } .b-h5 .gutter { top:3px; } .b-h6 .gutter { top:2px; }
|
|
21022
|
-
.b-quote .editable { border-left:3px solid var(--line-strong); padding:1px 0 1px 16px; color:var(--fg-soft); font-style:italic; }
|
|
21617
|
+
/* floating word count: a small pill in the bottom-right gutter (fixed to the
|
|
21618
|
+
viewport, aligned with .doc-toc's right edge). Collapsed it hugs the \u5B57\u6570;
|
|
21619
|
+
hover expands the whole pill (square-off + surface bg) and reveals the char
|
|
21620
|
+
count + reading estimate. Centre-aligned throughout, and the collapsed detail
|
|
21621
|
+
is width:0 so its wider text can't stretch the pill. Purely client-side. */
|
|
21622
|
+
.doc-wc { position:fixed; right:18px; bottom:calc(16px + env(safe-area-inset-bottom)); z-index:40;
|
|
21623
|
+
display:flex; flex-direction:column; align-items:center; justify-content:center;
|
|
21624
|
+
padding:4px 12px; border:1px solid var(--line); border-radius:999px; background:var(--bg);
|
|
21625
|
+
color:var(--muted); font-size:var(--fs-sm); line-height:1.2; user-select:none;
|
|
21626
|
+
box-shadow:var(--shadow-sm); opacity:.66;
|
|
21627
|
+
transition:opacity .18s ease, border-radius .2s ease, background .18s ease, box-shadow .2s ease, color .18s ease; }
|
|
21628
|
+
.doc-wc:hover { opacity:1; border-radius:var(--radius); background:var(--surface); border-color:var(--line-strong);
|
|
21629
|
+
box-shadow:var(--shadow-md); color:var(--fg-soft); }
|
|
21630
|
+
.doc-wc-num { font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
21631
|
+
.doc-wc.sel .doc-wc-num { color:var(--accent); font-weight:550; }
|
|
21632
|
+
/* detail rows: collapsed to zero size until hover. width:0 keeps them from
|
|
21633
|
+
widening the pill; no container gap (a phantom gap would push the collapsed
|
|
21634
|
+
count off-centre) \u2014 the spacing comes from margin-top only when expanded. */
|
|
21635
|
+
.doc-wc-detail { display:flex; flex-direction:column; align-items:center; gap:2px;
|
|
21636
|
+
width:0; max-height:0; margin-top:0; overflow:hidden; opacity:0; font-size:12px; color:var(--muted);
|
|
21637
|
+
transition:max-height .2s ease, opacity .18s ease, margin-top .2s ease; }
|
|
21638
|
+
.doc-wc-detail span { font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
21639
|
+
.doc-wc:hover .doc-wc-detail { width:auto; max-height:40px; margin-top:4px; opacity:1; }
|
|
21640
|
+
/* quick-note mini window has no room for it */
|
|
21641
|
+
.qn-body .doc-wc { display:none; }
|
|
21023
21642
|
|
|
21024
21643
|
/* code block: transparent textarea over a highlight.js mirror */
|
|
21025
|
-
.b-code .codeblock { flex:1; min-width:0; }
|
|
21026
21644
|
.codeblock { position:relative; --code-fs:12.5px; --code-lh:1.55; --code-pad:8px; margin:2px 0;
|
|
21027
21645
|
background:var(--code-bg); border:1px solid var(--line); border-radius:8px; overflow:hidden; box-shadow:var(--shadow-sm); }
|
|
21028
21646
|
/* lang + copy float bottom-right, revealed on hover */
|
|
@@ -21031,27 +21649,57 @@ var styles_default = ` :root {
|
|
|
21031
21649
|
background:color-mix(in srgb,var(--code-bg) 82%,transparent); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
|
|
21032
21650
|
opacity:0; transform:translateY(3px); pointer-events:none; transition:opacity .12s,transform .12s; }
|
|
21033
21651
|
.codeblock:hover .code-tools, .code-tools:focus-within { opacity:1; transform:none; pointer-events:auto; }
|
|
21652
|
+
/* hidden while editing (textarea focused) or whole-block selected \u2014 the pill would cover the
|
|
21653
|
+
bottom-right of the editing area; keep it if focus is on the pill itself (lang dropdown open) */
|
|
21654
|
+
.cm-void-code:focus-within .code-tools:not(:focus-within),
|
|
21655
|
+
.codeblock.ci-selected .code-tools:not(:focus-within) { opacity:0; transform:translateY(3px); pointer-events:none; }
|
|
21034
21656
|
.code-lang { position:relative; display:inline-flex; align-items:center; }
|
|
21035
21657
|
.code-lang select { appearance:none; -webkit-appearance:none; border:0; outline:none; background:none; cursor:pointer;
|
|
21036
21658
|
font-family:var(--mono); font-size:10.5px; font-weight:500; color:var(--fg-soft); padding:3px 17px 3px 7px; border-radius:5px; }
|
|
21037
21659
|
.code-lang select:hover { background:var(--hover-2); }
|
|
21038
21660
|
.code-lang svg { position:absolute; right:3px; width:12px; height:12px; pointer-events:none; color:var(--muted); }
|
|
21039
|
-
.code-copy { display:inline-flex; align-items:center; gap:4px; font-size:10.5px; font-weight:500; color:var(--muted);
|
|
21040
|
-
padding:3px 7px; border-radius:5px; transition:background .12s,color .12s; }
|
|
21041
|
-
.code-copy:hover { background:var(--hover-2); color:var(--fg-soft); }
|
|
21042
|
-
.code-copy.ok { color:var(--accent); }
|
|
21043
|
-
.code-
|
|
21661
|
+
.code-copy, .code-fmt, .code-wrap-btn { display:inline-flex; align-items:center; gap:4px; font-size:10.5px; font-weight:500; color:var(--muted);
|
|
21662
|
+
padding:3px 7px; border-radius:5px; transition:background .12s,color .12s; white-space:nowrap; }
|
|
21663
|
+
.code-copy:hover, .code-fmt:hover, .code-wrap-btn:hover { background:var(--hover-2); color:var(--fg-soft); }
|
|
21664
|
+
.code-copy.ok, .code-fmt.ok, .code-wrap-btn.on { color:var(--accent); }
|
|
21665
|
+
.code-fmt.err { color:var(--danger); }
|
|
21666
|
+
/* Default (no wrap): lines run long, the textarea scrolls horizontally and
|
|
21667
|
+
mirrors its scrollLeft onto the highlight layer; the gutter is a separate
|
|
21668
|
+
flex column of "1\\n2\\n3" (every logical line is exactly one row high). */
|
|
21669
|
+
.code-body { display:flex; align-items:stretch; font-family:var(--mono); font-size:var(--code-fs); line-height:var(--code-lh);
|
|
21670
|
+
--code-ln-ch:1ch; --code-gutter-w:calc(var(--code-ln-ch) + 20px); }
|
|
21044
21671
|
.code-gutter { flex:none; text-align:right; white-space:pre; user-select:none; color:var(--muted); opacity:.55;
|
|
21045
21672
|
padding:var(--code-pad) 8px var(--code-pad) 12px; border-right:1px solid var(--line); font-variant-numeric:tabular-nums; }
|
|
21046
21673
|
.code-scroll { position:relative; flex:1; min-width:0; }
|
|
21047
21674
|
.code-hl, .code-input { margin:0; font-family:var(--mono); font-size:var(--code-fs); line-height:var(--code-lh);
|
|
21048
21675
|
padding:var(--code-pad) 16px; tab-size:2; -moz-tab-size:2; white-space:pre; letter-spacing:0; }
|
|
21049
21676
|
.code-hl { position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none; background:none; color:var(--fg); }
|
|
21050
|
-
.code-hl code { display:block; font:inherit; background:none; padding:0; }
|
|
21677
|
+
.code-hl code { display:block; font:inherit; background:none; padding:0; counter-reset:code-ln; }
|
|
21678
|
+
.code-hl-line { counter-increment:code-ln;
|
|
21679
|
+
min-height:calc(var(--code-fs) * var(--code-lh)); /* empty logical line still occupies its row */ }
|
|
21051
21680
|
.code-input { display:block; position:relative; z-index:1; width:100%; border:0; outline:none; resize:none;
|
|
21052
21681
|
overflow-x:auto; overflow-y:hidden; background:transparent; color:transparent; caret-color:var(--fg);
|
|
21053
21682
|
min-height:calc(var(--code-fs) * var(--code-lh) + var(--code-pad) * 2); }
|
|
21054
21683
|
.code-input::placeholder { color:var(--muted); }
|
|
21684
|
+
/* Wrap mode (.wrap, global toggle in the tools pill, persisted): textarea and
|
|
21685
|
+
mirror share pre-wrap + break-word (the UA textarea wrap rules), so their
|
|
21686
|
+
wrap points match and the caret stays on the glyphs. The flex gutter would
|
|
21687
|
+
misalign (a logical line can span several visual rows), so it hides and
|
|
21688
|
+
line numbers become CSS counters on the mirror's per-logical-line blocks \u2014
|
|
21689
|
+
a wrapped line grows its block and the next number moves with it, no JS
|
|
21690
|
+
height sync. --code-ln-ch (digit count) is set by paint(). */
|
|
21691
|
+
.code-body.wrap { display:block; position:relative; }
|
|
21692
|
+
.code-body.wrap::before { content:""; position:absolute; left:var(--code-gutter-w); top:0; bottom:0;
|
|
21693
|
+
width:1px; background:var(--line); }
|
|
21694
|
+
.code-body.wrap .code-gutter { display:none; }
|
|
21695
|
+
.code-body.wrap .code-hl, .code-body.wrap .code-input {
|
|
21696
|
+
padding-left:calc(var(--code-gutter-w) + 17px);
|
|
21697
|
+
white-space:pre-wrap; overflow-wrap:break-word; }
|
|
21698
|
+
.code-body.wrap .code-input { overflow:hidden; }
|
|
21699
|
+
.code-body.wrap .code-hl-line { position:relative; }
|
|
21700
|
+
.code-body.wrap .code-hl-line::before { content:counter(code-ln); position:absolute; top:0;
|
|
21701
|
+
left:calc(-17px - var(--code-gutter-w)); width:calc(var(--code-gutter-w) - 8px);
|
|
21702
|
+
text-align:right; color:var(--muted); opacity:.55; font-variant-numeric:tabular-nums; }
|
|
21055
21703
|
|
|
21056
21704
|
/* highlight.js token theme (cohesive with the app palette) */
|
|
21057
21705
|
.hljs-comment, .hljs-quote { color:var(--hl-cmt); font-style:italic; }
|
|
@@ -21065,24 +21713,50 @@ var styles_default = ` :root {
|
|
|
21065
21713
|
.hljs-params, .hljs-subst { color:var(--fg); }
|
|
21066
21714
|
.hljs-emphasis { font-style:italic; }
|
|
21067
21715
|
.hljs-strong { font-weight:700; }
|
|
21068
|
-
.marker { width:24px; flex:none; text-align:center; color:var(--fg-soft); user-select:none; padding-top:4px; }
|
|
21069
|
-
.b-todo .marker { box-sizing:content-box; padding-top:3px; height:calc(1em * var(--note-lh));
|
|
21070
|
-
display:flex; align-items:center; justify-content:center; }
|
|
21071
|
-
.b-todo .marker input { width:15px; height:15px; accent-color:var(--accent); cursor:pointer; }
|
|
21072
|
-
.b-divider { padding:10px 0; }
|
|
21073
|
-
.b-divider hr { border:0; border-top:1px solid var(--line-strong); margin:0; width:100%; }
|
|
21074
|
-
.b-done .editable { color:var(--muted); text-decoration:line-through; }
|
|
21075
|
-
.editable b, .editable strong { font-weight:700; }
|
|
21076
|
-
.editable code { font-family:var(--mono); background:var(--surface-2); padding:1px 5px; border-radius:4px; font-size:.9em; }
|
|
21077
|
-
.editable a { color:var(--accent); }
|
|
21078
|
-
/* document images (pasted/dropped \u2192 /blob/<hash>); fetched on demand */
|
|
21079
|
-
.editable img.doc-img { max-width:100%; height:auto; border-radius:8px; margin:6px 0; display:block; vertical-align:bottom; }
|
|
21080
21716
|
|
|
21081
|
-
/*
|
|
21082
|
-
|
|
21083
|
-
|
|
21084
|
-
|
|
21717
|
+
/* CodeIsland (CM6 void widget): the tools pill (lang + copy) is the only
|
|
21718
|
+
focus-state chrome \u2014 an absolute overlay, so revealing it never changes the
|
|
21719
|
+
block's height (no layout shift on click). .ci-selected marks the island
|
|
21720
|
+
when the CM selection spans the whole void (Escape from inside / Backspace
|
|
21721
|
+
from the line below). */
|
|
21722
|
+
.cm-void-code .codeblock.ci-selected { box-shadow:0 0 0 2px var(--accent); }
|
|
21723
|
+
|
|
21724
|
+
/* Whole-void selected (the CM selection spans the block \u2014 click-to-select on
|
|
21725
|
+
media, Backspace-from-below, select-all): 2px accent ring, the old editor's
|
|
21726
|
+
\`.block.selected .void-block\` treatment. Code draws its own via .ci-selected. */
|
|
21727
|
+
/* Intrinsic-width firewall (same problem the doc table solved with
|
|
21728
|
+
contain:inline-size on .doc-table-wrap): a void widget's max-content \u2014
|
|
21729
|
+
e.g. a file card whose nowrap filename caps at 480px \u2014 must not propagate
|
|
21730
|
+
into .cm-content and push the whole editor wider than a narrow window
|
|
21731
|
+
(quick notes), shoving the card's right edge offscreen. width:0 zeroes the
|
|
21732
|
+
host's intrinsic contribution; min-width:100% restores the line width at
|
|
21733
|
+
layout time; the card's own min-width:0 + ellipsis then get to run.
|
|
21734
|
+
NOT contain:inline-size: Chromium 130's ink-overflow culling clips a
|
|
21735
|
+
size-contained host's paint at a stale boundary on 2x displays \u2014 the
|
|
21736
|
+
card's right border and selection ring were visibly sliced off. */
|
|
21737
|
+
.cm-void { box-sizing:border-box; width:0; min-width:100%; }
|
|
21738
|
+
.cm-void.selected .void-block { box-shadow:0 0 0 2px var(--accent); }
|
|
21739
|
+
.cm-void.selected table.doc-table { box-shadow:0 0 0 2px var(--accent), var(--shadow-sm); }
|
|
21740
|
+
|
|
21741
|
+
/* Editor text selection: one accent tint for BOTH the native ::selection and
|
|
21742
|
+
.cm-mk-sel, the class block-deco puts on marker widgets (bullet / ordered
|
|
21743
|
+
number / checkbox) whose marker range the selection covers. Native selection
|
|
21744
|
+
cannot paint replaced widgets, which left holes in the marker column during
|
|
21745
|
+
drag-select / select-all; unifying the two colors makes the line read as one
|
|
21746
|
+
continuous highlight (and tracks the theme in dark mode). */
|
|
21747
|
+
.cm-content ::selection { background: color-mix(in srgb, var(--accent) 24%, transparent); }
|
|
21748
|
+
.cm-content .cm-mk-sel { background: color-mix(in srgb, var(--accent) 24%, transparent); }
|
|
21749
|
+
/* bullet/number boxes are 24px + 2px margin-right; the margin can't take a
|
|
21750
|
+
background, so a same-color shadow fills the seam up to the content column */
|
|
21751
|
+
.cm-content .cm-bullet.cm-mk-sel, .cm-content .cm-ol-num.cm-mk-sel {
|
|
21752
|
+
box-shadow: 2px 0 0 color-mix(in srgb, var(--accent) 24%, transparent);
|
|
21753
|
+
}
|
|
21754
|
+
/* the glyph/number text inside a painted marker also sits in the DOM selection;
|
|
21755
|
+
without this it gets the ::selection tint ON TOP of the span background and
|
|
21756
|
+
reads darker than the rest of the line */
|
|
21757
|
+
.cm-content .cm-mk-sel::selection, .cm-content .cm-mk-sel ::selection { background: transparent; }
|
|
21085
21758
|
|
|
21759
|
+
/* ---- block-level void embeds: image / video / audio / file / html ---- */
|
|
21086
21760
|
.void-image { position:relative; display:inline-block; max-width:100%; border-radius:8px; line-height:0; }
|
|
21087
21761
|
.void-image img { display:block; width:100%; max-width:100%; height:auto; border-radius:8px; user-select:none; }
|
|
21088
21762
|
.void-image .void-expand { position:absolute; top:8px; right:8px; width:28px; height:28px; display:grid; place-items:center;
|
|
@@ -21120,7 +21794,6 @@ var styles_default = ` :root {
|
|
|
21120
21794
|
color:var(--fg-soft); font-size:var(--fs-micro); transition:background .12s; }
|
|
21121
21795
|
.void-html .html-toggle:hover { background:var(--hover-2); color:var(--fg); }
|
|
21122
21796
|
.void-html .html-toggle.on { background:var(--accent-soft); color:var(--accent); }
|
|
21123
|
-
.void-html .html-source { width:100%; min-height:120px; padding:12px; border:0; background:var(--bg); resize:vertical; }
|
|
21124
21797
|
.void-html .html-frame { display:block; width:100%; border:0; background:var(--bg); }
|
|
21125
21798
|
.void-html .html-empty { padding:24px; text-align:center; color:var(--muted); cursor:text; font-size:var(--fs-ui); }
|
|
21126
21799
|
|
|
@@ -21131,30 +21804,47 @@ var styles_default = ` :root {
|
|
|
21131
21804
|
.lightbox-toolbar { flex:none; display:flex; align-items:center; gap:6px; padding:10px 14px; color:#fff; }
|
|
21132
21805
|
.lightbox-toolbar .lightbox-name { font-size:var(--fs-ui); opacity:.85; max-width:40vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
21133
21806
|
.lightbox-toolbar .lightbox-spacer { flex:1; }
|
|
21134
|
-
.lightbox-toolbar button { width:34px; height:34px; display:grid; place-items:center; border-radius:8px; color:#fff;
|
|
21807
|
+
.lightbox-toolbar > button { width:34px; height:34px; display:grid; place-items:center; border-radius:8px; color:#fff;
|
|
21135
21808
|
background:rgba(255,255,255,.08); transition:background .12s; }
|
|
21136
|
-
.lightbox-toolbar button:hover { background:rgba(255,255,255,.2); }
|
|
21809
|
+
.lightbox-toolbar > button:hover { background:rgba(255,255,255,.2); }
|
|
21137
21810
|
.lightbox-toolbar .lightbox-pct { width:auto; min-width:52px; font-size:var(--fs-micro); font-variant-numeric:tabular-nums; }
|
|
21811
|
+
.lightbox-toolbar .ico { width:16px; height:16px; stroke-width:2; }
|
|
21138
21812
|
.lightbox-stage { flex:1; min-height:0; overflow:hidden; display:grid; place-items:center; }
|
|
21139
21813
|
.lightbox-stage img { max-width:94vw; max-height:100%; will-change:transform; -webkit-user-drag:none; user-select:none; }
|
|
21140
21814
|
|
|
21141
21815
|
/* ---- image annotator ---- */
|
|
21142
21816
|
.annot { flex:1; min-height:0; display:flex; flex-direction:column; }
|
|
21143
|
-
|
|
21144
|
-
|
|
21145
|
-
.annot-
|
|
21146
|
-
.annot-
|
|
21147
|
-
.annot-
|
|
21148
|
-
|
|
21149
|
-
.annot-
|
|
21150
|
-
.annot-
|
|
21151
|
-
.annot-
|
|
21817
|
+
/* Single-row chrome: filename \xB7 segmented tools \xB7 colors \xB7 undo/redo \xB7
|
|
21818
|
+
cancel/save \xB7 close. Wraps only as a narrow-window fallback. */
|
|
21819
|
+
.annot-bar { flex-wrap:wrap; row-gap:6px; }
|
|
21820
|
+
.annot-seg { display:flex; align-items:center; gap:2px; padding:2px; border-radius:9px; background:rgba(255,255,255,.1); }
|
|
21821
|
+
.annot-seg-btn { width:30px; height:26px; display:grid; place-items:center; border-radius:7px;
|
|
21822
|
+
color:rgba(255,255,255,.72); transition:background .12s, color .12s; }
|
|
21823
|
+
.annot-seg-btn:hover { color:#fff; background:rgba(255,255,255,.12); }
|
|
21824
|
+
.annot-seg-btn.on { background:#fff; color:#1c1c26; box-shadow:0 1px 2px rgba(0,0,0,.3); }
|
|
21825
|
+
.annot-div { flex:none; width:1px; height:18px; margin:0 5px; background:rgba(255,255,255,.18); }
|
|
21826
|
+
.annot-colors { display:flex; align-items:center; gap:8px; padding:0 2px; }
|
|
21827
|
+
.annot-swatch { width:15px; height:15px; border-radius:50%; box-shadow:inset 0 0 0 1px rgba(255,255,255,.3);
|
|
21828
|
+
transition:transform .12s, box-shadow .12s; }
|
|
21829
|
+
.annot-swatch:hover { transform:scale(1.18); }
|
|
21830
|
+
.annot-swatch.on { box-shadow:inset 0 0 0 1px rgba(255,255,255,.3), 0 0 0 2px rgba(12,12,18,.9), 0 0 0 3.5px rgba(255,255,255,.92); }
|
|
21831
|
+
.annot-bar .annot-icon-btn { width:30px; height:30px; display:grid; place-items:center; border-radius:7px;
|
|
21832
|
+
color:rgba(255,255,255,.85); background:none; transition:background .12s; }
|
|
21833
|
+
.annot-bar .annot-icon-btn:hover { background:rgba(255,255,255,.14); }
|
|
21834
|
+
.annot-bar .annot-icon-btn:disabled { opacity:.35; cursor:default; background:none; }
|
|
21835
|
+
.annot-actions { display:flex; align-items:center; gap:8px; margin:0 2px 0 4px; }
|
|
21152
21836
|
.annot-stage { flex:1; min-height:0; overflow:auto; display:grid; place-items:center; padding:8px 14px 18px; }
|
|
21153
21837
|
.annot-img-wrap { position:relative; line-height:0; }
|
|
21154
21838
|
.annot-img-wrap img { display:block; max-width:90vw; max-height:78vh; width:auto; height:auto; -webkit-user-drag:none; user-select:none; }
|
|
21155
|
-
.annot-canvas { position:absolute; inset:0; width:100%; height:100%; }
|
|
21839
|
+
.annot-canvas { position:absolute; inset:0; width:100%; height:100%; touch-action:none; }
|
|
21156
21840
|
.annot-canvas.tool-text { cursor:text; }
|
|
21157
21841
|
.annot-canvas.tool-rect, .annot-canvas.tool-arrow { cursor:crosshair; }
|
|
21842
|
+
/* Inline text overlay: WYSIWYG twin of the flattened canvas text (same font,
|
|
21843
|
+
line-height and halo), plus an accent ring so the edit region reads. */
|
|
21844
|
+
.annot-text-input { position:absolute; z-index:2; min-width:60px; padding:2px 4px; margin:0; border:0; outline:none;
|
|
21845
|
+
resize:none; overflow:hidden; white-space:pre; background:transparent; border-radius:4px; caret-color:currentColor;
|
|
21846
|
+
font:600 20px/1.3 system-ui, -apple-system, sans-serif; text-shadow:0 1px 2px rgba(0,0,0,.55);
|
|
21847
|
+
box-shadow:0 0 0 1.5px var(--accent), 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent); }
|
|
21158
21848
|
|
|
21159
21849
|
/* ---- transient "uploading" placeholder + unavailable fallback ---- */
|
|
21160
21850
|
.ico.spin { animation:mh-spin .8s linear infinite; }
|
|
@@ -21163,7 +21853,10 @@ var styles_default = ` :root {
|
|
|
21163
21853
|
display:flex; align-items:center; gap:12px; margin:2px 0; max-width:480px; padding:12px 14px;
|
|
21164
21854
|
border:1px solid var(--line-strong); border-radius:var(--radius); background:var(--surface); color:var(--fg-soft); }
|
|
21165
21855
|
.void-uploading { border-style:dashed; }
|
|
21166
|
-
|
|
21856
|
+
/* min-width:0: .void-uploading's vu-name is a direct flex item \u2014 without it a
|
|
21857
|
+
long nowrap filename sets the item's min-content floor and the placeholder
|
|
21858
|
+
card overflows a narrow window instead of ellipsizing. */
|
|
21859
|
+
.void-uploading .vu-name, .void-unavailable .vu-name { font-weight:600; color:var(--fg); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
|
|
21167
21860
|
.void-uploading .vu-sub, .void-unavailable .vu-sub { font-size:var(--fs-micro); color:var(--muted); }
|
|
21168
21861
|
.void-unavailable { border-color:var(--danger-soft); color:var(--danger); }
|
|
21169
21862
|
.void-unavailable .ico { color:var(--danger); flex:none; }
|
|
@@ -21209,24 +21902,53 @@ var styles_default = ` :root {
|
|
|
21209
21902
|
/* Chrome rides the theme-tinted glass \u2192 theme-aware (var(--fg)), readable on a
|
|
21210
21903
|
light or dark tint alike. */
|
|
21211
21904
|
body.preview-window .lightbox-toolbar { color:var(--fg); }
|
|
21212
|
-
body.preview-window .lightbox-toolbar button,
|
|
21213
|
-
body.preview-window .
|
|
21214
|
-
body.preview-window .
|
|
21215
|
-
body.preview-window .annot-
|
|
21216
|
-
body.preview-window .annot-
|
|
21217
|
-
body.preview-window .annot-
|
|
21905
|
+
body.preview-window .lightbox-toolbar > button { color:var(--fg); background:color-mix(in srgb, var(--fg) 10%, transparent); }
|
|
21906
|
+
body.preview-window .lightbox-toolbar > button:hover { background:color-mix(in srgb, var(--fg) 20%, transparent); }
|
|
21907
|
+
body.preview-window .annot-seg { background:color-mix(in srgb, var(--fg) 8%, transparent); }
|
|
21908
|
+
body.preview-window .annot-seg-btn { color:color-mix(in srgb, var(--fg) 65%, transparent); }
|
|
21909
|
+
body.preview-window .annot-seg-btn:hover { color:var(--fg); background:color-mix(in srgb, var(--fg) 10%, transparent); }
|
|
21910
|
+
body.preview-window .annot-seg-btn.on { background:var(--bg); color:var(--fg); box-shadow:var(--shadow-sm); }
|
|
21911
|
+
body.preview-window .annot-div { background:color-mix(in srgb, var(--fg) 15%, transparent); }
|
|
21912
|
+
body.preview-window .annot-swatch { box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--fg) 25%, transparent); }
|
|
21913
|
+
body.preview-window .annot-swatch.on { box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--fg) 25%, transparent),
|
|
21914
|
+
0 0 0 2px var(--bg), 0 0 0 3.5px color-mix(in srgb, var(--fg) 78%, transparent); }
|
|
21915
|
+
body.preview-window .annot-bar .annot-icon-btn { color:var(--fg); background:none; }
|
|
21916
|
+
body.preview-window .annot-bar .annot-icon-btn:hover { background:color-mix(in srgb, var(--fg) 12%, transparent); }
|
|
21917
|
+
body.preview-window .annot-bar .annot-icon-btn:disabled { background:none; }
|
|
21218
21918
|
/* In the frameless window the toolbar doubles as the OS drag region; its
|
|
21219
21919
|
buttons opt back out so they stay clickable. */
|
|
21220
21920
|
.lightbox-toolbar.app-drag { -webkit-app-region:drag; }
|
|
21221
21921
|
.lightbox-toolbar.app-drag button { -webkit-app-region:no-drag; }
|
|
21222
21922
|
|
|
21223
|
-
/* document table (GFM pipe table block)
|
|
21224
|
-
|
|
21923
|
+
/* document table (GFM pipe table block). contain:inline-size zeroes the
|
|
21924
|
+
widget's intrinsic-width contribution \u2014 otherwise the inner
|
|
21925
|
+
width:max-content propagates through overflow-x:auto into .cm-content's
|
|
21926
|
+
min-width:auto floor (intrinsic sizing ignores overflow) and pushes the
|
|
21927
|
+
whole page wider than the viewport on narrow screens. With the intrinsic
|
|
21928
|
+
loop broken, .doc-table-wrap resolves to the doc column width and the
|
|
21929
|
+
scroll box below finally gets to scroll. container-type additionally
|
|
21930
|
+
unlocks cqw for the sticky add-row bar (old engines fall back to contain);
|
|
21931
|
+
position:relative anchors the gutter row handle explicitly rather than
|
|
21932
|
+
leaning on container-type's implied containing block. */
|
|
21933
|
+
.doc-table-wrap { position:relative; contain:inline-size; container-type:inline-size; }
|
|
21225
21934
|
.doc-table-scroll { overflow-x:auto; padding:2px 0; }
|
|
21226
|
-
.doc-table-inner { width:max-content; }
|
|
21935
|
+
.doc-table-inner { position:relative; width:max-content; } /* anchors the drag outline / drop lines so they track horizontal scroll */
|
|
21227
21936
|
.doc-table-row { display:flex; align-items:stretch; }
|
|
21228
21937
|
table.doc-table { border-collapse:separate; border-spacing:0; table-layout:fixed; width:max-content;
|
|
21229
21938
|
border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden; box-shadow:var(--shadow-sm); }
|
|
21939
|
+
/* Default (no manual widths yet): browser auto table layout sizes columns by
|
|
21940
|
+
content, capped to the wrap (cqw works despite the max-content .doc-table-inner;
|
|
21941
|
+
26px keeps the add-col button inside) so it wraps instead of scrolling. The
|
|
21942
|
+
first resizer drag freezes measured widths and drops this class (table-block.tsx). */
|
|
21943
|
+
table.doc-table.autofit { table-layout:auto; width:auto; max-width:calc(100cqw - 26px); }
|
|
21944
|
+
table.doc-table.autofit .doc-td { min-width:48px; } /* empty columns don't collapse */
|
|
21945
|
+
/* Code tokens are atomic: raising the column's min-content makes the auto
|
|
21946
|
+
algorithm shed width into wrappable prose columns instead of breaking
|
|
21947
|
+
\`file-names.md\` at hyphens; an over-long token falls back to the scroll
|
|
21948
|
+
box. Autofit only \u2014 in manual fixed layout nowrap can't grow the column,
|
|
21949
|
+
it would overflow the cell. Phones relax this (see the 560px block):
|
|
21950
|
+
there the atom IS the cap, and it crushes prose columns to their floor. */
|
|
21951
|
+
table.doc-table.autofit .doc-td code { white-space:nowrap; }
|
|
21230
21952
|
table.doc-table td { position:relative; vertical-align:top; padding:0;
|
|
21231
21953
|
border-right:1px solid var(--line); border-bottom:1px solid var(--line); }
|
|
21232
21954
|
table.doc-table tr td:last-child { border-right:0; }
|
|
@@ -21238,24 +21960,59 @@ var styles_default = ` :root {
|
|
|
21238
21960
|
.doc-td b, .doc-td strong { font-weight:700; }
|
|
21239
21961
|
.doc-td code { font-family:var(--mono); background:var(--surface-2); padding:1px 5px; border-radius:4px; font-size:.9em; }
|
|
21240
21962
|
.doc-td a { color:var(--accent); }
|
|
21241
|
-
/* header cell: column menu + resize handle */
|
|
21242
|
-
.doc-col-menu { position:absolute; top:4px; right:4px; width:18px; height:18px; display:grid; place-items:center;
|
|
21243
|
-
color:var(--muted); border-radius:4px; opacity:0; transition:opacity .12s,background .12s; }
|
|
21244
|
-
.doc-th:hover .doc-col-menu { opacity:1; }
|
|
21245
|
-
.doc-col-menu:hover { background:var(--hover-2); color:var(--fg); }
|
|
21246
21963
|
.doc-col-resizer { position:absolute; top:0; right:-4px; width:7px; height:100%; cursor:col-resize; z-index:3; touch-action:none; }
|
|
21247
|
-
/*
|
|
21248
|
-
|
|
21249
|
-
|
|
21250
|
-
|
|
21251
|
-
|
|
21252
|
-
|
|
21964
|
+
/* Row/column pill handles (Notion-style): one per axis, tracked onto the
|
|
21965
|
+
active cell (hovered on desktop, focused on touch) by JS. They live on
|
|
21966
|
+
.doc-table-wrap OUTSIDE .doc-table-scroll so overflow-x can't clip them,
|
|
21967
|
+
straddling the table's left/top border \u2014 never on cell text. Resting =
|
|
21968
|
+
quiet grey pill; pressed/dragging (.grab) = accent pill with light dots
|
|
21969
|
+
plus the source outline + drop line below. touch-action:none so a finger
|
|
21970
|
+
drag on the handle reorders instead of scrolling. Data rows only get a
|
|
21971
|
+
row pill (the block gutter's +/grip pins at the header row's y). */
|
|
21972
|
+
.doc-row-handle, .doc-col-handle { position:absolute; display:grid; place-items:center; z-index:7;
|
|
21973
|
+
color:var(--muted); background:var(--bg); border:1px solid var(--line); box-shadow:var(--shadow-sm);
|
|
21974
|
+
cursor:grab; touch-action:none; animation:gripFade .12s ease both;
|
|
21975
|
+
transition:background .12s,color .12s,border-color .12s; }
|
|
21976
|
+
.doc-row-handle { left:-8px; width:15px; height:28px; border-radius:8px; }
|
|
21977
|
+
.doc-col-handle { top:-6px; width:28px; height:15px; border-radius:8px; transform:translateX(-50%); }
|
|
21978
|
+
.doc-row-handle svg, .doc-col-handle svg { width:13px; height:13px; }
|
|
21979
|
+
.doc-row-handle:hover, .doc-col-handle:hover { background:var(--surface); color:var(--fg); }
|
|
21980
|
+
.doc-row-handle.grab, .doc-col-handle.grab { background:var(--accent); color:var(--accent-fg,#fff);
|
|
21981
|
+
border-color:var(--accent); cursor:grabbing; }
|
|
21982
|
+
.doc-row-handle::after { content:""; position:absolute; inset:-4px; }
|
|
21983
|
+
.doc-col-handle::after { content:""; position:absolute; inset:-4px; }
|
|
21984
|
+
/* drag-reorder overlays (inside .doc-table-inner, so they scroll with the
|
|
21985
|
+
table): accent outline on the source row/column, 2px line at the drop
|
|
21986
|
+
boundary (grid .rowdrop's cousin) */
|
|
21987
|
+
.doc-drag-outline { position:absolute; border:2px solid var(--accent); border-radius:6px;
|
|
21988
|
+
pointer-events:none; z-index:6; box-sizing:border-box; }
|
|
21989
|
+
.doc-rowdrop { position:absolute; height:2px; margin-top:-1px; background:var(--accent); border-radius:1px; pointer-events:none; z-index:6; }
|
|
21990
|
+
.doc-coldrop { position:absolute; width:2px; margin-left:-1px; background:var(--accent); border-radius:1px; pointer-events:none; z-index:6; }
|
|
21991
|
+
/* touch selection action bar: the OSK has no Delete/\u2318C, so the cell
|
|
21992
|
+
rectangle gets on-screen verbs; sits on the wrap layer above the selection */
|
|
21993
|
+
.doc-sel-bar { position:absolute; z-index:8; display:flex; gap:2px; padding:3px;
|
|
21994
|
+
background:var(--accent); border-radius:9px; box-shadow:var(--shadow-sm); animation:gripFade .12s ease both; }
|
|
21995
|
+
.doc-sel-bar button { color:var(--accent-fg,#fff); font-size:13px; line-height:1; padding:5px 9px; border-radius:6px; }
|
|
21996
|
+
.doc-sel-bar button:active { background:color-mix(in srgb, #000 18%, transparent); }
|
|
21253
21997
|
/* add-column (right edge) and add-row (bottom edge) buttons */
|
|
21254
21998
|
.doc-table-addcol { width:22px; margin-left:4px; flex:none; display:grid; place-items:center; color:var(--muted);
|
|
21255
|
-
border:1px dashed var(--line-strong); border-radius:var(--radius-sm); background:var(--surface); transition:background .12s,color .12s,border-color .12s; }
|
|
21256
|
-
|
|
21257
|
-
|
|
21999
|
+
border:1px dashed var(--line-strong); border-radius:var(--radius-sm); background:var(--surface); transition:background .12s,color .12s,border-color .12s,opacity .12s; }
|
|
22000
|
+
/* sticky + 100cqw (the wrap is the container): on a wide table the bar pins
|
|
22001
|
+
to the scrollport at any horizontal offset; narrow tables never hit the
|
|
22002
|
+
max-width and keep their table-wide bar. Engines without cqw just ignore
|
|
22003
|
+
the cap and fall back to scrolling with the table. */
|
|
22004
|
+
.doc-table-addrow { width:100%; max-width:100cqw; position:sticky; left:0; height:22px; margin-top:4px; display:grid; place-items:center; color:var(--muted);
|
|
22005
|
+
border:1px dashed var(--line-strong); border-radius:var(--radius-sm); background:var(--surface); transition:background .12s,color .12s,border-color .12s,opacity .12s; }
|
|
21258
22006
|
.doc-table-addcol:hover, .doc-table-addrow:hover { background:var(--accent-soft); color:var(--accent); border-color:var(--accent); }
|
|
22007
|
+
/* Notion-style: the add bars only materialize while the table is active \u2014
|
|
22008
|
+
hover or keyboard focus on desktop, an editing (focused) cell on touch.
|
|
22009
|
+
Opacity-only so nothing shifts; pointer-events gates off the invisible
|
|
22010
|
+
bars (a touch tap must not hit a bar it can't see). */
|
|
22011
|
+
.doc-table-addrow, .doc-table-addcol { opacity:0; pointer-events:none; }
|
|
22012
|
+
.doc-table-wrap:focus-within :is(.doc-table-addrow, .doc-table-addcol) { opacity:1; pointer-events:auto; }
|
|
22013
|
+
@media (hover:hover) and (pointer:fine) {
|
|
22014
|
+
.doc-table-wrap:hover :is(.doc-table-addrow, .doc-table-addcol) { opacity:1; pointer-events:auto; }
|
|
22015
|
+
}
|
|
21259
22016
|
/* cell range selection (box select): soft fill per cell; the accent rectangle
|
|
21260
22017
|
outline is drawn via inline inset box-shadow on the edge cells (see
|
|
21261
22018
|
cell-select.ts edgeShadow). While selecting, suppress the caret + native
|
|
@@ -21450,7 +22207,7 @@ var styles_default = ` :root {
|
|
|
21450
22207
|
.shv-search { display:flex; align-items:center; gap:6px; background:var(--bg); border:1px solid var(--line-strong); border-radius:var(--radius-sm); padding:5px 10px; color:var(--muted); }
|
|
21451
22208
|
.shv-search input { border:0; background:none; outline:none; color:var(--fg); font-size:13px; width:180px; }
|
|
21452
22209
|
|
|
21453
|
-
.shv-list { display:flex; flex-direction:column; gap:8px; }
|
|
22210
|
+
.shv-list { display:flex; flex-direction:column; gap:8px; list-style:none; padding:0; margin:0; }
|
|
21454
22211
|
.shv-row { display:flex; align-items:center; gap:13px; padding:13px 15px; background:var(--bg); border:1px solid var(--line);
|
|
21455
22212
|
border-radius:var(--radius-lg); transition:border-color .12s, box-shadow .12s; }
|
|
21456
22213
|
.shv-row:hover { border-color:var(--line-strong); box-shadow:var(--shadow-sm); }
|
|
@@ -21472,7 +22229,25 @@ var styles_default = ` :root {
|
|
|
21472
22229
|
.shv-exp.dead { text-decoration:line-through; }
|
|
21473
22230
|
.shv-actions { display:flex; align-items:center; gap:6px; flex:none; }
|
|
21474
22231
|
.shv-actions .btn { padding:5px 10px; font-size:12px; }
|
|
21475
|
-
@media (max-width:640px){
|
|
22232
|
+
@media (max-width:640px){
|
|
22233
|
+
/* head stacks: title/desc full row, then segmented tabs, then search */
|
|
22234
|
+
.shares-page .db-head { flex-direction:column; align-items:stretch; gap:12px; }
|
|
22235
|
+
.shv-tools { margin-left:0; width:100%; }
|
|
22236
|
+
.shv-seg { display:flex; width:100%; }
|
|
22237
|
+
.shv-seg-btn { flex:1; text-align:center; }
|
|
22238
|
+
.shv-search { flex:1; }
|
|
22239
|
+
.shv-search input { width:100%; flex:1; }
|
|
22240
|
+
|
|
22241
|
+
/* cards: title clamps to two lines, meta wraps naturally */
|
|
22242
|
+
.shv-row { flex-wrap:wrap; }
|
|
22243
|
+
.shv-title { max-width:100%; white-space:normal; display:-webkit-box;
|
|
22244
|
+
-webkit-line-clamp:2; -webkit-box-orient:vertical; text-align:left; }
|
|
22245
|
+
.shv-meta { white-space:normal; overflow:visible; text-overflow:clip; }
|
|
22246
|
+
|
|
22247
|
+
/* actions: full row of evenly stretched touch targets */
|
|
22248
|
+
.shv-actions { width:100%; }
|
|
22249
|
+
.shv-actions .btn { flex:1; justify-content:center; padding:7px 10px; }
|
|
22250
|
+
}
|
|
21476
22251
|
|
|
21477
22252
|
/* in-context "\u5DF2\u5206\u4EAB" badge on docs / tables / site cards */
|
|
21478
22253
|
.share-badge { display:inline-grid; place-items:center; width:22px; height:22px; border-radius:6px; color:var(--accent);
|
|
@@ -21549,7 +22324,9 @@ var styles_default = ` :root {
|
|
|
21549
22324
|
box-shadow:var(--shadow-lg); z-index:71; transform:translateX(100%); transition:transform .24s cubic-bezier(.32,.72,0,1); display:flex; flex-direction:column; }
|
|
21550
22325
|
.peek.open { transform:none; }
|
|
21551
22326
|
.peek-head { display:flex; align-items:center; gap:4px; padding:12px 16px; border-bottom:1px solid var(--line); }
|
|
21552
|
-
|
|
22327
|
+
/* Same stable-gutter treatment as .content (stable only \u2014 see .content on
|
|
22328
|
+
why both-edges is forbidden). */
|
|
22329
|
+
.peek-body { overflow:auto; scrollbar-gutter:stable; padding:28px 32px; }
|
|
21553
22330
|
.peek h2 { font-size:27px; font-weight:700; letter-spacing:-.02em; margin:0 0 20px; outline:none; }
|
|
21554
22331
|
.proprow { display:flex; gap:8px; align-items:flex-start; margin:1px 0; }
|
|
21555
22332
|
.proprow .k { width:160px; flex:none; color:var(--muted); display:flex; align-items:center; gap:7px; font-size:13px; padding:7px 8px; border-radius:var(--radius-sm); }
|
|
@@ -21606,7 +22383,7 @@ var styles_default = ` :root {
|
|
|
21606
22383
|
.hist-restore { margin-top:8px; align-self:flex-start; }
|
|
21607
22384
|
|
|
21608
22385
|
/* menu / popover */
|
|
21609
|
-
.menu-layer { position:fixed; inset:0; z-index:
|
|
22386
|
+
.menu-layer { position:fixed; inset:0; z-index:115; }
|
|
21610
22387
|
.pop { position:fixed; background:var(--bg); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-lg);
|
|
21611
22388
|
padding:6px; min-width:232px; max-height:70vh; overflow:auto; animation:pop .12s ease; }
|
|
21612
22389
|
@keyframes pop { from { opacity:0; transform:translateY(-4px); } }
|
|
@@ -21624,6 +22401,9 @@ var styles_default = ` :root {
|
|
|
21624
22401
|
.pop .item .meta { flex:1; min-width:0; }
|
|
21625
22402
|
.pop .item .t { font-size:var(--fs-ui); } .pop .item .d { font-size:var(--fs-xs); color:var(--muted); display:block; }
|
|
21626
22403
|
.pop .item .chk { color:var(--accent); margin-left:auto; }
|
|
22404
|
+
/* shortcut badge, mirrors .sb-search kbd; margin-left:auto pins it right (and
|
|
22405
|
+
a trailing .chk, whose own auto then collapses, rides the gap beside it) */
|
|
22406
|
+
.pop .item kbd { margin-left:auto; font-family:var(--mono); font-size:10px; color:var(--muted); border:1px solid var(--line-strong); border-radius:4px; padding:1px 4px; }
|
|
21627
22407
|
.pop .item.danger { color:var(--danger); } .pop .item.danger .lico { color:var(--danger); }
|
|
21628
22408
|
.pop .sep { height:1px; background:var(--line); margin:5px 4px; }
|
|
21629
22409
|
.pop .typegrid { display:grid; grid-template-columns:repeat(4,1fr); gap:4px; padding:2px 2px 4px; }
|
|
@@ -21743,24 +22523,28 @@ var styles_default = ` :root {
|
|
|
21743
22523
|
.view-placeholder { padding:40px; text-align:center; border:1px dashed var(--line-strong); border-radius:var(--radius); color:var(--muted); margin-top:8px; }
|
|
21744
22524
|
.ico.flip { transform:scaleX(-1); }
|
|
21745
22525
|
|
|
21746
|
-
/* ---- find in document (Ctrl-F) ---- */
|
|
21747
|
-
/*
|
|
21748
|
-
|
|
21749
|
-
|
|
22526
|
+
/* ---- find in document (Ctrl-F, CM6 bar) ---- */
|
|
22527
|
+
/* Match highlights are CM6 Decoration.marks (.cm-find/.cm-find-cur, editor-theme.ts).
|
|
22528
|
+
Matches INSIDE void widgets (code mirror, table cells) can't be mark
|
|
22529
|
+
decorations \u2014 the block replace widget swallows them \u2014 so those paint via
|
|
22530
|
+
the CSS Custom Highlight API (find.ts collectMatches/applyHighlights). */
|
|
22531
|
+
::highlight(mh-find-all) { background: color-mix(in srgb, var(--accent) 26%, transparent); border-radius: 2px; }
|
|
22532
|
+
::highlight(mh-find-current) { background: var(--accent); color: var(--accent-fg); }
|
|
21750
22533
|
.find-bar {
|
|
21751
|
-
position:fixed; top:58px; right:18px; z-index:
|
|
22534
|
+
position:fixed; top:58px; right:18px; z-index:96; /* below the ~50px topbar */
|
|
21752
22535
|
display:flex; align-items:center; gap:4px;
|
|
21753
22536
|
padding:5px 6px; background:var(--bg); border:1px solid var(--line-strong);
|
|
21754
22537
|
border-radius:var(--radius); box-shadow:var(--shadow-lg);
|
|
21755
22538
|
}
|
|
21756
22539
|
.find-bar .find-ico { color:var(--muted); margin:0 2px; flex:none; }
|
|
21757
22540
|
.find-bar input {
|
|
21758
|
-
width:200px; max-width:42vw; border:0; outline:0; background:transparent;
|
|
22541
|
+
width:200px; max-width:42vw; min-width:0; border:0; outline:0; background:transparent;
|
|
21759
22542
|
color:var(--fg); font-size:13px; padding:3px 2px;
|
|
21760
22543
|
}
|
|
21761
22544
|
.find-bar input::placeholder { color:var(--muted); }
|
|
21762
|
-
.find-count { font-size:12px; color:var(--muted); min-width:48px; text-align:right; padding:0 2px; font-variant-numeric:tabular-nums; }
|
|
22545
|
+
.find-count { font-size:12px; color:var(--muted); min-width:48px; text-align:right; padding:0 2px; font-variant-numeric:tabular-nums; flex:none; }
|
|
21763
22546
|
.find-opt {
|
|
22547
|
+
flex:none; white-space:nowrap;
|
|
21764
22548
|
border:1px solid var(--line-strong); background:transparent; color:var(--fg-soft);
|
|
21765
22549
|
border-radius:var(--radius-sm); font-size:12px; line-height:1; padding:4px 6px; cursor:pointer;
|
|
21766
22550
|
transition:background .12s, color .12s, border-color .12s;
|
|
@@ -21780,6 +22564,14 @@ var styles_default = ` :root {
|
|
|
21780
22564
|
.find-bar { left:12px; right:12px; }
|
|
21781
22565
|
.find-bar input { width:auto; flex:1; max-width:none; }
|
|
21782
22566
|
}
|
|
22567
|
+
/* Very narrow windows (quicknote resizes down to 280px): drop the reserved
|
|
22568
|
+
count width and tighten gaps so the option buttons never get crushed. */
|
|
22569
|
+
@media (max-width:360px) {
|
|
22570
|
+
.find-bar { gap:2px; padding:5px 4px; }
|
|
22571
|
+
.find-count { min-width:0; }
|
|
22572
|
+
}
|
|
22573
|
+
/* Quicknote window: its qn-bar is ~34px, not the main ~50px topbar. */
|
|
22574
|
+
body.quicknote .find-bar { top:42px; }
|
|
21783
22575
|
|
|
21784
22576
|
/* ---- board (kanban) view ---- */
|
|
21785
22577
|
.board-wrap { margin-top:4px; }
|
|
@@ -21936,8 +22728,7 @@ var styles_default = ` :root {
|
|
|
21936
22728
|
.navitem { padding:9px 8px; font-size:16px; }
|
|
21937
22729
|
.navitem .emoji { font-size:18px; }
|
|
21938
22730
|
.navitem .tw { width:26px; height:26px; }
|
|
21939
|
-
.sb-
|
|
21940
|
-
.sb-search kbd { display:none; } /* no hardware keyboard, no shortcut hint */
|
|
22731
|
+
.sb-search kbd, .pop .item kbd { display:none; } /* no hardware keyboard, no shortcut hint */
|
|
21941
22732
|
/* Search box is hidden until the header search button reveals it; it then
|
|
21942
22733
|
slides+fades down in. Desktop base (.sb-search{display:flex}) is untouched,
|
|
21943
22734
|
so search stays always-on there. reduced-motion is handled globally. */
|
|
@@ -21952,17 +22743,26 @@ var styles_default = ` :root {
|
|
|
21952
22743
|
/* Document: bigger body text, tighter side padding for narrow screens. */
|
|
21953
22744
|
.doc { padding:24px 18px calc(36vh + env(safe-area-inset-bottom)); }
|
|
21954
22745
|
.doc-title { font-size:30px; }
|
|
21955
|
-
|
|
22746
|
+
/* CM6 editor (single-document): its .cm-editor inherits from here, so the whole
|
|
22747
|
+
body incl. table cells gets 16px on mobile \u2014 same iOS anti-zoom reason. */
|
|
22748
|
+
.doc .cm-doc-body { font-size:16px; }
|
|
21956
22749
|
/* 16px (not 14): a focused code textarea below 16px makes iOS auto-zoom. */
|
|
21957
22750
|
.codeblock { --code-fs:16px; }
|
|
21958
|
-
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
22751
|
+
/* CM6 hover gutter: no hover on touch, and -52px overlays would overflow
|
|
22752
|
+
the tight mobile margins \u2014 hide. */
|
|
22753
|
+
.cm-gutter-layer { display:none; }
|
|
22754
|
+
/* Autofit tables: on phones the atomic code span IS the width cap \u2014 one
|
|
22755
|
+
long \`file-name.md\` becomes the column's min-content, eats the whole
|
|
22756
|
+
100cqw budget and crushes prose columns to their 48px floor. Let code
|
|
22757
|
+
wrap at its natural break points (hyphens/slashes) instead; wider
|
|
22758
|
+
layouts keep the atomic-code tradeoff (see the autofit rules). */
|
|
22759
|
+
table.doc-table.autofit .doc-td code { white-space:pre-wrap; overflow-wrap:normal; word-break:normal; }
|
|
21962
22760
|
/* Find bar clears the taller mobile topbar (49px min + notch inset). */
|
|
21963
22761
|
.find-bar { top:calc(49px + env(safe-area-inset-top) + 8px); }
|
|
21964
22762
|
/* narrower handle gutter on phones; .db left padding must be \u2265 --grip-gutter */
|
|
21965
22763
|
.db { padding:16px 14px 90px 24px; }
|
|
22764
|
+
/* pages reusing .db without row grips don't need the left gutter \u2014 keep padding symmetric */
|
|
22765
|
+
.shares-page.db { padding-left:14px; }
|
|
21966
22766
|
.gridhost { --grip-gutter:24px; }
|
|
21967
22767
|
.peek { width:100%; }
|
|
21968
22768
|
/* history drawer: the side-by-side list/preview doesn't fit a phone \u2014 stack. */
|
|
@@ -21997,13 +22797,19 @@ var styles_default = ` :root {
|
|
|
21997
22797
|
@media (pointer: coarse) {
|
|
21998
22798
|
/* iOS Safari auto-zooms the page when a focused field is below 16px; the
|
|
21999
22799
|
class selectors out-specify the element-level sizes set on these. */
|
|
22000
|
-
input, textarea, .text-input,
|
|
22800
|
+
input, textarea, .text-input, input.inlineedit { font-size:16px; }
|
|
22001
22801
|
.navitem .acts button { width:34px; height:34px; }
|
|
22802
|
+
.sb-tab { min-height:40px; padding:8px 10px; }
|
|
22803
|
+
.sb-tabs .add { width:40px; height:40px; display:grid; place-items:center; padding:0; }
|
|
22002
22804
|
.iconbtn { width:40px; height:40px; }
|
|
22003
22805
|
.btn { padding:9px 14px; min-height:40px; }
|
|
22004
22806
|
.pop .item { padding:11px 10px; }
|
|
22005
22807
|
/* Column/sidebar resize handles: 7px is a mouse affordance, not a finger's. */
|
|
22006
22808
|
.col-resizer, .doc-col-resizer, .sb-resizer { width:16px; right:-8px; }
|
|
22809
|
+
/* cell-rectangle corner handle: big enough to grab with a finger (it is the
|
|
22810
|
+
only way to grow a touch selection \u2014 pan gestures stay scrolls) */
|
|
22811
|
+
.doc-cell-handle { width:12px; height:12px; right:-6px; bottom:-6px; }
|
|
22812
|
+
.doc-cell-handle::after { inset:-10px; }
|
|
22007
22813
|
}
|
|
22008
22814
|
|
|
22009
22815
|
/* No hover available (touch): controls that only materialize on :hover are
|
|
@@ -22012,9 +22818,11 @@ var styles_default = ` :root {
|
|
|
22012
22818
|
@media (hover: none) {
|
|
22013
22819
|
.code-tools { opacity:1; transform:none; pointer-events:auto; }
|
|
22014
22820
|
.rowactions { opacity:1; pointer-events:auto; }
|
|
22015
|
-
.
|
|
22016
|
-
.navitem .acts
|
|
22017
|
-
|
|
22821
|
+
.cal-add, .filerow .facts,
|
|
22822
|
+
.navitem .acts { opacity:1; }
|
|
22823
|
+
/* the pill handles survive on touch (focus-driven, not hover-driven), but
|
|
22824
|
+
sticky tap-hover must not tint them */
|
|
22825
|
+
.doc-row-handle:hover, .doc-col-handle:hover { background:var(--bg); color:var(--muted); }
|
|
22018
22826
|
.selcell input { opacity:1; }
|
|
22019
22827
|
.tl-item .tl-h { opacity:1; }
|
|
22020
22828
|
/* Neutralize the stickiest tap-residue tints (a tap "hovers" until the
|
|
@@ -22026,12 +22834,16 @@ var styles_default = ` :root {
|
|
|
22026
22834
|
.filerow:hover { background:none; }
|
|
22027
22835
|
.cal-day:hover { background:none; }
|
|
22028
22836
|
.cal-day.dim:hover { background:var(--surface); }
|
|
22837
|
+
.doc-table-addcol:hover, .doc-table-addrow:hover { background:var(--surface); color:var(--muted); border-color:var(--line-strong); }
|
|
22029
22838
|
}
|
|
22030
22839
|
|
|
22031
22840
|
/* Quick Notes window (desktop, loaded at #quick). A transparent body lets the
|
|
22032
22841
|
macOS vibrancy blur show through; a translucent panel keeps text readable.
|
|
22033
22842
|
The top bar is the window drag region; its buttons opt back out. */
|
|
22034
|
-
|
|
22843
|
+
/* --pop-top-inset: editor popups (slash menu) clamp their upward growth below
|
|
22844
|
+
this line, so they never cover the drag bar / macOS traffic lights (bar =
|
|
22845
|
+
26px actions + 2\xD74px padding). */
|
|
22846
|
+
body.quicknote { background:transparent; --pop-top-inset:38px; }
|
|
22035
22847
|
/* html carries an opaque bg for the iOS 26 Safari toolbar tint; the vibrancy
|
|
22036
22848
|
window needs the root transparent again or the blur is walled off. */
|
|
22037
22849
|
html:has(body.quicknote) { background:transparent; }
|
|
@@ -22051,22 +22863,32 @@ var styles_default = ` :root {
|
|
|
22051
22863
|
.qn-actions { display:flex; gap:2px; -webkit-app-region:no-drag; }
|
|
22052
22864
|
.qn-actions .iconbtn { width:26px; height:26px; }
|
|
22053
22865
|
.qn-actions .iconbtn.active { color:var(--accent); background:var(--hover-2); }
|
|
22054
|
-
|
|
22055
|
-
|
|
22866
|
+
/* Quicknote scrolling: the app-wide thin translucent scrollbar (transparent
|
|
22867
|
+
track, so the reserved lane just reads as right margin). stable reserves
|
|
22868
|
+
the lane up front so width never jumps when the doc grows past one
|
|
22869
|
+
screen; the doc's right padding + lane \u2248 left padding, keeping the prose
|
|
22870
|
+
visually centered. stable only, NEVER both-edges \u2014 see .content: a left
|
|
22871
|
+
gutter desyncs Chromium's scroll cull rect and paints a blank stripe over
|
|
22872
|
+
the rightmost 15px of content (it sliced this window's file cards). */
|
|
22873
|
+
/* scrollbar-width does NOT inherit (unlike scrollbar-color): without an
|
|
22874
|
+
explicit thin here the lane is the classic 15px. thin = 11px, so the
|
|
22875
|
+
doc's 1px right padding + 11px lane mirrors the 12px left padding. */
|
|
22876
|
+
.qn-body { flex:1; overflow:auto; scrollbar-gutter:stable; scrollbar-width:thin; -webkit-app-region:no-drag; }
|
|
22877
|
+
.qn-body .doc { max-width:none; margin:0; padding:14px 1px 30vh 12px; }
|
|
22056
22878
|
.qn-body .doc-title { font-size:24px; margin-bottom:2px; }
|
|
22057
22879
|
.qn-body .doc-meta { display:none; }
|
|
22058
|
-
/* The
|
|
22059
|
-
desktop window never matches \u2014 re-hide explicitly: quick notes offer no
|
|
22880
|
+
/* The CM6 hover gutter never fits the compact window: quick notes offer no
|
|
22060
22881
|
block insert/drag handles (Enter / slash commands cover insertion). */
|
|
22061
|
-
body.quicknote .
|
|
22882
|
+
body.quicknote .cm-gutter-layer { display:none; }
|
|
22062
22883
|
/* A native checkbox paints an opaque white box when unchecked, which breaks
|
|
22063
22884
|
the window's vibrancy blur. Draw a custom box so the unchecked background
|
|
22064
|
-
stays transparent; checked falls back to the accent fill + checkmark.
|
|
22065
|
-
|
|
22885
|
+
stays transparent; checked falls back to the accent fill + checkmark.
|
|
22886
|
+
(Same treatment main gave \`.b-todo .marker input\`.) */
|
|
22887
|
+
body.quicknote .cm-todo-box {
|
|
22066
22888
|
appearance:none; -webkit-appearance:none; box-sizing:border-box;
|
|
22067
22889
|
border:1.5px solid color-mix(in srgb, var(--fg-soft) 55%, transparent);
|
|
22068
22890
|
border-radius:4px; background:transparent; }
|
|
22069
|
-
body.quicknote .
|
|
22891
|
+
body.quicknote .cm-todo-box:checked {
|
|
22070
22892
|
border-color:var(--accent);
|
|
22071
22893
|
background:var(--accent) center/10px no-repeat
|
|
22072
22894
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5L20 7'/%3E%3C/svg%3E"); }
|
|
@@ -22088,8 +22910,11 @@ var styles_default = ` :root {
|
|
|
22088
22910
|
.qn-set-main { flex:1; min-width:0; }
|
|
22089
22911
|
.qn-set-name { font-weight:600; }
|
|
22090
22912
|
.qn-set-desc { color:var(--muted); font-size:13px; margin-top:2px; }
|
|
22091
|
-
.qn-shortcut { min-width:140px; justify-content:center; font-family
|
|
22913
|
+
.qn-shortcut { min-width:140px; justify-content:center; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",sans-serif; }
|
|
22092
22914
|
.qn-shortcut.capturing { outline:2px solid var(--accent); color:var(--accent); }
|
|
22915
|
+
.qn-keys { display:inline-flex; align-items:center; gap:6px; }
|
|
22916
|
+
.qn-key { line-height:1; }
|
|
22917
|
+
.qn-key.sym { font-size:1.15em; line-height:1; } /* \u2318 \u21E7 glyphs render smaller \u2014 bump to match letters */
|
|
22093
22918
|
|
|
22094
22919
|
/* No-origin bootstrap: enroll screen + hydration splash (data-blind shell). */
|
|
22095
22920
|
.enroll-splash { min-height:100dvh; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px; padding:24px; }
|
|
@@ -22103,6 +22928,44 @@ var styles_default = ` :root {
|
|
|
22103
22928
|
.enroll-err { color:var(--danger); font-size:13px; margin-top:12px; }
|
|
22104
22929
|
.enroll-go { width:100%; justify-content:center; margin-top:18px; }
|
|
22105
22930
|
.enroll-hint { font-size:12px; margin-top:14px; line-height:1.5; }
|
|
22931
|
+
.enroll-card { animation:enroll-in .35s ease both; }
|
|
22932
|
+
@keyframes enroll-in { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
|
|
22933
|
+
|
|
22934
|
+
/* Scan-first enroll: square viewfinder (corner frame + sweeping accent beam)
|
|
22935
|
+
over the live camera feed. The chip stays black in both themes \u2014 the feed
|
|
22936
|
+
brings its own colors; accent carries the beam and the success flash. */
|
|
22937
|
+
.enroll-scan { position:relative; aspect-ratio:1/1; margin:2px 0 12px; border-radius:16px; overflow:hidden; background:#0b0b0e; }
|
|
22938
|
+
.enroll-scan video { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
|
|
22939
|
+
.enroll-scan::after { content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none; box-shadow:inset 0 0 44px rgba(0,0,0,.35); }
|
|
22940
|
+
.enroll-scan-frame { position:absolute; inset:0; width:100%; height:100%; fill:none; stroke:rgba(255,255,255,.92); stroke-width:2.5; stroke-linecap:round; pointer-events:none; }
|
|
22941
|
+
.enroll-scan-beam { position:absolute; left:9%; right:9%; top:12%; height:2px; border-radius:2px; pointer-events:none; opacity:.85;
|
|
22942
|
+
background:linear-gradient(90deg, transparent, var(--accent), transparent);
|
|
22943
|
+
animation:enroll-beam 2.4s ease-in-out infinite alternate; }
|
|
22944
|
+
@keyframes enroll-beam { to { top:86%; } }
|
|
22945
|
+
.enroll-scan.hit .enroll-scan-frame { stroke:var(--accent); }
|
|
22946
|
+
.enroll-scan.hit::after { animation:enroll-hit .38s ease-out; }
|
|
22947
|
+
@keyframes enroll-hit { from { box-shadow:inset 0 0 0 3px var(--accent), inset 0 0 60px rgba(0,0,0,.2); } to { box-shadow:inset 0 0 44px rgba(0,0,0,.35); } }
|
|
22948
|
+
.enroll-scan-wait { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:rgba(255,255,255,.45); }
|
|
22949
|
+
.enroll-scan-waitico { width:30px; height:30px; animation:enroll-wait 1.6s ease-in-out infinite; }
|
|
22950
|
+
@keyframes enroll-wait { 0%,100% { opacity:.4; } 50% { opacity:.9; } }
|
|
22951
|
+
.enroll-cam-err { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; padding:20px; text-align:center; color:rgba(255,255,255,.9); font-size:13px; }
|
|
22952
|
+
.enroll-cam-err .muted { color:rgba(255,255,255,.55); font-size:12px; }
|
|
22953
|
+
.enroll-cam-errico { width:26px; height:26px; opacity:.7; }
|
|
22954
|
+
.enroll-cam-err .btn { margin-top:8px; }
|
|
22955
|
+
.enroll-paste { width:100%; font-size:13px; }
|
|
22956
|
+
.enroll-switch { display:flex; justify-content:center; align-items:center; gap:10px; margin-top:14px; }
|
|
22957
|
+
.enroll-switch-dot { color:var(--muted); }
|
|
22958
|
+
.enroll-link { background:none; border:0; padding:0; font:inherit; font-size:13px; color:var(--accent); cursor:pointer; }
|
|
22959
|
+
.enroll-link:hover { text-decoration:underline; }
|
|
22960
|
+
/* Unlock step: compact bucket summary chip above the passphrase field. */
|
|
22961
|
+
.enroll-bucket-card { display:flex; align-items:center; gap:10px; padding:12px 14px; margin:2px 0 14px; border:1px solid var(--line); border-radius:12px; background:var(--bg); }
|
|
22962
|
+
.enroll-bucket-ico { width:18px; height:18px; color:var(--muted); }
|
|
22963
|
+
.enroll-bucket-meta { flex:1; min-width:0; }
|
|
22964
|
+
.enroll-bucket-name { font-weight:600; font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
22965
|
+
.enroll-bucket-host { font-family:var(--mono); font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
22966
|
+
.enroll-bucket-badge { display:inline-flex; align-items:center; gap:4px; flex:none; font-size:11px; color:var(--accent); padding:3px 8px; border-radius:999px;
|
|
22967
|
+
border:1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
|
|
22968
|
+
.enroll-bucket-badge .ico { width:12px; height:12px; }
|
|
22106
22969
|
|
|
22107
22970
|
/* Enroll QR (settings \u2192 \u540C\u6B65\u5B58\u50A8 \u2192 \u5728\u624B\u673A\u4E0A\u6253\u5F00). The QR inverts with the
|
|
22108
22971
|
theme: light = dark modules on a light chip, dark = light modules on a dark
|
|
@@ -22305,6 +23168,42 @@ var ICON_192 = "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAAXNSR0IArs4c6QAA
|
|
|
22305
23168
|
var ICON_512 = "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";
|
|
22306
23169
|
var ICON_180 = "iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAtKADAAQAAAABAAAAtAAAAABW1ZZ5AAAZyElEQVR4Ae1da7AVVXZe3X1e99yLQNDRCIwP1FEHlWEQYyKpjGVUsIjMKGMMjq9iwDyoVE2qJGX+UPlhRiuZqoQZo0gEB/ExTKZQo1edV6IkBqUYfGJGER0QJULuBe7rPLo76+tzG+7j3HtO7919us89a1GHc273Xnut9e2vd+9X7yYSEQQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEgfERMMY/ncyzq1a52Wl279SC6UwxbXeKYWVyVC4n09lm8SqVItcuDjiW0Z11zO7DVnvX2rVGoVnc9/1MPKFd1zXuXnnk7EwmM5ccmueQczEfO8skOsUlo50DyZpmir9cPyb5VkLAIMfxKoWCQW6vQ/S5YRh7TTLfJJN2FIvFnfc/NPlDPpZooBNL6HtW9M5lIBeTYVzLZJ1tWZkOwzDJdZnSrs3fNh92B2mcaIyV6BWPkkEeIQz+Niwy+eNjbtvFHiLjbcb8Ba5Ynr13XfvOeHwc32qiCL16xf9NTlltS7gGvpMM9/JUqi3tOCWv5gCRReJDAMTGndA009y66y+Ra7zKtfUjZbt/633rfutIfJ4Nt5wIQoPIlplbzlXCCstMn0dMXq4RuPaVmnd4cSXjL9TjfMfkCtsk2yn9mm+Z62xnYH0SiB0rodescc3CZ73LDNO6xzIz51dqY774k1Fu4kUNBEAe1Nj42E7xPdex782e1r55zRojtttpbIRevaLnIsuy7jNNayGaE3yl14BPTicZAb6zeu1tx7E7bdtefd+6jrfi8DcWQv/1yt67TNO4l2vlqaVyfxxxi82IEEin2lBbdzmOe893H2p/MCIzY2bbUEKvWnX4pPZS9h9TZuZ2r3mBkQqRCYcARke8zqNT3NibLvzl2rXTjjYqyIYR+jt/dnhmzsk+ZqXafr9U6uP4pKXcqEKOx45B6XSe7HL/ywNm4ZbvPTBtXyP8aAih/2rl4QszRm6LZWUvLJdBZpFWQSCVYlLbhXeL7sDSf3ho2rtRxx05oe9efuwCK2U9y2OYs8r2QNTxSP4JRCBl5TCXsMcu24vvXz9pd5QuRkrou5f3zTBN5yUes7xAyBxlMSY/b5Ca5xZ2O4559f3r8/uj8piXREQjd9/5+STTsh9PpXJC5mggbqpcUaGBC+AEuBGV8xER2jXMVGYtt58WlKTNHFXZNV2+4AI4AW7woEAkrYNICM2TJissq+22ymhG0+EuDkeIADgBboAjUZgJ/SpZ/e1jX+Ypo228cGXK4HLEKPyWPJsYASxy4gVo3fzfFfc9POmdMEMJtYZeutS1eBL7eykrI2QOs5QmWF6o6DyOMFfAmTDDC5XQZ0w+enPayl0t09lhFtHEzAscAVfAmTAjDK3JsWrZ4ZPa2lM7LCN9riw0CrOIJm5eWNBku6X3+3vL89ZuDmd6PLQaOp9PL09beSHzxOVf6JGh4gNnwJ2wMg+lhsaio1x/aidfcbOw6EhEEKgXgcpa6tKegbby3DAWMeHpUm1hMi/hx6Vm8aM52nlJBq2FAGppcCfX37+EI/+hbvTaTQ48dcLPqn4bj02JCAJKCDB3wCFwSUl/iJJ2Br2fdF9smuZlZX4GUEQQUEEA3AGHwCUV/aE62k0O1zWvT6dy6VKpd2i+8lsQCICAy82O9nSx3Hc9K+0KoDgqqVYNvXTpjyzDdRdKR3AUrnIgIALgELgETgVUHZZcq4aecfLVZ7hFmm3zzI88fzIMV/kjIAKDHJoNTrHqhwHVjyfXqqGtAs2zrFy7bAJzHE/5oYgAOAQugVOKWXhqWoTmrbrmmbzZiIggEAYCHpeYUzp5aTU5yDEucrGniLQ3dMpAdAcRcB3mEnNKBxDl6nXVQjfLu1SehY0TRQSBMBAAl8ApcEs1P2VCT5reO4U3NzvZ2wVU1XoEerhZ8CB95RNB/s2eZZLxqXDJOLnCLTWklZscBXNgKrmpDiwGScqmiiVeRsI7wVIux9sJ8qVaLLhU5GO8lzcP3KsBNFG0cDfHnvDpNFEG91b+e2CAS44ZjmPJEPbHcDs8bhEdVPFJmdBu0ZhiYv1fApocKCxcWJdcmqZLfzdDp56OrV8NOtLt0Lu7SvTatiIdO+JQKh3KWiwVnGPVKZdcmjTZpPlXZOjCOWmaPMXkbQVcOnjAodf/q0jv7KxskBn7Rc9XGe9LnXaYW6qAKROaHLPNsAyTcYlVQOYs18jfvCNPX7lseFVz8qkmzfpSiuYvyNAT6/to7/tlro1ai9QlJvNZ56bo5uV5vtCHz1l84bctuuirafrV9hL9aEMfFbjGjpPUoJKJjaiZW6qkUr4Rc+AxU5lDZg/QxLjpzrZRZB4KCAryjlXtdBp/t9KrWBArYkbsI8k8FB9UBMAQWCagVHFRKXNLmdBDAYnrd6ns0uyvpGnOfN58u4ZMnmrSspV5yufxLpEaiSfAacSIWBEzYq8lwBBYAtNmltqRjhMdQo/zgyrlUm4X1itfPDtF37i1jTec5+FO/sTpe5S2/fgQK2KuVzwsGdMofasn73r9rZau/mirafveVTsX8TGYxmjGF0a0C2uZncedxs8+senFrQOUyUzM9nSp6NI1S3KEWIMIsASmaHfHhgwKVkO0amgNu/qqHDjafDyYEVgWfqON5lya8Yb1AisnXAFDlYgNMQYVYJmUdnRQ3/30zUtoPwKFb4s7+zfdmacZZ3AnkWujiSKIBTEhNsTYiqJJaJAh7o9asXWchA5TO7VP4k6iHXcM+vYRA2JBTIhNXfR90eeEuvdahG7u0IlmnGl549doMDZzJxG+IwaMxSMmHWn2Mm3aTuHxG4NO6bEuhqs+3W/Tcz/ub9pOIjqB193I/YI6hi9rwuUzumbCiBLAvoZo1dAadhOles2SNm9EAB2qZhP4jNEMxCDCgwQCQmXh0tLb2+mMWSlvyKpZMMHwGnyG73FOWScJLyH0YGm087pBdKhO4kU8ThMs8YaP8NXr2HprHpNEq/h8EUIPwf70mRYv4mn3lp5ieWVSBb5heSx8hc8iJxBQ7hTyuhcyucmJ9bRxiL+IP2zbs+emadENbfT0E32UTuhMIpoa19+cJ/gatkSFa71+wr7ODVKZ0BUHweaYGH0cofDtX7U4R58dsOm//73Ai+GPG0rEj2KB6Hf+IEvwMXzxyzN8TOv3Vc+2NDmqII3p3xtvzdPZvJYatWFSBL7AJ/jmTVEnxbEE+SGEHqMw2njp5S13tdMUXnpp69wDx8g/6GH4AF/gE3wTqY6AOqHRiB56h4rrd/W4Qjl6Kj/RsWxFB78PhENFJzGmGGEbPsAX+BSpxBTjMGzBLUVRJzQbTELs8CFKueCSNC3+47y38D2ueLHoHj7AlyglrvhG2tWJUYvQOoabSfdr1+bo967MxbLcFDOBsA0fRGojIISujZG38OeGb+XpvC/zI0q8bqJRAluwCdvxrbhvVLTh2GkpQqMteuigWg8PT5bfsrKDpp1ika3Rxqu32GADtmATtlUEsXptfxXlJtXRI/TIxk8cfwcAHrNrzzzZTx/xdgYqgm0RMNWMjVm86fGI4kXesAFbsKkiiBGxIuZAElFMgTpcgRwenjhouMO1A3kZFVIjXKrxZ88xh374wDHqOYphi+Dypdlp+jo3ARwb7wUJPybkibxhA7ZUBLF5MXKswSX8mIIPHwT32tfQInQzhm7x3Oi+j2za/HAPNx0QQXBZcFWOFlzTRkVu44aNAfJE3rChIogJsSFGxBpUwo5HJb+gPg9Nr0XooRk10+80T2fv2l70FvWr+v31P8nztlr8oG2InUTkhTyRt6rgQQXEhhhbUfQIrXL5ha0TtNQG7WNLsJ8+3Uevb+PFEQqCDQ/RYTv1NO4k4l2jmnEhD+SFPJG3iiAWxORtd+b7EzQjXy/O76A+D0mvR+ghGTXbT28tBP/31CO99NEHap3EqdN4KvpPmYDcOtDZjQm6yAN5IU8VQQyIBYs8Wnmdhxp6KognUAdPefT3ubSJO4lHulQ6UETnnJ+mpbe1e4T2+ogB44QOCI08kJeKwHfEgFha/ckVLULHeVfybasQwNfFNzpOB/ZxJ3Fdj/KkyeVfy9GV1/FMokInETrQRR4qgskX+I4YEMvQ2FTyG6of128Vv30dLUL7mTT7d5rbrG/uKNIzT/Uph/JHvNbioq8G240J09rQga6qwGf4jhhEtB6S5XYn7pexf4IWY3WfMZHx83/rp1d/ORA0Qy89OmLo0J0+Y3A3phq4YJcjpIWO6p7V8BU+ezvwV7Pn1dcBwvGq5Or4NLScSa1Pg0ilhh4sb3Sk0P7csrGHPngPwxbBBdvW3vrnk7z1yuN1EnEOa5qRtp6tbqt5Ah/hK3xu5U7gSGyE0EMQATkKXEGjg9V1SK2TeOY5Kd5brsNrzKLSHCneMT6ONEirIvANPsLXVu8EjsRPCD0CEXSsDn5q06YHjylPmsxfkKWreeOXaivzcAznkEZF0ImEb/BRZSZQxWYz6SgTGq87qNZsa+SxoE1EFEw9/uFp73d+VaSfbOJxXUVZdGM7zbks6723xGuacj54hwmO4ZyqwCf4Bh/riSWwnQQ0ocEtVVEmtKrBptDjQsVs3X+80E8vv6TWScSr5PDI1MyzUlTmWhUf/PYe6VJraXi+wCdvJhFXicgoBITQoyA5ccC0DK6le+h/3lbrJOJVarf9BXcS203vg984piLwAb7AJ5GxEVBDd+z8JtQZdLiwDwaWYqo+GDDzTLxSrcP74LeKwDZ8gC/SCRwfQSH0+Ph4Ha9D/wtC9Xht4BrJq56+9Iosv9xIrROIdjdswwfpBFaFd9hBtSpjMAssRq8sch+WZ0P+8DtEQY1V9CqvBK5XFxMX771V8MZ9l62c1LBxX/iKsWbYRrs5CNaVsqk3whPp4ixTeBEkxhNen/glNfQJLMb9BUK98tN++sVz6tPj4xqochK2YNPrBFY5L4dGIyCEHo3JmEdSKYO2Ps7DZruKY6YJ6wRswBZsitSPgBC6fqy8B04xRrrpgaPeuw4DqAZKivcowgZsBX7INZCliZdYCB2wTPG6tK7DDj36g6Pe+uOA6jWTY00z8oaNVn01W02QxkmgRWi/Yxbn9zixjXlK11+sjvtgd4me+peeUPe9wB4ayBN5w4aun2MCMM4JXZth6I/jXs1TWoSumfsETYBJOnTU/vOX/fQiP8MXliAv5OmNaISVaYvlI4TWKHDUos882UtvvK7fSUQeyEt1bbRGGBNKVQitUZxYh+zwWy8fe/AoHfiN+ooa6CIP5CVrmzUKhFX1CB1Gg0k3j6Dx69oboW8xgke6bNrwT0epV2GnIuhAF3kgL+2G8wj/gsITuv2R/tTzd2CnTyhoEvpERq38C82EvR+U6PGHj3lPcNeLBZ5cgQ50pakxiJrmKkLlqW/cYLGXvKb9est+VDrYVbHt66nojnJiyAF05La/PECnTU/R4pvqW+/83JZeTyeKTqAf5xAX6/qpqldX5nUkgn21/WErmevV0HU42EpJsOj+uR9XSForbpAfaZP66rha/if1vHINndSA4vQLHTo0ETf9c6U9veAP20YRFo9gYX3GTx7r8VyVTmC4JaZOaLQ5vE5MuA7VnZvqvdHXw3cEYjKpsVn5k+uPeVsiXDI/S9O/WIH5Ex7NeOO1An28p8RLQQ1CWqV2Uz1++3HWk3ZoGlW9oXno/tZoc6gTWtfpCayPWjfFHcXffFimve+Xjq/HwEygxU+c4JxINAhoETrOi9m3je8goqoXxIaf1mR0zSqr5YL67OcX5NuPU0WnEf6N5ZeubS1CV+6Xui6MFVpUx1WKOipfosxXNU5VvbBi0eOTHqHjjF3Vtq+nh1tYpRddPn6cQS2o6gW1E1F6GbaLCFjJNh4EhNDx4C5WI0JACB0RsJJtPAhotaHjbG6p2vb18D2RxY8zaIyqekHtjJVet1y0CO1NCuh6MFZktY77yKvY93Vr2Wjm8zq4qOgmBCtpciSkIMSNcBAQQoeDo+SSEAQ0mhxYzAH1Zrs/tUJ7A+xSjVNVDzbDENgHt9REg9DqkKm5OlxLB3Yd3eFeJPcvxKgicWOj6rcfqxahlSsB37rOt2rkfomp6uv43EhdP04Vm3Fjo7F2S9rQKgUuOolFQAid2KIRx1QQEEKroCY6iUVAvQ2dhCdWVN685rct424nRk0JP86gdoBp3NjE8cQK+CxPfQdlS+PSq/JZVS+syGBfg8/eQLKGL3GHD9fhQ1BR0QlqI+70KjH65amiG1a8erZbrg3dSk9Zt1Ks/uXU1ITGQ6f4BJFqb3cNot9MaYPGqoJn0vBQ7xQiEv8OFUNUGHvv73Po0/1lOpV3K6pHsI/cwQNl3kOOtfXubPWYizUNYkSsiLl9Un31FrAEpt5T6U2KT32RjlE0Pp/j+ubNOmnbz/vH8G704TdeL9Chz21vW4G4fG6UXbzKArEi5noFWALTRvk4lp16/a2WTovQ1TJs5DHUJLu2D9Dr22q/vrjrsE1PP3GMX1ypMa/ayOBCsIVYETNiryXAEFg2+54hyoTmnTNjZwY6PbjKN36/m157ZeyaGrfSH/xdFx38lJsbGGtsEUGsiBmxA4OxBNgBQ2CZhI6kDrfqa3xWQYI3UOnnjdwccnFzAxTxCF4VPMAv2ln390xq3gDx8ivb6PSZKW+v5e4uh7feGqBtP+ujI91OZZ+5+FyNBSBs0/vB7iJ9d/UhuuKqPF0yP0dTpppkc2f6wL4yvfqLftrFGGFPPjPOiQUPHa6hDNfxuKWIlnItu+KGQ+e75eIu9iDL72VVNB+iGrtQKlV2wM/lsW+cwa8ydvgYtuXiwmqhpkY1VPF2gDJjgbfiZnMmt5VdryIAkb29qZWZUM2a2jHDu+m7BSOVmbPuX09+TyUX5RraKdldrkE9zJus7utsVRwfpcMFkspUjmK4Cv9w70gPvmI7ERfdKKcbd2AoFgOFSnvRYnJDEoMNk4kvsB6DuVXxLPj/ym3oT0pHuxmKQwaQSprgzpWAGidpsPj+eNgkEJ8Kl9xDFW753gb7VmZjZ+e5GA/aaxjKlXwwTyX1hEdgkEt7B7mlFK8WGw2X3jJc89okNKGVohelRCHAXOI+Ib2l45RyDQ2j3LHYgYEOEUEgDATAJXBKJy8tQmcde4ftFHoT2Y7WQUV0G44AOAQugVM6xrUIfSi//WMe9XkbbR8M3MlHMFDlwCCH3ganYiP0li3f5DlVt1M6hjpFILpAoMIht7PCKXVMtDqFMGuZ7tO2PfA3XD3zqCauTxFBICgCBjGHSuBSUM2R6bWaHMhs+pzpb7quvd0yB2c1RlqQvwWBGgiAO+AQuFQjac3T2oRes8bg9Rz0sCEzGTXBlgTVEfC4wxzyuFQ9Sd1HtZscsFSw27a6Rt8e08jMclxeMCAiCNSJgGmkqWT37yna+a11qoybTLuGRu6bO6cddV3jATgnQx0MiGpXvwX1wBlwBxwal6l1ngyF0LDFV9n6st33vmkOrnip0wFJ1roIgCvgDLgTFgqhEXpz57lH+VL7W164GZZvks8ER8DjCnPG405IsYbKvp78jCfKzsBLKastJPckm4mKADgCroAzYcYYKqG3bDFs13C+w1OY3YbRQs86hVkiLZAXuAGOgCvgTJghR7Iq9o5F+1eaVuZB2ymyrzLZEmaBNX9eBk/GZcixi3dteH7GQ2HHE2oN7Tu34fnp62xn4FFpeviIyLePADgBboAj/rEwvyMhNJ50NK22VdyDfUVIHWZxNXdeXruZOQFugCNRRBNJk8N3dNnC/TMylvkSD89cwHP1/mH5bkEELCtHjlPaXbSdqzd3ztgfFQSREhpOf+u6AxekyHjWNFOzhNRRFWOy862QubynTO7iTc+dvjtKbyMnNJy/Y+HHF5KZ2cI19YVle+wNYaIMVPKOBwE0M7hmfpec4tINnWe8G7UXEbWhh7vtBVLqvda2Cy+nrDyfbMh1NNwJ+avBCBiEskaZE5d9I8iMABtCaBja8NI5+0pu32JudmzEsI2MUwOViSkoW5QxyhpljrJvVKSxVJW3L9p/FzP6Xg56atnrLEbS4W0UhmLnOAKolXM8LFfs4mUQ92x8fsaDx0816EcshEZsty/adxEPsd/HV/NCl/hpX0eWnTaozCMxg4VGWJvBC/U7XbJXb3x+ptZ2BKpOxkZoOLyGXPOjRZ8s49r6HsvInO84ZX6MXYitWphx6GH5J49gke0W32M233vm89M3ryF+6CMmiZXQfsxLr9ozOZ/OLmdir+Ar/TwGBrctPi1NER+jZH1Xpq+5vHBn/TWX17q+UmH9lp/NOhK3n4kgtA8CiN2RzS5xXONOfnnK5ZbVlnZcHr30PrFd9L57Lf2NfTPwZLbJH9vuL/FOmK+ahvtIT6GwNQlE9gsnUYT2ncL3Ldd8NNe00osN172WK+zZlpXuQO+Z22iDHxDc3zdTavKh2Kn/rmxoi2HVCoEt/q5gbtsl7DT7tmsYLzh26dnHXjxzp7qd6DQTS+gTIbvGbQv3nc2rQ+YymPO45r6YO5FnMeinsPPtTOUsag0RfQRwN2RMC4xpL1cWn3Mnby/XxG9ypbKDl3rufLRz5oeMe6JrjyYg9OiCWrXw/ewxIzWVytkpZDlTyOWxIhr7lQujc5AjoxHgSsHgMVTb7KZUoXuSW+5aW9lhdnRSOSIICAKCgCAgCAgCgoAgIAgIAoKAICAICAKCgCAgCAgCgoAgIAgIAoKAICAICAKCgCAgCAgCgoAgIAgIAoKAICAICAJNjMD/AxMAEbVbwVdUAAAAAElFTkSuQmCC";
|
|
22307
23170
|
|
|
23171
|
+
// src/webui/fmt/manifest.ts
|
|
23172
|
+
var FMT_PROVIDERS = [
|
|
23173
|
+
{ id: "core", js: "/webui-fmt.js", entry: "fmt/fmt-core.ts" },
|
|
23174
|
+
{
|
|
23175
|
+
id: "ruff",
|
|
23176
|
+
js: "/webui-fmt-ruff.js",
|
|
23177
|
+
entry: "fmt/fmt-ruff.ts",
|
|
23178
|
+
wasm: { route: "/webui-fmt-ruff.wasm", pkg: "@wasm-fmt/ruff_fmt", file: "ruff_fmt_bg.wasm" }
|
|
23179
|
+
},
|
|
23180
|
+
{
|
|
23181
|
+
id: "gofmt",
|
|
23182
|
+
js: "/webui-fmt-gofmt.js",
|
|
23183
|
+
entry: "fmt/fmt-gofmt.ts",
|
|
23184
|
+
wasm: { route: "/webui-fmt-gofmt.wasm", pkg: "@wasm-fmt/gofmt", file: "gofmt.wasm" }
|
|
23185
|
+
},
|
|
23186
|
+
{
|
|
23187
|
+
id: "clang",
|
|
23188
|
+
js: "/webui-fmt-clang.js",
|
|
23189
|
+
entry: "fmt/fmt-clang.ts",
|
|
23190
|
+
wasm: { route: "/webui-fmt-clang.wasm", pkg: "@wasm-fmt/clang-format", file: "clang-format.wasm" }
|
|
23191
|
+
},
|
|
23192
|
+
{
|
|
23193
|
+
id: "lua",
|
|
23194
|
+
js: "/webui-fmt-lua.js",
|
|
23195
|
+
entry: "fmt/fmt-lua.ts",
|
|
23196
|
+
wasm: { route: "/webui-fmt-lua.wasm", pkg: "@wasm-fmt/lua_fmt", file: "lua_fmt_bg.wasm" }
|
|
23197
|
+
},
|
|
23198
|
+
{
|
|
23199
|
+
id: "taplo",
|
|
23200
|
+
js: "/webui-fmt-taplo.js",
|
|
23201
|
+
entry: "fmt/fmt-taplo.ts",
|
|
23202
|
+
wasm: { route: "/webui-fmt-taplo.wasm", pkg: "@wasm-fmt/taplo_fmt", file: "taplo_fmt_bg.wasm" }
|
|
23203
|
+
},
|
|
23204
|
+
{ id: "sh", js: "/webui-fmt-sh.js", entry: "fmt/fmt-sh.ts" }
|
|
23205
|
+
];
|
|
23206
|
+
|
|
22308
23207
|
// src/webui/server/assets.ts
|
|
22309
23208
|
var RUNNING_FROM_SOURCE = import.meta.url.includes("/src/webui/");
|
|
22310
23209
|
var HTML2 = `<!doctype html>
|
|
@@ -22349,14 +23248,25 @@ var injected = {
|
|
|
22349
23248
|
sdk: null
|
|
22350
23249
|
};
|
|
22351
23250
|
var injectedWasm = null;
|
|
23251
|
+
var injectedFmtWasm = {};
|
|
23252
|
+
function toArrayBuffer(w) {
|
|
23253
|
+
return w.byteOffset === 0 && w.byteLength === w.buffer.byteLength ? w.buffer : w.slice().buffer;
|
|
23254
|
+
}
|
|
22352
23255
|
function setWebuiBundle(bundle) {
|
|
22353
23256
|
injected.js = bundle.js;
|
|
22354
23257
|
injected.sw = bundle.sw;
|
|
22355
23258
|
injected.dbWorker = bundle.dbWorker;
|
|
22356
23259
|
injected.runtime = bundle.runtime;
|
|
22357
23260
|
injected.sdk = bundle.sdk;
|
|
22358
|
-
|
|
22359
|
-
|
|
23261
|
+
injectedWasm = toArrayBuffer(bundle.wasm);
|
|
23262
|
+
for (const p of FMT_PROVIDERS) {
|
|
23263
|
+
const js = bundle.fmt?.[p.js];
|
|
23264
|
+
if (js != null)
|
|
23265
|
+
injected[`fmt_${p.id}`] = js;
|
|
23266
|
+
const w = p.wasm && bundle.fmtWasm?.[p.wasm.route];
|
|
23267
|
+
if (p.wasm && w)
|
|
23268
|
+
injectedFmtWasm[p.wasm.route] = toArrayBuffer(w);
|
|
23269
|
+
}
|
|
22360
23270
|
}
|
|
22361
23271
|
function newestSourceMtime(dir) {
|
|
22362
23272
|
let newest = 0;
|
|
@@ -22406,6 +23316,24 @@ function bundleGetter(opts) {
|
|
|
22406
23316
|
}
|
|
22407
23317
|
var getJs = bundleGetter({ key: "js", entry: "../app.tsx", dist: "webui.js" });
|
|
22408
23318
|
var getSwRaw = bundleGetter({ key: "sw", entry: "../sw.ts", dist: "sw.js" });
|
|
23319
|
+
var fmtJs = {};
|
|
23320
|
+
for (const p of FMT_PROVIDERS) {
|
|
23321
|
+
injected[`fmt_${p.id}`] = null;
|
|
23322
|
+
fmtJs[p.js] = bundleGetter({ key: `fmt_${p.id}`, entry: `../${p.entry}`, dist: p.js.slice(1) });
|
|
23323
|
+
}
|
|
23324
|
+
var fmtWasmByRoute = Object.fromEntries(FMT_PROVIDERS.filter((p) => p.wasm).map((p) => [p.wasm.route, p]));
|
|
23325
|
+
async function getFmtWasm(p) {
|
|
23326
|
+
const w = p.wasm;
|
|
23327
|
+
const embedded = injectedFmtWasm[w.route];
|
|
23328
|
+
if (embedded != null)
|
|
23329
|
+
return embedded;
|
|
23330
|
+
const path = RUNNING_FROM_SOURCE ? join4(dirname(Bun.resolveSync(w.pkg, fileURLToPath(new URL(".", import.meta.url)))), w.file) : fileURLToPath(new URL(`.${w.route}`, import.meta.url));
|
|
23331
|
+
const f = Bun.file(path);
|
|
23332
|
+
if (!await f.exists()) {
|
|
23333
|
+
throw new Error(`${w.route} missing at ${path} \u2014 run \`bun install\` / \`bun run build\``);
|
|
23334
|
+
}
|
|
23335
|
+
return f.arrayBuffer();
|
|
23336
|
+
}
|
|
22409
23337
|
var getDbWorker = bundleGetter({ key: "dbWorker", entry: "../data/db-worker.ts", dist: "db-worker.js" });
|
|
22410
23338
|
var getRuntime = bundleGetter({ key: "runtime", entry: "../runtime.ts", dist: "mh-runtime.js" });
|
|
22411
23339
|
var getSdk = bundleGetter({ key: "sdk", entry: "../../sdk/client.ts", dist: "metahub-sdk.js", extraDirs: ["../../sdk"] });
|
|
@@ -22487,12 +23415,24 @@ async function serveWebui(req) {
|
|
|
22487
23415
|
return new Response(`sqlite3.wasm unavailable: ${e}`, { status: 500 });
|
|
22488
23416
|
}
|
|
22489
23417
|
}
|
|
23418
|
+
const fmtWasmProvider = fmtWasmByRoute[pathname];
|
|
23419
|
+
if (fmtWasmProvider) {
|
|
23420
|
+
try {
|
|
23421
|
+
return new Response(await getFmtWasm(fmtWasmProvider), {
|
|
23422
|
+
headers: { "content-type": "application/wasm", "cache-control": "public, max-age=86400" }
|
|
23423
|
+
});
|
|
23424
|
+
} catch (e) {
|
|
23425
|
+
console.error(`[webui] failed to serve ${pathname} \u2014`, e);
|
|
23426
|
+
return new Response(`${pathname} unavailable: ${e}`, { status: 500 });
|
|
23427
|
+
}
|
|
23428
|
+
}
|
|
22490
23429
|
const script = {
|
|
22491
23430
|
"/webui.js": getJsStamped,
|
|
22492
23431
|
"/sw.js": getSw,
|
|
22493
23432
|
"/db-worker.js": getDbWorker,
|
|
22494
23433
|
"/mh-runtime.js": getRuntime,
|
|
22495
|
-
"/metahub-sdk.js": getSdk
|
|
23434
|
+
"/metahub-sdk.js": getSdk,
|
|
23435
|
+
...fmtJs
|
|
22496
23436
|
};
|
|
22497
23437
|
const getter = script[pathname];
|
|
22498
23438
|
if (getter) {
|
|
@@ -24370,6 +25310,7 @@ export {
|
|
|
24370
25310
|
ensureDirs,
|
|
24371
25311
|
emitFields,
|
|
24372
25312
|
emit,
|
|
25313
|
+
editDocumentBatch,
|
|
24373
25314
|
editDocument,
|
|
24374
25315
|
duplicateDocument,
|
|
24375
25316
|
duplicateDatabase,
|
|
@@ -24422,4 +25363,4 @@ export {
|
|
|
24422
25363
|
BLOB_HASH_HEX
|
|
24423
25364
|
};
|
|
24424
25365
|
|
|
24425
|
-
//# debugId=
|
|
25366
|
+
//# debugId=6391E3807369E20264756E2164756E21
|