@tensorix/metahub 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1195 -267
- package/dist/cli.js.map +19 -15
- package/dist/core/databases.d.ts +5 -1
- package/dist/core/databases.d.ts.map +1 -1
- package/dist/core/documents.d.ts +19 -0
- package/dist/core/documents.d.ts.map +1 -1
- package/dist/core/md/grammar.d.ts +40 -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 +1094 -254
- package/dist/index.js.map +15 -11
- 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();
|
|
@@ -1389,6 +1405,33 @@ var init_documents = __esm(() => {
|
|
|
1389
1405
|
reconcileBody(db, id, newBody);
|
|
1390
1406
|
return { id, changed: newBody !== body, replaced: opts.replaceAll ? count : 1, version: documentVersion(db, id) };
|
|
1391
1407
|
});
|
|
1408
|
+
editDocumentBatch = grouped(function editDocumentBatch2(db, id, opts) {
|
|
1409
|
+
if (!getDocument(db, id))
|
|
1410
|
+
throw new MhError("not_found", `no such document: ${id}`);
|
|
1411
|
+
if (opts.edits.length === 0)
|
|
1412
|
+
throw new MhError("invalid_input", "no edits given");
|
|
1413
|
+
if (opts.ifMatch !== undefined && documentVersion(db, id) !== opts.ifMatch)
|
|
1414
|
+
throw new MhError("stale", `stale: document changed since ${opts.ifMatch}; re-read first`);
|
|
1415
|
+
ensureBlocks(db, id);
|
|
1416
|
+
const orig = serializeDocBlocks(liveBlocks(db, id).map((b) => ({ text: b.text, blankAfter: b.blank_after })));
|
|
1417
|
+
let body = orig;
|
|
1418
|
+
let total = 0;
|
|
1419
|
+
opts.edits.forEach((e, i) => {
|
|
1420
|
+
if (e.old === "")
|
|
1421
|
+
throw new MhError("invalid_input", `edit[${i}]: old must not be empty`);
|
|
1422
|
+
const count = countOccurrences(body, e.old);
|
|
1423
|
+
if (count === 0)
|
|
1424
|
+
throw new MhError("invalid_input", `edit[${i}]: anchor not found: no match for old`);
|
|
1425
|
+
if (count > 1 && !e.replaceAll)
|
|
1426
|
+
throw new MhError("ambiguous", `edit[${i}]: ${count} matches for old; add surrounding context or set replaceAll`);
|
|
1427
|
+
const repl = e.new ?? "";
|
|
1428
|
+
body = e.replaceAll ? body.replaceAll(e.old, repl) : body.replace(e.old, repl);
|
|
1429
|
+
total += e.replaceAll ? count : 1;
|
|
1430
|
+
});
|
|
1431
|
+
if (body !== orig)
|
|
1432
|
+
reconcileBody(db, id, body);
|
|
1433
|
+
return { id, changed: body !== orig, replaced: total, version: documentVersion(db, id) };
|
|
1434
|
+
});
|
|
1392
1435
|
appendDocument = grouped(function appendDocument2(db, id, body) {
|
|
1393
1436
|
if (!getDocument(db, id))
|
|
1394
1437
|
throw new MhError("not_found", `no such document: ${id}`);
|
|
@@ -18759,29 +18802,280 @@ var init_shares = __esm(() => {
|
|
|
18759
18802
|
SHARE_KINDS = new Set(["doc", "database", "site"]);
|
|
18760
18803
|
});
|
|
18761
18804
|
|
|
18805
|
+
// src/core/md/grammar.ts
|
|
18806
|
+
function matchQuoteLine(text) {
|
|
18807
|
+
const m = text.match(RE.quote);
|
|
18808
|
+
return m ? m[1] : null;
|
|
18809
|
+
}
|
|
18810
|
+
function matchListLine(line, minIndent) {
|
|
18811
|
+
const indent = leadingIndent(line);
|
|
18812
|
+
if (indent < minIndent)
|
|
18813
|
+
return null;
|
|
18814
|
+
const text = stripIndent(line, indent);
|
|
18815
|
+
let m = text.match(/^[-*+]\s+\[([ xX])\][ \t](.*)$/);
|
|
18816
|
+
if (m) {
|
|
18817
|
+
return {
|
|
18818
|
+
indent,
|
|
18819
|
+
type: "todo",
|
|
18820
|
+
checked: m[1].toLowerCase() === "x",
|
|
18821
|
+
content: m[2]
|
|
18822
|
+
};
|
|
18823
|
+
}
|
|
18824
|
+
m = text.match(/^(\d+)[.)][ \t]+(.*)$/);
|
|
18825
|
+
if (m)
|
|
18826
|
+
return { indent, type: "numbered", content: m[2], num: parseInt(m[1], 10) };
|
|
18827
|
+
m = text.match(/^[-*+][ \t]+(.*)$/);
|
|
18828
|
+
if (m)
|
|
18829
|
+
return { indent, type: "bullet", content: m[1] };
|
|
18830
|
+
return null;
|
|
18831
|
+
}
|
|
18832
|
+
function leadingIndent(line) {
|
|
18833
|
+
let indent = 0;
|
|
18834
|
+
for (const ch of line) {
|
|
18835
|
+
if (ch === " ")
|
|
18836
|
+
indent++;
|
|
18837
|
+
else if (ch === "\t")
|
|
18838
|
+
indent += 4;
|
|
18839
|
+
else
|
|
18840
|
+
break;
|
|
18841
|
+
}
|
|
18842
|
+
return indent;
|
|
18843
|
+
}
|
|
18844
|
+
function stripIndent(line, columns) {
|
|
18845
|
+
let indent = 0;
|
|
18846
|
+
let i = 0;
|
|
18847
|
+
for (;i < line.length && indent < columns; i++) {
|
|
18848
|
+
const ch = line[i];
|
|
18849
|
+
if (ch === " ")
|
|
18850
|
+
indent++;
|
|
18851
|
+
else if (ch === "\t")
|
|
18852
|
+
indent += 4;
|
|
18853
|
+
else
|
|
18854
|
+
break;
|
|
18855
|
+
}
|
|
18856
|
+
return line.slice(i);
|
|
18857
|
+
}
|
|
18858
|
+
function isFenceClose(line, char, len) {
|
|
18859
|
+
const trimmed = line.trim();
|
|
18860
|
+
if (!trimmed || trimmed[0] !== char)
|
|
18861
|
+
return false;
|
|
18862
|
+
for (const ch of trimmed) {
|
|
18863
|
+
if (ch !== char)
|
|
18864
|
+
return false;
|
|
18865
|
+
}
|
|
18866
|
+
return trimmed.length >= len;
|
|
18867
|
+
}
|
|
18868
|
+
function cleanLang(lang) {
|
|
18869
|
+
return lang.trim().replace(/[^A-Za-z0-9_+.#-]/g, "");
|
|
18870
|
+
}
|
|
18871
|
+
function looksLikeTableAt(lines, i, minIndent) {
|
|
18872
|
+
const head = lines[i];
|
|
18873
|
+
const delim = lines[i + 1];
|
|
18874
|
+
if (head == null || delim == null)
|
|
18875
|
+
return false;
|
|
18876
|
+
if (leadingIndent(head) < minIndent || leadingIndent(delim) < minIndent)
|
|
18877
|
+
return false;
|
|
18878
|
+
const headText = stripIndent(head, minIndent);
|
|
18879
|
+
const delimText = stripIndent(delim, minIndent);
|
|
18880
|
+
if (!headText.includes("|") || !delimText.includes("|"))
|
|
18881
|
+
return false;
|
|
18882
|
+
const cells = splitTableRow(delimText);
|
|
18883
|
+
return cells.length > 0 && cells.every((c) => RE_DELIM_CELL.test(c));
|
|
18884
|
+
}
|
|
18885
|
+
function splitTableRow(line) {
|
|
18886
|
+
const cells = [];
|
|
18887
|
+
let cur = "";
|
|
18888
|
+
for (let i = 0;i < line.length; i++) {
|
|
18889
|
+
const ch = line[i];
|
|
18890
|
+
if (ch === "\\" && line[i + 1] === "|") {
|
|
18891
|
+
cur += "|";
|
|
18892
|
+
i++;
|
|
18893
|
+
continue;
|
|
18894
|
+
}
|
|
18895
|
+
if (ch === "|") {
|
|
18896
|
+
cells.push(cur);
|
|
18897
|
+
cur = "";
|
|
18898
|
+
continue;
|
|
18899
|
+
}
|
|
18900
|
+
cur += ch;
|
|
18901
|
+
}
|
|
18902
|
+
cells.push(cur);
|
|
18903
|
+
if (cells.length && cells[0].trim() === "")
|
|
18904
|
+
cells.shift();
|
|
18905
|
+
if (cells.length && cells[cells.length - 1].trim() === "")
|
|
18906
|
+
cells.pop();
|
|
18907
|
+
return cells.map((c) => c.trim());
|
|
18908
|
+
}
|
|
18909
|
+
var HTML_FENCE = "mh-html", RE, RE_DELIM_CELL;
|
|
18910
|
+
var init_grammar = __esm(() => {
|
|
18911
|
+
RE = {
|
|
18912
|
+
fenceOpen: /^\s*(`{3,}|~{3,})\s*([^\s`]*)?.*$/,
|
|
18913
|
+
h: /^(#{1,6})\s+(.*)$/,
|
|
18914
|
+
todo: /^\s*[-*+]\s+\[([ xX])\][ \t](.*)$/,
|
|
18915
|
+
bullet: /^\s*[-*+]\s+(.*)$/,
|
|
18916
|
+
numbered: /^\s*(\d+)[.)]\s+(.*)$/,
|
|
18917
|
+
quote: /^>[ \t](.*)$/,
|
|
18918
|
+
divider: /^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/
|
|
18919
|
+
};
|
|
18920
|
+
RE_DELIM_CELL = /^\s*:?-+:?\s*$/;
|
|
18921
|
+
});
|
|
18922
|
+
|
|
18923
|
+
// src/core/md/inline.ts
|
|
18924
|
+
function tokenizeInline(text) {
|
|
18925
|
+
if (!text)
|
|
18926
|
+
return [];
|
|
18927
|
+
const cands = [];
|
|
18928
|
+
for (const p of PATTERNS) {
|
|
18929
|
+
p.re.lastIndex = 0;
|
|
18930
|
+
let m;
|
|
18931
|
+
while (m = p.re.exec(text)) {
|
|
18932
|
+
const start = m.index;
|
|
18933
|
+
const end = start + m[0].length;
|
|
18934
|
+
let innerFrom;
|
|
18935
|
+
let innerTo;
|
|
18936
|
+
let url2;
|
|
18937
|
+
let alt;
|
|
18938
|
+
if (p.kind === "link") {
|
|
18939
|
+
innerFrom = start + 1;
|
|
18940
|
+
innerTo = start + 1 + m[1].length;
|
|
18941
|
+
url2 = m[2];
|
|
18942
|
+
} else if (p.kind === "image") {
|
|
18943
|
+
innerFrom = start + 2;
|
|
18944
|
+
innerTo = start + 2 + m[1].length;
|
|
18945
|
+
url2 = m[2];
|
|
18946
|
+
alt = m[1];
|
|
18947
|
+
} else {
|
|
18948
|
+
innerFrom = start + p.delim;
|
|
18949
|
+
innerTo = end - p.delim;
|
|
18950
|
+
if (innerTo <= innerFrom)
|
|
18951
|
+
continue;
|
|
18952
|
+
}
|
|
18953
|
+
cands.push({ kind: p.kind, start, end, innerFrom, innerTo, url: url2, alt, rank: p.rank });
|
|
18954
|
+
}
|
|
18955
|
+
}
|
|
18956
|
+
cands.sort((a, b) => a.start - b.start || a.rank - b.rank || b.end - a.end);
|
|
18957
|
+
const out = [];
|
|
18958
|
+
let cursor = 0;
|
|
18959
|
+
for (const c of cands) {
|
|
18960
|
+
if (c.start < cursor)
|
|
18961
|
+
continue;
|
|
18962
|
+
cursor = c.end;
|
|
18963
|
+
out.push({
|
|
18964
|
+
kind: c.kind,
|
|
18965
|
+
start: c.start,
|
|
18966
|
+
end: c.end,
|
|
18967
|
+
innerFrom: c.innerFrom,
|
|
18968
|
+
innerTo: c.innerTo,
|
|
18969
|
+
...c.url !== undefined ? { url: c.url } : {},
|
|
18970
|
+
...c.alt !== undefined ? { alt: c.alt } : {}
|
|
18971
|
+
});
|
|
18972
|
+
}
|
|
18973
|
+
return out;
|
|
18974
|
+
}
|
|
18975
|
+
var PATTERNS;
|
|
18976
|
+
var init_inline = __esm(() => {
|
|
18977
|
+
PATTERNS = [
|
|
18978
|
+
{ kind: "code", re: /(?<!\\)`([^`\n]+)`/g, delim: 1, rank: 0 },
|
|
18979
|
+
{ kind: "image", re: /(?<!\\)!\[([^\]\n]*)\]\(([^)\s]+)\)/g, delim: 0, rank: 1 },
|
|
18980
|
+
{ kind: "strong", re: /(?<!\\)\*\*([^*\n]+?)(?<!\\)\*\*/g, delim: 2, rank: 2 },
|
|
18981
|
+
{ kind: "strong", re: /(?<!\\)__([^_\n]+?)(?<!\\)__/g, delim: 2, rank: 2 },
|
|
18982
|
+
{ kind: "del", re: /(?<!\\)~~([^~\n]+?)(?<!\\)~~/g, delim: 2, rank: 3 },
|
|
18983
|
+
{ kind: "em", re: /(?<![*\w])(?<!\\)\*([^*\n]+?)(?<!\\)\*(?!\*)/g, delim: 1, rank: 4 },
|
|
18984
|
+
{ kind: "em", re: /(?<![_\w])(?<!\\)_([^_\n]+?)(?<!\\)_(?!_)/g, delim: 1, rank: 4 },
|
|
18985
|
+
{ kind: "link", re: /(?<!\\)\[([^\]\n]+)\]\(([^)\s]+)\)/g, delim: 0, rank: 5 }
|
|
18986
|
+
];
|
|
18987
|
+
});
|
|
18988
|
+
|
|
18989
|
+
// src/core/md/heal.ts
|
|
18990
|
+
function fenceMask(lines) {
|
|
18991
|
+
const mask = new Array(lines.length).fill(false);
|
|
18992
|
+
for (let i = 0;i < lines.length; i++) {
|
|
18993
|
+
const m = lines[i].match(RE.fenceOpen);
|
|
18994
|
+
if (!m)
|
|
18995
|
+
continue;
|
|
18996
|
+
const ch = m[1][0];
|
|
18997
|
+
const len = m[1].length;
|
|
18998
|
+
let close = -1;
|
|
18999
|
+
for (let j = i + 1;j < lines.length; j++) {
|
|
19000
|
+
if (isFenceClose(lines[j], ch, len)) {
|
|
19001
|
+
close = j;
|
|
19002
|
+
break;
|
|
19003
|
+
}
|
|
19004
|
+
}
|
|
19005
|
+
if (close === -1)
|
|
19006
|
+
continue;
|
|
19007
|
+
for (let j = i;j <= close; j++)
|
|
19008
|
+
mask[j] = true;
|
|
19009
|
+
i = close;
|
|
19010
|
+
}
|
|
19011
|
+
return mask;
|
|
19012
|
+
}
|
|
19013
|
+
function healLegacyMarkdown(src) {
|
|
19014
|
+
if (!MAYBE_LEGACY.test(src))
|
|
19015
|
+
return src;
|
|
19016
|
+
const lines = src.split(`
|
|
19017
|
+
`);
|
|
19018
|
+
const inFence = fenceMask(lines);
|
|
19019
|
+
const quoteish = (i) => i >= 0 && i < lines.length && !inFence[i] && matchQuoteLine(lines[i].replace(/^[ \t]*/, "")) !== null;
|
|
19020
|
+
let changed = false;
|
|
19021
|
+
for (let i = 0;i < lines.length; i++) {
|
|
19022
|
+
if (inFence[i])
|
|
19023
|
+
continue;
|
|
19024
|
+
const line = lines[i];
|
|
19025
|
+
if (LEGACY_EMPTY_TODO.test(line)) {
|
|
19026
|
+
lines[i] = line + " ";
|
|
19027
|
+
changed = true;
|
|
19028
|
+
} else if (BARE_QUOTE.test(line) && (quoteish(i - 1) || quoteish(i + 1))) {
|
|
19029
|
+
lines[i] = line + " ";
|
|
19030
|
+
changed = true;
|
|
19031
|
+
}
|
|
19032
|
+
}
|
|
19033
|
+
return changed ? lines.join(`
|
|
19034
|
+
`) : src;
|
|
19035
|
+
}
|
|
19036
|
+
var LEGACY_EMPTY_TODO, BARE_QUOTE, MAYBE_LEGACY;
|
|
19037
|
+
var init_heal = __esm(() => {
|
|
19038
|
+
init_grammar();
|
|
19039
|
+
LEGACY_EMPTY_TODO = /^[ \t]*[-*+][ \t]+\[[ xX]\]$/;
|
|
19040
|
+
BARE_QUOTE = /^[ \t]*>$/;
|
|
19041
|
+
MAYBE_LEGACY = /^[ \t]*(?:[-*+][ \t]+\[[ xX]\]|>)$/m;
|
|
19042
|
+
});
|
|
19043
|
+
|
|
18762
19044
|
// src/core/sync/share-render.ts
|
|
18763
19045
|
function escapeHtml(s) {
|
|
18764
19046
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
18765
19047
|
}
|
|
18766
19048
|
function renderInline(src, opts = {}) {
|
|
18767
|
-
const
|
|
18768
|
-
let
|
|
18769
|
-
|
|
18770
|
-
|
|
18771
|
-
|
|
18772
|
-
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
18776
|
-
|
|
18777
|
-
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
19049
|
+
const tokens = tokenizeInline(src);
|
|
19050
|
+
let out = "";
|
|
19051
|
+
let pos = 0;
|
|
19052
|
+
for (const t of tokens) {
|
|
19053
|
+
out += escapeHtml(src.slice(pos, t.start));
|
|
19054
|
+
const inner = src.slice(t.innerFrom, t.innerTo);
|
|
19055
|
+
const url2 = rewrite(t.url ?? "", opts).replace(/"/g, """);
|
|
19056
|
+
switch (t.kind) {
|
|
19057
|
+
case "code":
|
|
19058
|
+
out += `<code>${escapeHtml(inner)}</code>`;
|
|
19059
|
+
break;
|
|
19060
|
+
case "strong":
|
|
19061
|
+
out += `<strong>${escapeHtml(inner)}</strong>`;
|
|
19062
|
+
break;
|
|
19063
|
+
case "em":
|
|
19064
|
+
out += `<em>${escapeHtml(inner)}</em>`;
|
|
19065
|
+
break;
|
|
19066
|
+
case "del":
|
|
19067
|
+
out += `<del>${escapeHtml(inner)}</del>`;
|
|
19068
|
+
break;
|
|
19069
|
+
case "link":
|
|
19070
|
+
out += `<a href="${url2}" target="_blank" rel="noreferrer noopener">${escapeHtml(inner)}</a>`;
|
|
19071
|
+
break;
|
|
19072
|
+
case "image":
|
|
19073
|
+
out += `<img src="${url2}" alt="${escapeHtml(t.alt ?? "")}" loading="lazy">`;
|
|
19074
|
+
break;
|
|
19075
|
+
}
|
|
19076
|
+
pos = t.end;
|
|
19077
|
+
}
|
|
19078
|
+
return out + escapeHtml(src.slice(pos));
|
|
18785
19079
|
}
|
|
18786
19080
|
function rewrite(url2, opts) {
|
|
18787
19081
|
if (opts.rewriteBlob && url2.startsWith("/blob/"))
|
|
@@ -18789,8 +19083,9 @@ function rewrite(url2, opts) {
|
|
|
18789
19083
|
return url2;
|
|
18790
19084
|
}
|
|
18791
19085
|
function renderMarkdown(md, opts = {}) {
|
|
18792
|
-
const
|
|
18793
|
-
`)
|
|
19086
|
+
const healed = healLegacyMarkdown((md ?? "").replace(/\r\n?/g, `
|
|
19087
|
+
`));
|
|
19088
|
+
const lines = healed.split(`
|
|
18794
19089
|
`);
|
|
18795
19090
|
const out = [];
|
|
18796
19091
|
let i = 0;
|
|
@@ -18803,38 +19098,39 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18803
19098
|
};
|
|
18804
19099
|
while (i < lines.length) {
|
|
18805
19100
|
const line = lines[i];
|
|
18806
|
-
const fence = line.match(
|
|
19101
|
+
const fence = line.match(RE.fenceOpen);
|
|
18807
19102
|
if (fence) {
|
|
18808
|
-
flushPara();
|
|
18809
19103
|
const marker = fence[1][0];
|
|
18810
|
-
const
|
|
18811
|
-
|
|
18812
|
-
i
|
|
18813
|
-
|
|
18814
|
-
|
|
18815
|
-
if (m && m[1][0] === marker) {
|
|
18816
|
-
i++;
|
|
19104
|
+
const len = fence[1].length;
|
|
19105
|
+
let close = -1;
|
|
19106
|
+
for (let j = i + 1;j < lines.length; j++) {
|
|
19107
|
+
if (isFenceClose(lines[j], marker, len)) {
|
|
19108
|
+
close = j;
|
|
18817
19109
|
break;
|
|
18818
19110
|
}
|
|
18819
|
-
body.push(lines[i]);
|
|
18820
|
-
i++;
|
|
18821
19111
|
}
|
|
18822
|
-
if (
|
|
18823
|
-
|
|
19112
|
+
if (close !== -1) {
|
|
19113
|
+
flushPara();
|
|
19114
|
+
const info = cleanLang(fence[2] ?? "");
|
|
19115
|
+
const body = lines.slice(i + 1, close);
|
|
19116
|
+
i = close + 1;
|
|
19117
|
+
if (info === HTML_FENCE) {
|
|
19118
|
+
out.push(`<iframe class="mh-embed" sandbox="allow-scripts allow-popups" srcdoc="${escapeHtml(body.join(`
|
|
18824
19119
|
`))}"></iframe>`);
|
|
18825
|
-
|
|
18826
|
-
|
|
18827
|
-
|
|
19120
|
+
} else {
|
|
19121
|
+
const cls = info ? ` class="language-${escapeHtml(info)}"` : "";
|
|
19122
|
+
out.push(`<pre><code${cls}>${escapeHtml(body.join(`
|
|
18828
19123
|
`))}</code></pre>`);
|
|
19124
|
+
}
|
|
19125
|
+
continue;
|
|
18829
19126
|
}
|
|
18830
|
-
continue;
|
|
18831
19127
|
}
|
|
18832
19128
|
if (line.trim() === "") {
|
|
18833
19129
|
flushPara();
|
|
18834
19130
|
i++;
|
|
18835
19131
|
continue;
|
|
18836
19132
|
}
|
|
18837
|
-
const h = line.match(
|
|
19133
|
+
const h = stripLead(line).match(RE.h);
|
|
18838
19134
|
if (h) {
|
|
18839
19135
|
flushPara();
|
|
18840
19136
|
const level = h[1].length;
|
|
@@ -18842,30 +19138,33 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18842
19138
|
i++;
|
|
18843
19139
|
continue;
|
|
18844
19140
|
}
|
|
18845
|
-
if (
|
|
19141
|
+
if (RE.divider.test(line)) {
|
|
18846
19142
|
flushPara();
|
|
18847
19143
|
out.push("<hr>");
|
|
18848
19144
|
i++;
|
|
18849
19145
|
continue;
|
|
18850
19146
|
}
|
|
18851
|
-
if (
|
|
19147
|
+
if (matchQuoteLine(stripLead(line)) !== null) {
|
|
18852
19148
|
flushPara();
|
|
18853
19149
|
const q = [];
|
|
18854
|
-
while (i < lines.length
|
|
18855
|
-
|
|
19150
|
+
while (i < lines.length) {
|
|
19151
|
+
const content = matchQuoteLine(stripLead(lines[i]));
|
|
19152
|
+
if (content === null)
|
|
19153
|
+
break;
|
|
19154
|
+
q.push(content);
|
|
18856
19155
|
i++;
|
|
18857
19156
|
}
|
|
18858
19157
|
out.push(`<blockquote>${renderMarkdown(q.join(`
|
|
18859
19158
|
`), opts)}</blockquote>`);
|
|
18860
19159
|
continue;
|
|
18861
19160
|
}
|
|
18862
|
-
if (
|
|
19161
|
+
if (looksLikeTableAt(lines, i, 0)) {
|
|
18863
19162
|
flushPara();
|
|
18864
19163
|
const rows = [];
|
|
18865
|
-
const head =
|
|
19164
|
+
const head = splitTableRow(stripLead(lines[i]));
|
|
18866
19165
|
i += 2;
|
|
18867
19166
|
while (i < lines.length && lines[i].includes("|") && lines[i].trim() !== "") {
|
|
18868
|
-
rows.push(
|
|
19167
|
+
rows.push(splitTableRow(stripLead(lines[i])));
|
|
18869
19168
|
i++;
|
|
18870
19169
|
}
|
|
18871
19170
|
const th = head.map((c) => `<th>${renderInline(c, opts)}</th>`).join("");
|
|
@@ -18873,19 +19172,25 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18873
19172
|
out.push(`<table><thead><tr>${th}</tr></thead><tbody>${body}</tbody></table>`);
|
|
18874
19173
|
continue;
|
|
18875
19174
|
}
|
|
18876
|
-
const
|
|
18877
|
-
|
|
18878
|
-
if (ul || ol) {
|
|
19175
|
+
const first = matchListLine(line, 0);
|
|
19176
|
+
if (first) {
|
|
18879
19177
|
flushPara();
|
|
18880
|
-
const ordered =
|
|
18881
|
-
const
|
|
18882
|
-
|
|
18883
|
-
|
|
18884
|
-
|
|
19178
|
+
const ordered = first.type === "numbered";
|
|
19179
|
+
const lis = [];
|
|
19180
|
+
while (i < lines.length) {
|
|
19181
|
+
const it = matchListLine(lines[i], 0);
|
|
19182
|
+
if (!it || it.type === "numbered" !== ordered)
|
|
19183
|
+
break;
|
|
19184
|
+
if (it.type === "todo") {
|
|
19185
|
+
lis.push(`<li class="todo"><input type="checkbox" disabled${it.checked ? " checked" : ""}> ${renderInline(it.content, opts)}</li>`);
|
|
19186
|
+
} else if (it.type === "numbered" && it.num !== undefined) {
|
|
19187
|
+
lis.push(`<li value="${it.num}">${renderInline(it.content, opts)}</li>`);
|
|
19188
|
+
} else {
|
|
19189
|
+
lis.push(`<li>${renderInline(it.content, opts)}</li>`);
|
|
19190
|
+
}
|
|
18885
19191
|
i++;
|
|
18886
19192
|
}
|
|
18887
|
-
|
|
18888
|
-
out.push(ordered ? `<ol>${lis}</ol>` : `<ul>${lis}</ul>`);
|
|
19193
|
+
out.push(ordered ? `<ol>${lis.join("")}</ol>` : `<ul>${lis.join("")}</ul>`);
|
|
18889
19194
|
continue;
|
|
18890
19195
|
}
|
|
18891
19196
|
if (/^\s*!\[[^\]]*\]\([^)\s]+\)\s*$/.test(line)) {
|
|
@@ -18901,20 +19206,13 @@ function renderMarkdown(md, opts = {}) {
|
|
|
18901
19206
|
return out.join(`
|
|
18902
19207
|
`);
|
|
18903
19208
|
}
|
|
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());
|
|
19209
|
+
function stripLead(line) {
|
|
19210
|
+
return stripIndent(line, Number.MAX_SAFE_INTEGER);
|
|
18911
19211
|
}
|
|
18912
|
-
var A, B, RESTORE, FENCE;
|
|
18913
19212
|
var init_share_render = __esm(() => {
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
FENCE = /^\s*(`{3,}|~{3,})(.*)$/;
|
|
19213
|
+
init_grammar();
|
|
19214
|
+
init_inline();
|
|
19215
|
+
init_heal();
|
|
18918
19216
|
});
|
|
18919
19217
|
|
|
18920
19218
|
// src/core/sync/share-serve.ts
|
|
@@ -19509,6 +19807,7 @@ CREATE TABLE IF NOT EXISTS databases (
|
|
|
19509
19807
|
id TEXT PRIMARY KEY,
|
|
19510
19808
|
name TEXT,
|
|
19511
19809
|
icon TEXT,
|
|
19810
|
+
meta TEXT, -- JSON: generic replicated metadata (e.g. UI flags), one LWW register
|
|
19512
19811
|
created_hlc TEXT,
|
|
19513
19812
|
__deleted INTEGER NOT NULL DEFAULT 0
|
|
19514
19813
|
);
|
|
@@ -19791,9 +20090,25 @@ function migrateStoragePeerUrls(db) {
|
|
|
19791
20090
|
});
|
|
19792
20091
|
tx();
|
|
19793
20092
|
}
|
|
20093
|
+
function migrateDatabases(db) {
|
|
20094
|
+
if (hasColumn(db, "databases", "meta"))
|
|
20095
|
+
return;
|
|
20096
|
+
db.exec("ALTER TABLE databases ADD COLUMN meta TEXT");
|
|
20097
|
+
db.exec(`
|
|
20098
|
+
UPDATE databases SET meta = (
|
|
20099
|
+
SELECT c.value FROM crdt_changes c
|
|
20100
|
+
WHERE c.dataset = 'databases' AND c.col = 'meta' AND c.row_id = databases.id
|
|
20101
|
+
ORDER BY c.hlc DESC LIMIT 1
|
|
20102
|
+
)
|
|
20103
|
+
WHERE id IN (
|
|
20104
|
+
SELECT DISTINCT row_id FROM crdt_changes WHERE dataset = 'databases' AND col = 'meta'
|
|
20105
|
+
)
|
|
20106
|
+
`);
|
|
20107
|
+
}
|
|
19794
20108
|
function initSchema(db) {
|
|
19795
20109
|
runSchema(db);
|
|
19796
20110
|
migrateOplog(db);
|
|
20111
|
+
migrateDatabases(db);
|
|
19797
20112
|
migrateCrdtChangesSeq(db);
|
|
19798
20113
|
migrateRecords(db);
|
|
19799
20114
|
migratePeers(db);
|
|
@@ -20033,7 +20348,7 @@ init_config();
|
|
|
20033
20348
|
// package.json
|
|
20034
20349
|
var package_default = {
|
|
20035
20350
|
name: "@tensorix/metahub",
|
|
20036
|
-
version: "0.
|
|
20351
|
+
version: "0.3.1",
|
|
20037
20352
|
description: "Metahub core library and CLI",
|
|
20038
20353
|
license: "AGPL-3.0-only",
|
|
20039
20354
|
type: "module",
|
|
@@ -20063,16 +20378,21 @@ var package_default = {
|
|
|
20063
20378
|
"build:shell": "bun run scripts/build-shell.ts",
|
|
20064
20379
|
"build:share-viewer": "bun run scripts/build-share-viewer.ts",
|
|
20065
20380
|
"build:binaries": "bun run scripts/compile-binaries.ts",
|
|
20381
|
+
"build:binaries:host": "HOST_ONLY=1 bun run scripts/compile-binaries.ts",
|
|
20066
20382
|
prepublishOnly: "bun run build",
|
|
20067
20383
|
test: "bun test",
|
|
20068
20384
|
release: "bun run scripts/tag-release.ts"
|
|
20069
20385
|
},
|
|
20070
20386
|
dependencies: {
|
|
20071
20387
|
"@clack/prompts": "^1.5.0",
|
|
20388
|
+
"@codemirror/commands": "6.10.4",
|
|
20389
|
+
"@codemirror/state": "6.7.0",
|
|
20390
|
+
"@codemirror/view": "6.43.4",
|
|
20072
20391
|
"@sqlite.org/sqlite-wasm": "^3.53.0-build1",
|
|
20073
20392
|
aws4fetch: "^1.0.20",
|
|
20074
20393
|
citty: "^0.1.6",
|
|
20075
20394
|
"highlight.js": "^11.11.1",
|
|
20395
|
+
jsqr: "^1.4.0",
|
|
20076
20396
|
preact: "^10.29.2",
|
|
20077
20397
|
"qrcode-generator": "^2.0.4",
|
|
20078
20398
|
turndown: "^7.2.4",
|
|
@@ -20081,8 +20401,17 @@ var package_default = {
|
|
|
20081
20401
|
},
|
|
20082
20402
|
devDependencies: {
|
|
20083
20403
|
"@happy-dom/global-registrator": "^20.10.2",
|
|
20404
|
+
"@prettier/plugin-php": "^0.25.0",
|
|
20084
20405
|
"@types/bun": "latest",
|
|
20085
20406
|
"@types/turndown": "^5.0.6",
|
|
20407
|
+
"@wasm-fmt/clang-format": "^22.1.8",
|
|
20408
|
+
"@wasm-fmt/gofmt": "^0.7.3",
|
|
20409
|
+
"@wasm-fmt/lua_fmt": "^0.3.3",
|
|
20410
|
+
"@wasm-fmt/ruff_fmt": "^0.15.20",
|
|
20411
|
+
"@wasm-fmt/taplo_fmt": "^0.2.5",
|
|
20412
|
+
"mvdan-sh": "^0.10.1",
|
|
20413
|
+
prettier: "^3.6",
|
|
20414
|
+
"sql-formatter": "^15",
|
|
20086
20415
|
typescript: "^5"
|
|
20087
20416
|
}
|
|
20088
20417
|
};
|
|
@@ -20208,56 +20537,196 @@ function wantsHtml(req) {
|
|
|
20208
20537
|
return req.method === "GET" && (req.headers.get("accept") ?? "").includes("text/html");
|
|
20209
20538
|
}
|
|
20210
20539
|
var HTML_HEADERS = { "content-type": "text/html; charset=utf-8" };
|
|
20540
|
+
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"/>';
|
|
20541
|
+
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"/>';
|
|
20542
|
+
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"/>';
|
|
20543
|
+
var UNLOCK_CSS = `
|
|
20544
|
+
:root{color-scheme:light;
|
|
20545
|
+
--bg:#ffffff;--surface:#fbfbfa;--surface-2:#f1f1ef;--fg:#2c2c30;--fg-soft:#5b5b62;
|
|
20546
|
+
--muted:#75757f;--line:#ebebe8;--line-strong:#dededb;--accent:#4a55d6;--accent-fg:#ffffff;
|
|
20547
|
+
--accent-soft:#eef0ff;--danger:#d6473b;--danger-soft:#fdeceb;--code-bg:#f8f8f6}
|
|
20548
|
+
:root[data-resolved="dark"]{color-scheme:dark;
|
|
20549
|
+
--bg:#1a1a1c;--surface:#202022;--surface-2:#2a2a2d;--fg:#e6e6e9;--fg-soft:#b4b4bb;
|
|
20550
|
+
--muted:#8b8b95;--line:#2c2c30;--line-strong:#3a3a40;--accent:#7b86ff;--accent-fg:#14141a;
|
|
20551
|
+
--accent-soft:#23243a;--danger:#f87168;--danger-soft:#341e1c;--code-bg:#26262a}
|
|
20552
|
+
*{box-sizing:border-box}
|
|
20553
|
+
html,body{margin:0;min-height:100%}
|
|
20554
|
+
body{font-family:"Hanken Grotesk",-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
|
|
20555
|
+
color:var(--fg);background:var(--bg);line-height:1.55;
|
|
20556
|
+
display:flex;align-items:center;justify-content:center;min-height:100dvh;
|
|
20557
|
+
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));
|
|
20558
|
+
-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;overflow:hidden}
|
|
20559
|
+
/* Ambient wash: two faint accent blooms behind the card. */
|
|
20560
|
+
body::before{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
|
|
20561
|
+
background:
|
|
20562
|
+
radial-gradient(58vmax 58vmax at 14% -8%,color-mix(in srgb,var(--accent) 13%,transparent),transparent 60%),
|
|
20563
|
+
radial-gradient(50vmax 50vmax at 108% 112%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 55%)}
|
|
20564
|
+
.card{position:relative;z-index:1;width:100%;max-width:420px;background:var(--surface);
|
|
20565
|
+
border:1px solid var(--line);border-radius:18px;padding:32px 26px 26px;
|
|
20566
|
+
box-shadow:0 24px 60px rgba(20,20,40,.14),0 4px 14px rgba(20,20,40,.06);
|
|
20567
|
+
animation:rise .45s cubic-bezier(.2,.7,.3,1) both}
|
|
20568
|
+
@keyframes rise{from{opacity:0;transform:translateY(12px) scale(.985)}to{opacity:1;transform:none}}
|
|
20569
|
+
.brand{width:52px;height:52px;border-radius:14px;display:flex;align-items:center;justify-content:center;
|
|
20570
|
+
background:var(--accent-soft);color:var(--accent);margin-bottom:18px}
|
|
20571
|
+
.brand svg{width:28px;height:28px;fill:none;stroke:currentColor;stroke-width:1.6;
|
|
20572
|
+
stroke-linecap:round;stroke-linejoin:round}
|
|
20573
|
+
.brand path{stroke-dasharray:120;stroke-dashoffset:120;animation:draw .9s .12s ease forwards}
|
|
20574
|
+
@keyframes draw{to{stroke-dashoffset:0}}
|
|
20575
|
+
h1{font-size:21px;font-weight:650;letter-spacing:-.01em;margin:0 0 8px}
|
|
20576
|
+
.sub{color:var(--muted);font-size:13.5px;margin:0 0 22px;line-height:1.6}
|
|
20577
|
+
.sub code{font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,monospace;font-size:12.5px;
|
|
20578
|
+
background:var(--code-bg);border:1px solid var(--line);border-radius:5px;padding:1px 6px;color:var(--fg-soft);
|
|
20579
|
+
white-space:nowrap}
|
|
20580
|
+
.field{position:relative;display:flex;align-items:center}
|
|
20581
|
+
input{width:100%;height:50px;padding:0 48px 0 15px;font-size:16px; /* 16px: never let iOS Safari zoom on focus */
|
|
20582
|
+
font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,monospace;letter-spacing:.01em;
|
|
20583
|
+
color:var(--fg);background:var(--bg);border:1.5px solid var(--line-strong);border-radius:12px;
|
|
20584
|
+
outline:none;transition:border-color .15s,box-shadow .15s}
|
|
20585
|
+
input::placeholder{font-family:var(--ui,inherit);letter-spacing:normal;color:var(--muted);opacity:.8}
|
|
20586
|
+
input:focus{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
|
|
20587
|
+
.card.bad input{border-color:var(--danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--danger) 20%,transparent)}
|
|
20588
|
+
.card.bad .field{animation:shake .34s cubic-bezier(.36,.07,.19,.97)}
|
|
20589
|
+
@keyframes shake{10%,90%{transform:translateX(-1px)}20%,80%{transform:translateX(2px)}
|
|
20590
|
+
30%,50%,70%{transform:translateX(-5px)}40%,60%{transform:translateX(5px)}}
|
|
20591
|
+
.peek{position:absolute;right:6px;width:40px;height:40px;border:0;background:none;cursor:pointer;
|
|
20592
|
+
color:var(--muted);display:flex;align-items:center;justify-content:center;border-radius:9px}
|
|
20593
|
+
.peek:hover{color:var(--fg-soft)}
|
|
20594
|
+
.peek svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.7;
|
|
20595
|
+
stroke-linecap:round;stroke-linejoin:round}
|
|
20596
|
+
.err{color:var(--danger);font-size:13px;font-weight:500;margin:11px 2px 0;
|
|
20597
|
+
display:flex;gap:6px;align-items:center;animation:fade .2s ease both}
|
|
20598
|
+
@keyframes fade{from{opacity:0}to{opacity:1}}
|
|
20599
|
+
button.go{width:100%;height:48px;margin-top:20px;border:0;border-radius:12px;cursor:pointer;
|
|
20600
|
+
background:var(--accent);color:var(--accent-fg);font-size:15px;font-weight:600;font-family:inherit;
|
|
20601
|
+
display:flex;align-items:center;justify-content:center;gap:9px;
|
|
20602
|
+
transition:filter .15s,transform .05s}
|
|
20603
|
+
button.go:hover{filter:brightness(1.06)}
|
|
20604
|
+
button.go:active{transform:translateY(1px)}
|
|
20605
|
+
button.go:disabled{opacity:.7;cursor:default}
|
|
20606
|
+
.spin{width:17px;height:17px;border:2px solid color-mix(in srgb,var(--accent-fg) 45%,transparent);
|
|
20607
|
+
border-top-color:var(--accent-fg);border-radius:50%;animation:spin .7s linear infinite}
|
|
20608
|
+
/* Full-page loader shown while a silent renewal is in flight. */
|
|
20609
|
+
.boot{position:fixed;inset:0;z-index:2;display:flex;align-items:center;justify-content:center;background:var(--bg)}
|
|
20610
|
+
.boot svg{width:40px;height:40px;fill:none;stroke:var(--accent);stroke-width:1.4;
|
|
20611
|
+
stroke-linecap:round;stroke-linejoin:round;animation:breathe 1.5s ease-in-out infinite}
|
|
20612
|
+
@keyframes breathe{0%,100%{opacity:.35;transform:scale(.94)}50%{opacity:1;transform:scale(1)}}
|
|
20613
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
20614
|
+
@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important}
|
|
20615
|
+
.brand path{stroke-dashoffset:0}}
|
|
20616
|
+
`;
|
|
20211
20617
|
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
|
-
<
|
|
20618
|
+
return `<!doctype html><html lang="zh-CN"><head>
|
|
20619
|
+
<meta charset="utf-8">
|
|
20620
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
20621
|
+
<meta name="theme-color" content="#ffffff">
|
|
20622
|
+
<title>\u9A8C\u8BC1\u8BBF\u95EE\u4EE4\u724C \xB7 metahub</title>
|
|
20623
|
+
<script>
|
|
20624
|
+
// FOUC-guard theme resolution, same key/logic as src/webui/theme.ts.
|
|
20625
|
+
try {
|
|
20626
|
+
var mt = localStorage.getItem("mh-theme");
|
|
20627
|
+
var dark = mt === "dark" || (mt !== "light" && matchMedia("(prefers-color-scheme: dark)").matches);
|
|
20628
|
+
document.documentElement.dataset.resolved = dark ? "dark" : "light";
|
|
20629
|
+
document.head.querySelector('meta[name=theme-color]').setAttribute("content", dark ? "#1a1a1c" : "#ffffff");
|
|
20630
|
+
} catch (e) {}
|
|
20631
|
+
</script>
|
|
20632
|
+
<style>${UNLOCK_CSS}</style></head><body>
|
|
20633
|
+
<div class="boot" id="boot"><svg viewBox="0 0 24 24">${CUBE_SVG}</svg></div>
|
|
20634
|
+
<form class="card" id="f" style="display:none" autocomplete="on">
|
|
20635
|
+
<div class="brand"><svg viewBox="0 0 24 24">${CUBE_SVG}</svg></div>
|
|
20636
|
+
<h1>\u8F93\u5165\u8BBF\u95EE\u4EE4\u724C</h1>
|
|
20637
|
+
<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>
|
|
20638
|
+
<div class="field">
|
|
20639
|
+
<input id="t" type="password" placeholder="\u7C98\u8D34\u8BBF\u95EE\u4EE4\u724C\u6216\u767B\u5F55\u94FE\u63A5"
|
|
20640
|
+
autocomplete="current-password" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="go">
|
|
20641
|
+
<button type="button" class="peek" id="peek" aria-label="\u663E\u793A\u4EE4\u724C"><svg viewBox="0 0 24 24">${EYE_SVG}</svg></button>
|
|
20642
|
+
</div>
|
|
20643
|
+
<p class="err" id="e" style="display:none"></p>
|
|
20644
|
+
<button type="submit" class="go" id="go">\u89E3\u9501\u5E76\u8FDB\u5165</button>
|
|
20231
20645
|
</form>
|
|
20232
20646
|
<script>
|
|
20233
20647
|
var KEY = "mh_token";
|
|
20648
|
+
var form = document.getElementById("f"), boot = document.getElementById("boot");
|
|
20649
|
+
var input = document.getElementById("t"), errEl = document.getElementById("e");
|
|
20650
|
+
var goBtn = document.getElementById("go"), peek = document.getElementById("peek");
|
|
20651
|
+
|
|
20234
20652
|
function save(t) {
|
|
20235
20653
|
localStorage.setItem(KEY, t);
|
|
20236
20654
|
document.cookie = "${COOKIE}=" + encodeURIComponent(t) + "; ${COOKIE_ATTRS}" +
|
|
20237
20655
|
(location.protocol === "https:" ? "; Secure" : "");
|
|
20238
20656
|
}
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20657
|
+
// Accept a pasted "\u2026/?token=xxx" login link, not just the bare token.
|
|
20658
|
+
function normalize(v) {
|
|
20659
|
+
v = (v || "").trim();
|
|
20660
|
+
var m = /[?&#]token=([^&\\s]+)/.exec(v);
|
|
20661
|
+
if (m) { try { return decodeURIComponent(m[1]); } catch (e) { return m[1]; } }
|
|
20662
|
+
return v;
|
|
20663
|
+
}
|
|
20664
|
+
function showForm(msg) {
|
|
20665
|
+
boot.style.display = "none";
|
|
20666
|
+
form.style.display = "block";
|
|
20667
|
+
if (msg) { errEl.textContent = msg; errEl.style.display = "flex"; }
|
|
20668
|
+
input.focus();
|
|
20669
|
+
}
|
|
20670
|
+
function fail(msg) {
|
|
20671
|
+
goBtn.disabled = false;
|
|
20672
|
+
goBtn.innerHTML = "\u89E3\u9501\u5E76\u8FDB\u5165";
|
|
20673
|
+
errEl.textContent = msg;
|
|
20674
|
+
errEl.style.display = "flex";
|
|
20675
|
+
form.classList.remove("bad");
|
|
20676
|
+
void form.offsetWidth; // reflow so the shake animation restarts
|
|
20677
|
+
form.classList.add("bad");
|
|
20678
|
+
input.focus();
|
|
20679
|
+
input.select();
|
|
20680
|
+
}
|
|
20681
|
+
peek.addEventListener("click", function () {
|
|
20682
|
+
var pw = input.type === "password";
|
|
20683
|
+
input.type = pw ? "text" : "password";
|
|
20684
|
+
peek.innerHTML = '<svg viewBox="0 0 24 24">' + (pw ? ${JSON.stringify(EYE_OFF_SVG)} : ${JSON.stringify(EYE_SVG)}) + "</svg>";
|
|
20685
|
+
peek.setAttribute("aria-label", pw ? "\u9690\u85CF\u4EE4\u724C" : "\u663E\u793A\u4EE4\u724C");
|
|
20686
|
+
input.focus();
|
|
20687
|
+
});
|
|
20688
|
+
input.addEventListener("input", function () {
|
|
20689
|
+
if (errEl.style.display !== "none") { errEl.style.display = "none"; form.classList.remove("bad"); }
|
|
20690
|
+
});
|
|
20691
|
+
|
|
20692
|
+
form.addEventListener("submit", function (ev) {
|
|
20246
20693
|
ev.preventDefault();
|
|
20247
|
-
|
|
20248
|
-
|
|
20694
|
+
var token = normalize(input.value);
|
|
20695
|
+
if (!token) { fail("\u8BF7\u8F93\u5165\u8BBF\u95EE\u4EE4\u724C\u3002"); return; }
|
|
20696
|
+
goBtn.disabled = true;
|
|
20697
|
+
goBtn.innerHTML = '<span class="spin"></span>\u9A8C\u8BC1\u4E2D\u2026';
|
|
20698
|
+
var hdr = { headers: { authorization: "Bearer " + token } };
|
|
20699
|
+
// Prefer the renewal endpoint (managed mode returns the canonical token,
|
|
20700
|
+
// also upgrading a pasted in-grace token). Fall back to a cheap
|
|
20701
|
+
// authenticated probe so we can reject a bad token *inline* instead of
|
|
20702
|
+
// storing it and reloading into another gate.
|
|
20703
|
+
fetch("${RENEW_PATH}", hdr)
|
|
20704
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
20705
|
+
.then(function (d) {
|
|
20706
|
+
if (d && d.token) { save(d.token); location.reload(); return; }
|
|
20707
|
+
return fetch("/api/version", hdr).then(function (r) {
|
|
20708
|
+
if (r.status === 401) { fail("\u4EE4\u724C\u65E0\u6548,\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002"); return; }
|
|
20709
|
+
// 200 (static-token mode) or no UI (404) \u2192 trust it and reload.
|
|
20710
|
+
save(token); location.reload();
|
|
20711
|
+
});
|
|
20712
|
+
})
|
|
20713
|
+
.catch(function () { save(token); location.reload(); }); // network hiccup \u2192 old behavior
|
|
20249
20714
|
});
|
|
20250
|
-
|
|
20251
|
-
//
|
|
20252
|
-
//
|
|
20715
|
+
|
|
20716
|
+
// Silent renewal on load: a held token may just need swapping for the current
|
|
20717
|
+
// one (rotation while this browser held the in-grace previous). Show the boot
|
|
20718
|
+
// loader meanwhile; drop to the form (with a hint) if it can't be renewed.
|
|
20253
20719
|
var stored = localStorage.getItem(KEY);
|
|
20254
20720
|
if (stored) {
|
|
20255
20721
|
fetch("${RENEW_PATH}", { headers: { authorization: "Bearer " + stored } })
|
|
20256
20722
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
20257
|
-
.then(function (d) {
|
|
20258
|
-
|
|
20723
|
+
.then(function (d) {
|
|
20724
|
+
if (d && d.token) { save(d.token); location.reload(); }
|
|
20725
|
+
else showForm("\u767B\u5F55\u5DF2\u8FC7\u671F,\u8BF7\u91CD\u65B0\u8F93\u5165\u4EE4\u724C\u3002");
|
|
20726
|
+
})
|
|
20727
|
+
.catch(function () { showForm(""); });
|
|
20259
20728
|
} else {
|
|
20260
|
-
showForm();
|
|
20729
|
+
showForm("");
|
|
20261
20730
|
}
|
|
20262
20731
|
</script>
|
|
20263
20732
|
</body></html>`;
|
|
@@ -20529,6 +20998,11 @@ var styles_default = ` :root {
|
|
|
20529
20998
|
svg { display: block; }
|
|
20530
20999
|
.ico { width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round; flex:none; }
|
|
20531
21000
|
.ico.sm { width:14px; height:14px; }
|
|
21001
|
+
/* Every window (incl. quicknote) gets the same thin translucent scrollbar:
|
|
21002
|
+
transparent track, rounded translucent thumb. In Chromium 121+ the
|
|
21003
|
+
inherited html scrollbar-width/color renders it; the ::-webkit rules
|
|
21004
|
+
cover Safari. Both force classic (layout-consuming) mode \u2014 tight layouts
|
|
21005
|
+
pair scrollbar-gutter:stable with rebalanced insets (see .qn-body). */
|
|
20532
21006
|
::-webkit-scrollbar { width:10px; height:10px; }
|
|
20533
21007
|
::-webkit-scrollbar-thumb { background:var(--line-strong); border-radius:8px; border:3px solid transparent; background-clip:padding-box; }
|
|
20534
21008
|
::-webkit-scrollbar-thumb:hover { background:var(--muted); background-clip:padding-box; border:3px solid transparent; }
|
|
@@ -20577,12 +21051,45 @@ var styles_default = ` :root {
|
|
|
20577
21051
|
.sb-search input { border:0; background:none; outline:none; color:var(--fg); width:100%; }
|
|
20578
21052
|
.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
21053
|
.sb-scroll { flex:1; overflow-y:auto; padding:4px 8px 24px; }
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
.sb-
|
|
20585
|
-
.sb-
|
|
21054
|
+
/* Notion-style list switcher: the active tab is an icon+label pill, inactive
|
|
21055
|
+
tabs are icon-only. One absolutely-positioned indicator slides & stretches
|
|
21056
|
+
under the active button; JS keeps it in sync via ResizeObserver so it
|
|
21057
|
+
chases the label's 0fr\u21921fr expansion instead of jumping to a stale width. */
|
|
21058
|
+
.sb-tabs { position:relative; display:flex; align-items:center; gap:2px; margin:2px 12px 8px; }
|
|
21059
|
+
.sb-tab { position:relative; z-index:1; display:flex; align-items:center;
|
|
21060
|
+
padding:5px 7px; border-radius:var(--radius-sm); color:var(--muted); cursor:pointer;
|
|
21061
|
+
font-size:var(--fs-ui); transition:color .18s; }
|
|
21062
|
+
.sb-tab:hover { color:var(--fg-soft); }
|
|
21063
|
+
.sb-tab.on { color:var(--fg); font-weight:600; }
|
|
21064
|
+
/* Label expansion: animating the grid track times correctly for any label
|
|
21065
|
+
width (max-width can't); the inner span clips during the ride. The
|
|
21066
|
+
icon-text gap rides the same curve so collapsing leaves no seam. */
|
|
21067
|
+
.sb-tab .tab-label { display:grid; grid-template-columns:0fr; opacity:0;
|
|
21068
|
+
transition:grid-template-columns .24s cubic-bezier(.22,1,.36,1), opacity .18s ease; }
|
|
21069
|
+
.sb-tab .tab-label > span { overflow:hidden; white-space:nowrap; min-width:0;
|
|
21070
|
+
transition:padding-left .24s cubic-bezier(.22,1,.36,1); }
|
|
21071
|
+
.sb-tab.on .tab-label { grid-template-columns:1fr; opacity:1; }
|
|
21072
|
+
.sb-tab.on .tab-label > span { padding-left:6px; }
|
|
21073
|
+
.sb-tab-ind { position:absolute; left:0; top:0; bottom:0; z-index:0; width:0;
|
|
21074
|
+
border-radius:var(--radius-sm); background:var(--hover-2);
|
|
21075
|
+
transition:transform .24s cubic-bezier(.22,1,.36,1), width .24s cubic-bezier(.22,1,.36,1); }
|
|
21076
|
+
.sb-tabs .add { margin-left:auto; padding:3px; border-radius:5px; color:var(--muted); }
|
|
21077
|
+
.sb-tabs .add:hover { background:var(--hover-2); color:var(--fg); }
|
|
21078
|
+
/* Pane swap: the entering list slides in from the side its tab lives on. */
|
|
21079
|
+
.sb-pane { margin-top:4px; }
|
|
21080
|
+
.sb-pane.in-r { animation:sb-pane-r .2s cubic-bezier(.22,1,.36,1); }
|
|
21081
|
+
.sb-pane.in-l { animation:sb-pane-l .2s cubic-bezier(.22,1,.36,1); }
|
|
21082
|
+
@keyframes sb-pane-r { from { opacity:0; transform:translateX(14px); } }
|
|
21083
|
+
@keyframes sb-pane-l { from { opacity:0; transform:translateX(-14px); } }
|
|
21084
|
+
.sb-subfold .chev { transition:transform .12s; }
|
|
21085
|
+
.sb-subfold .chev.open { transform:rotate(90deg); }
|
|
21086
|
+
/* Collapsed-databases group: a quiet tail row inside the \u6570\u636E\u8868 list for
|
|
21087
|
+
tables flagged meta.collapsed (site-facing / rarely browsed). */
|
|
21088
|
+
.sb-subfold { display:flex; align-items:center; gap:2px; width:100%; text-align:left;
|
|
21089
|
+
padding:4px 6px; margin-top:2px; border-radius:var(--radius-sm);
|
|
21090
|
+
color:var(--muted); font-size:var(--fs-micro); cursor:pointer; }
|
|
21091
|
+
.sb-subfold:hover { background:var(--hover-2); color:var(--fg-soft); }
|
|
21092
|
+
.navitem.dim { opacity:.62; }
|
|
20586
21093
|
.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
21094
|
color:var(--fg-soft); font-size:var(--fs-ui); position:relative; white-space:nowrap; transition:background .1s; }
|
|
20588
21095
|
.navitem:hover { background:var(--hover-2); }
|
|
@@ -20672,6 +21179,23 @@ var styles_default = ` :root {
|
|
|
20672
21179
|
.set-block-title { font-size:14px; font-weight:650; color:var(--fg); letter-spacing:-.005em; }
|
|
20673
21180
|
.set-block-desc { color:var(--fg-soft); font-size:var(--fs-sm); line-height:1.55; margin-bottom:14px; max-width:56ch; }
|
|
20674
21181
|
.set-block-desc:last-child { margin-bottom:0; }
|
|
21182
|
+
/* title+desc on the left, a control (e.g. a switch) pinned right \u2014 used for
|
|
21183
|
+
single boolean settings where a whole card of chrome would be overkill */
|
|
21184
|
+
.set-toggle-row { display:flex; align-items:center; justify-content:space-between; gap:24px; }
|
|
21185
|
+
.set-toggle-text { min-width:0; }
|
|
21186
|
+
.set-toggle-text .set-block-head { margin-bottom:4px; }
|
|
21187
|
+
.set-toggle-text .set-block-desc { margin-bottom:0; }
|
|
21188
|
+
/* iOS-style switch: a hidden checkbox drives a sliding track+thumb */
|
|
21189
|
+
.set-switch { position:relative; flex:none; display:inline-flex; align-items:center; cursor:pointer; }
|
|
21190
|
+
.set-switch input { position:absolute; width:1px; height:1px; opacity:0; margin:0; }
|
|
21191
|
+
.set-switch-track { display:inline-flex; align-items:center; width:40px; height:23px; padding:0 2px;
|
|
21192
|
+
border-radius:999px; background:var(--line-strong); transition:background .2s ease; }
|
|
21193
|
+
.set-switch-thumb { width:19px; height:19px; border-radius:50%; background:#fff;
|
|
21194
|
+
box-shadow:0 1px 3px rgba(20,20,40,.28); transition:transform .2s cubic-bezier(.4,0,.2,1); }
|
|
21195
|
+
.set-switch input:checked + .set-switch-track { background:var(--accent); }
|
|
21196
|
+
.set-switch input:checked + .set-switch-track .set-switch-thumb { transform:translateX(17px); }
|
|
21197
|
+
.set-switch:hover .set-switch-track { filter:brightness(.97); }
|
|
21198
|
+
.set-switch input:focus-visible + .set-switch-track { outline:2px solid var(--accent); outline-offset:2px; }
|
|
20675
21199
|
/* two-fact meta line under a block title (who publishes \xB7 where creds live) \u2014
|
|
20676
21200
|
answers the questions hiding under "is this bucket for server or browser?" */
|
|
20677
21201
|
.set-meta { display:flex; flex-wrap:wrap; gap:8px 16px; margin:0 0 12px; }
|
|
@@ -20871,10 +21395,19 @@ var styles_default = ` :root {
|
|
|
20871
21395
|
.sidebar.collapsed { overflow:hidden; }
|
|
20872
21396
|
.sidebar.collapsed .sb-resizer { pointer-events:none; }
|
|
20873
21397
|
.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
|
-
|
|
21398
|
+
/* Long titles ellipsize on one line \u2014 never wrap and grow the topbar. */
|
|
21399
|
+
.crumb > span:not(.emoji) { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
21400
|
+
.crumb .emoji { font-size:15px; flex:none; } .crumb .sub { color:var(--muted); font-weight:400; }
|
|
20875
21401
|
.iconbtn { width:30px; height:30px; display:grid; place-items:center; border-radius:var(--radius-sm); color:var(--fg-soft); transition:background .12s; }
|
|
20876
21402
|
.iconbtn:hover { background:var(--hover-2); color:var(--fg); }
|
|
20877
|
-
|
|
21403
|
+
/* Reserve the classic-scrollbar gutter up front: view width never changes
|
|
21404
|
+
when content grows past one screen \u2014 no jump / rewrap flicker. stable
|
|
21405
|
+
ONLY (never both-edges): a left-edge gutter desyncs Chromium's composited
|
|
21406
|
+
scroll cull rect by the gutter width, leaving a permanent unpainted
|
|
21407
|
+
stripe over the rightmost 15px of the scrollport (content reaching into
|
|
21408
|
+
it gets visibly sliced \u2014 first seen slicing the quicknote file card).
|
|
21409
|
+
Inert on mobile, where .content becomes overflow:visible. */
|
|
21410
|
+
.content { flex:1; overflow:auto; scrollbar-gutter:stable; }
|
|
20878
21411
|
.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
21412
|
.btn-ghost { color:var(--fg-soft); } .btn-ghost:hover { background:var(--hover-2); color:var(--fg); }
|
|
20880
21413
|
.btn-secondary { background:var(--bg); border:1px solid var(--line-strong); color:var(--fg); box-shadow:var(--shadow-sm); }
|
|
@@ -20939,9 +21472,9 @@ var styles_default = ` :root {
|
|
|
20939
21472
|
.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
21473
|
.doc-conflict-msg { color:var(--danger); font-weight:600; flex:1; min-width:180px; }
|
|
20941
21474
|
.doc.source-mode { min-height:100%; }
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
21475
|
+
/* wide mode (\u22EF menu): lift the column cap; the 60px side padding stays as the
|
|
21476
|
+
reading gutter and keeps the fixed .doc-toc ticks off the text. */
|
|
21477
|
+
.doc.wide-mode { max-width:none; }
|
|
20945
21478
|
/* floating table of contents: a column of indented tick marks in the right
|
|
20946
21479
|
gutter that expands to a labelled list on hover (Notion / Vercel-docs style).
|
|
20947
21480
|
Fixed to the viewport so it never disturbs the centred .doc column. */
|
|
@@ -20980,49 +21513,33 @@ var styles_default = ` :root {
|
|
|
20980
21513
|
/* not enough room for the gutter \u2192 hide entirely (also covers mobile) */
|
|
20981
21514
|
@media (max-width:1100px) { .doc-toc { display:none; } }
|
|
20982
21515
|
|
|
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; }
|
|
21516
|
+
/* floating word count: a small pill in the bottom-right gutter (fixed to the
|
|
21517
|
+
viewport, aligned with .doc-toc's right edge). Collapsed it hugs the \u5B57\u6570;
|
|
21518
|
+
hover expands the whole pill (square-off + surface bg) and reveals the char
|
|
21519
|
+
count + reading estimate. Centre-aligned throughout, and the collapsed detail
|
|
21520
|
+
is width:0 so its wider text can't stretch the pill. Purely client-side. */
|
|
21521
|
+
.doc-wc { position:fixed; right:18px; bottom:calc(16px + env(safe-area-inset-bottom)); z-index:40;
|
|
21522
|
+
display:flex; flex-direction:column; align-items:center; justify-content:center;
|
|
21523
|
+
padding:4px 12px; border:1px solid var(--line); border-radius:999px; background:var(--bg);
|
|
21524
|
+
color:var(--muted); font-size:var(--fs-sm); line-height:1.2; user-select:none;
|
|
21525
|
+
box-shadow:var(--shadow-sm); opacity:.66;
|
|
21526
|
+
transition:opacity .18s ease, border-radius .2s ease, background .18s ease, box-shadow .2s ease, color .18s ease; }
|
|
21527
|
+
.doc-wc:hover { opacity:1; border-radius:var(--radius); background:var(--surface); border-color:var(--line-strong);
|
|
21528
|
+
box-shadow:var(--shadow-md); color:var(--fg-soft); }
|
|
21529
|
+
.doc-wc-num { font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
21530
|
+
.doc-wc.sel .doc-wc-num { color:var(--accent); font-weight:550; }
|
|
21531
|
+
/* detail rows: collapsed to zero size until hover. width:0 keeps them from
|
|
21532
|
+
widening the pill; no container gap (a phantom gap would push the collapsed
|
|
21533
|
+
count off-centre) \u2014 the spacing comes from margin-top only when expanded. */
|
|
21534
|
+
.doc-wc-detail { display:flex; flex-direction:column; align-items:center; gap:2px;
|
|
21535
|
+
width:0; max-height:0; margin-top:0; overflow:hidden; opacity:0; font-size:12px; color:var(--muted);
|
|
21536
|
+
transition:max-height .2s ease, opacity .18s ease, margin-top .2s ease; }
|
|
21537
|
+
.doc-wc-detail span { font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
21538
|
+
.doc-wc:hover .doc-wc-detail { width:auto; max-height:40px; margin-top:4px; opacity:1; }
|
|
21539
|
+
/* quick-note mini window has no room for it */
|
|
21540
|
+
.qn-body .doc-wc { display:none; }
|
|
21023
21541
|
|
|
21024
21542
|
/* code block: transparent textarea over a highlight.js mirror */
|
|
21025
|
-
.b-code .codeblock { flex:1; min-width:0; }
|
|
21026
21543
|
.codeblock { position:relative; --code-fs:12.5px; --code-lh:1.55; --code-pad:8px; margin:2px 0;
|
|
21027
21544
|
background:var(--code-bg); border:1px solid var(--line); border-radius:8px; overflow:hidden; box-shadow:var(--shadow-sm); }
|
|
21028
21545
|
/* lang + copy float bottom-right, revealed on hover */
|
|
@@ -21031,27 +21548,57 @@ var styles_default = ` :root {
|
|
|
21031
21548
|
background:color-mix(in srgb,var(--code-bg) 82%,transparent); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
|
|
21032
21549
|
opacity:0; transform:translateY(3px); pointer-events:none; transition:opacity .12s,transform .12s; }
|
|
21033
21550
|
.codeblock:hover .code-tools, .code-tools:focus-within { opacity:1; transform:none; pointer-events:auto; }
|
|
21551
|
+
/* hidden while editing (textarea focused) or whole-block selected \u2014 the pill would cover the
|
|
21552
|
+
bottom-right of the editing area; keep it if focus is on the pill itself (lang dropdown open) */
|
|
21553
|
+
.cm-void-code:focus-within .code-tools:not(:focus-within),
|
|
21554
|
+
.codeblock.ci-selected .code-tools:not(:focus-within) { opacity:0; transform:translateY(3px); pointer-events:none; }
|
|
21034
21555
|
.code-lang { position:relative; display:inline-flex; align-items:center; }
|
|
21035
21556
|
.code-lang select { appearance:none; -webkit-appearance:none; border:0; outline:none; background:none; cursor:pointer;
|
|
21036
21557
|
font-family:var(--mono); font-size:10.5px; font-weight:500; color:var(--fg-soft); padding:3px 17px 3px 7px; border-radius:5px; }
|
|
21037
21558
|
.code-lang select:hover { background:var(--hover-2); }
|
|
21038
21559
|
.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-
|
|
21560
|
+
.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);
|
|
21561
|
+
padding:3px 7px; border-radius:5px; transition:background .12s,color .12s; white-space:nowrap; }
|
|
21562
|
+
.code-copy:hover, .code-fmt:hover, .code-wrap-btn:hover { background:var(--hover-2); color:var(--fg-soft); }
|
|
21563
|
+
.code-copy.ok, .code-fmt.ok, .code-wrap-btn.on { color:var(--accent); }
|
|
21564
|
+
.code-fmt.err { color:var(--danger); }
|
|
21565
|
+
/* Default (no wrap): lines run long, the textarea scrolls horizontally and
|
|
21566
|
+
mirrors its scrollLeft onto the highlight layer; the gutter is a separate
|
|
21567
|
+
flex column of "1\\n2\\n3" (every logical line is exactly one row high). */
|
|
21568
|
+
.code-body { display:flex; align-items:stretch; font-family:var(--mono); font-size:var(--code-fs); line-height:var(--code-lh);
|
|
21569
|
+
--code-ln-ch:1ch; --code-gutter-w:calc(var(--code-ln-ch) + 20px); }
|
|
21044
21570
|
.code-gutter { flex:none; text-align:right; white-space:pre; user-select:none; color:var(--muted); opacity:.55;
|
|
21045
21571
|
padding:var(--code-pad) 8px var(--code-pad) 12px; border-right:1px solid var(--line); font-variant-numeric:tabular-nums; }
|
|
21046
21572
|
.code-scroll { position:relative; flex:1; min-width:0; }
|
|
21047
21573
|
.code-hl, .code-input { margin:0; font-family:var(--mono); font-size:var(--code-fs); line-height:var(--code-lh);
|
|
21048
21574
|
padding:var(--code-pad) 16px; tab-size:2; -moz-tab-size:2; white-space:pre; letter-spacing:0; }
|
|
21049
21575
|
.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; }
|
|
21576
|
+
.code-hl code { display:block; font:inherit; background:none; padding:0; counter-reset:code-ln; }
|
|
21577
|
+
.code-hl-line { counter-increment:code-ln;
|
|
21578
|
+
min-height:calc(var(--code-fs) * var(--code-lh)); /* empty logical line still occupies its row */ }
|
|
21051
21579
|
.code-input { display:block; position:relative; z-index:1; width:100%; border:0; outline:none; resize:none;
|
|
21052
21580
|
overflow-x:auto; overflow-y:hidden; background:transparent; color:transparent; caret-color:var(--fg);
|
|
21053
21581
|
min-height:calc(var(--code-fs) * var(--code-lh) + var(--code-pad) * 2); }
|
|
21054
21582
|
.code-input::placeholder { color:var(--muted); }
|
|
21583
|
+
/* Wrap mode (.wrap, global toggle in the tools pill, persisted): textarea and
|
|
21584
|
+
mirror share pre-wrap + break-word (the UA textarea wrap rules), so their
|
|
21585
|
+
wrap points match and the caret stays on the glyphs. The flex gutter would
|
|
21586
|
+
misalign (a logical line can span several visual rows), so it hides and
|
|
21587
|
+
line numbers become CSS counters on the mirror's per-logical-line blocks \u2014
|
|
21588
|
+
a wrapped line grows its block and the next number moves with it, no JS
|
|
21589
|
+
height sync. --code-ln-ch (digit count) is set by paint(). */
|
|
21590
|
+
.code-body.wrap { display:block; position:relative; }
|
|
21591
|
+
.code-body.wrap::before { content:""; position:absolute; left:var(--code-gutter-w); top:0; bottom:0;
|
|
21592
|
+
width:1px; background:var(--line); }
|
|
21593
|
+
.code-body.wrap .code-gutter { display:none; }
|
|
21594
|
+
.code-body.wrap .code-hl, .code-body.wrap .code-input {
|
|
21595
|
+
padding-left:calc(var(--code-gutter-w) + 17px);
|
|
21596
|
+
white-space:pre-wrap; overflow-wrap:break-word; }
|
|
21597
|
+
.code-body.wrap .code-input { overflow:hidden; }
|
|
21598
|
+
.code-body.wrap .code-hl-line { position:relative; }
|
|
21599
|
+
.code-body.wrap .code-hl-line::before { content:counter(code-ln); position:absolute; top:0;
|
|
21600
|
+
left:calc(-17px - var(--code-gutter-w)); width:calc(var(--code-gutter-w) - 8px);
|
|
21601
|
+
text-align:right; color:var(--muted); opacity:.55; font-variant-numeric:tabular-nums; }
|
|
21055
21602
|
|
|
21056
21603
|
/* highlight.js token theme (cohesive with the app palette) */
|
|
21057
21604
|
.hljs-comment, .hljs-quote { color:var(--hl-cmt); font-style:italic; }
|
|
@@ -21065,24 +21612,50 @@ var styles_default = ` :root {
|
|
|
21065
21612
|
.hljs-params, .hljs-subst { color:var(--fg); }
|
|
21066
21613
|
.hljs-emphasis { font-style:italic; }
|
|
21067
21614
|
.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
21615
|
|
|
21081
|
-
/*
|
|
21082
|
-
|
|
21083
|
-
|
|
21084
|
-
|
|
21616
|
+
/* CodeIsland (CM6 void widget): the tools pill (lang + copy) is the only
|
|
21617
|
+
focus-state chrome \u2014 an absolute overlay, so revealing it never changes the
|
|
21618
|
+
block's height (no layout shift on click). .ci-selected marks the island
|
|
21619
|
+
when the CM selection spans the whole void (Escape from inside / Backspace
|
|
21620
|
+
from the line below). */
|
|
21621
|
+
.cm-void-code .codeblock.ci-selected { box-shadow:0 0 0 2px var(--accent); }
|
|
21622
|
+
|
|
21623
|
+
/* Whole-void selected (the CM selection spans the block \u2014 click-to-select on
|
|
21624
|
+
media, Backspace-from-below, select-all): 2px accent ring, the old editor's
|
|
21625
|
+
\`.block.selected .void-block\` treatment. Code draws its own via .ci-selected. */
|
|
21626
|
+
/* Intrinsic-width firewall (same problem the doc table solved with
|
|
21627
|
+
contain:inline-size on .doc-table-wrap): a void widget's max-content \u2014
|
|
21628
|
+
e.g. a file card whose nowrap filename caps at 480px \u2014 must not propagate
|
|
21629
|
+
into .cm-content and push the whole editor wider than a narrow window
|
|
21630
|
+
(quick notes), shoving the card's right edge offscreen. width:0 zeroes the
|
|
21631
|
+
host's intrinsic contribution; min-width:100% restores the line width at
|
|
21632
|
+
layout time; the card's own min-width:0 + ellipsis then get to run.
|
|
21633
|
+
NOT contain:inline-size: Chromium 130's ink-overflow culling clips a
|
|
21634
|
+
size-contained host's paint at a stale boundary on 2x displays \u2014 the
|
|
21635
|
+
card's right border and selection ring were visibly sliced off. */
|
|
21636
|
+
.cm-void { box-sizing:border-box; width:0; min-width:100%; }
|
|
21637
|
+
.cm-void.selected .void-block { box-shadow:0 0 0 2px var(--accent); }
|
|
21638
|
+
.cm-void.selected table.doc-table { box-shadow:0 0 0 2px var(--accent), var(--shadow-sm); }
|
|
21639
|
+
|
|
21640
|
+
/* Editor text selection: one accent tint for BOTH the native ::selection and
|
|
21641
|
+
.cm-mk-sel, the class block-deco puts on marker widgets (bullet / ordered
|
|
21642
|
+
number / checkbox) whose marker range the selection covers. Native selection
|
|
21643
|
+
cannot paint replaced widgets, which left holes in the marker column during
|
|
21644
|
+
drag-select / select-all; unifying the two colors makes the line read as one
|
|
21645
|
+
continuous highlight (and tracks the theme in dark mode). */
|
|
21646
|
+
.cm-content ::selection { background: color-mix(in srgb, var(--accent) 24%, transparent); }
|
|
21647
|
+
.cm-content .cm-mk-sel { background: color-mix(in srgb, var(--accent) 24%, transparent); }
|
|
21648
|
+
/* bullet/number boxes are 24px + 2px margin-right; the margin can't take a
|
|
21649
|
+
background, so a same-color shadow fills the seam up to the content column */
|
|
21650
|
+
.cm-content .cm-bullet.cm-mk-sel, .cm-content .cm-ol-num.cm-mk-sel {
|
|
21651
|
+
box-shadow: 2px 0 0 color-mix(in srgb, var(--accent) 24%, transparent);
|
|
21652
|
+
}
|
|
21653
|
+
/* the glyph/number text inside a painted marker also sits in the DOM selection;
|
|
21654
|
+
without this it gets the ::selection tint ON TOP of the span background and
|
|
21655
|
+
reads darker than the rest of the line */
|
|
21656
|
+
.cm-content .cm-mk-sel::selection, .cm-content .cm-mk-sel ::selection { background: transparent; }
|
|
21085
21657
|
|
|
21658
|
+
/* ---- block-level void embeds: image / video / audio / file / html ---- */
|
|
21086
21659
|
.void-image { position:relative; display:inline-block; max-width:100%; border-radius:8px; line-height:0; }
|
|
21087
21660
|
.void-image img { display:block; width:100%; max-width:100%; height:auto; border-radius:8px; user-select:none; }
|
|
21088
21661
|
.void-image .void-expand { position:absolute; top:8px; right:8px; width:28px; height:28px; display:grid; place-items:center;
|
|
@@ -21120,7 +21693,6 @@ var styles_default = ` :root {
|
|
|
21120
21693
|
color:var(--fg-soft); font-size:var(--fs-micro); transition:background .12s; }
|
|
21121
21694
|
.void-html .html-toggle:hover { background:var(--hover-2); color:var(--fg); }
|
|
21122
21695
|
.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
21696
|
.void-html .html-frame { display:block; width:100%; border:0; background:var(--bg); }
|
|
21125
21697
|
.void-html .html-empty { padding:24px; text-align:center; color:var(--muted); cursor:text; font-size:var(--fs-ui); }
|
|
21126
21698
|
|
|
@@ -21131,30 +21703,47 @@ var styles_default = ` :root {
|
|
|
21131
21703
|
.lightbox-toolbar { flex:none; display:flex; align-items:center; gap:6px; padding:10px 14px; color:#fff; }
|
|
21132
21704
|
.lightbox-toolbar .lightbox-name { font-size:var(--fs-ui); opacity:.85; max-width:40vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
21133
21705
|
.lightbox-toolbar .lightbox-spacer { flex:1; }
|
|
21134
|
-
.lightbox-toolbar button { width:34px; height:34px; display:grid; place-items:center; border-radius:8px; color:#fff;
|
|
21706
|
+
.lightbox-toolbar > button { width:34px; height:34px; display:grid; place-items:center; border-radius:8px; color:#fff;
|
|
21135
21707
|
background:rgba(255,255,255,.08); transition:background .12s; }
|
|
21136
|
-
.lightbox-toolbar button:hover { background:rgba(255,255,255,.2); }
|
|
21708
|
+
.lightbox-toolbar > button:hover { background:rgba(255,255,255,.2); }
|
|
21137
21709
|
.lightbox-toolbar .lightbox-pct { width:auto; min-width:52px; font-size:var(--fs-micro); font-variant-numeric:tabular-nums; }
|
|
21710
|
+
.lightbox-toolbar .ico { width:16px; height:16px; stroke-width:2; }
|
|
21138
21711
|
.lightbox-stage { flex:1; min-height:0; overflow:hidden; display:grid; place-items:center; }
|
|
21139
21712
|
.lightbox-stage img { max-width:94vw; max-height:100%; will-change:transform; -webkit-user-drag:none; user-select:none; }
|
|
21140
21713
|
|
|
21141
21714
|
/* ---- image annotator ---- */
|
|
21142
21715
|
.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-
|
|
21716
|
+
/* Single-row chrome: filename \xB7 segmented tools \xB7 colors \xB7 undo/redo \xB7
|
|
21717
|
+
cancel/save \xB7 close. Wraps only as a narrow-window fallback. */
|
|
21718
|
+
.annot-bar { flex-wrap:wrap; row-gap:6px; }
|
|
21719
|
+
.annot-seg { display:flex; align-items:center; gap:2px; padding:2px; border-radius:9px; background:rgba(255,255,255,.1); }
|
|
21720
|
+
.annot-seg-btn { width:30px; height:26px; display:grid; place-items:center; border-radius:7px;
|
|
21721
|
+
color:rgba(255,255,255,.72); transition:background .12s, color .12s; }
|
|
21722
|
+
.annot-seg-btn:hover { color:#fff; background:rgba(255,255,255,.12); }
|
|
21723
|
+
.annot-seg-btn.on { background:#fff; color:#1c1c26; box-shadow:0 1px 2px rgba(0,0,0,.3); }
|
|
21724
|
+
.annot-div { flex:none; width:1px; height:18px; margin:0 5px; background:rgba(255,255,255,.18); }
|
|
21725
|
+
.annot-colors { display:flex; align-items:center; gap:8px; padding:0 2px; }
|
|
21726
|
+
.annot-swatch { width:15px; height:15px; border-radius:50%; box-shadow:inset 0 0 0 1px rgba(255,255,255,.3);
|
|
21727
|
+
transition:transform .12s, box-shadow .12s; }
|
|
21728
|
+
.annot-swatch:hover { transform:scale(1.18); }
|
|
21729
|
+
.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); }
|
|
21730
|
+
.annot-bar .annot-icon-btn { width:30px; height:30px; display:grid; place-items:center; border-radius:7px;
|
|
21731
|
+
color:rgba(255,255,255,.85); background:none; transition:background .12s; }
|
|
21732
|
+
.annot-bar .annot-icon-btn:hover { background:rgba(255,255,255,.14); }
|
|
21733
|
+
.annot-bar .annot-icon-btn:disabled { opacity:.35; cursor:default; background:none; }
|
|
21734
|
+
.annot-actions { display:flex; align-items:center; gap:8px; margin:0 2px 0 4px; }
|
|
21152
21735
|
.annot-stage { flex:1; min-height:0; overflow:auto; display:grid; place-items:center; padding:8px 14px 18px; }
|
|
21153
21736
|
.annot-img-wrap { position:relative; line-height:0; }
|
|
21154
21737
|
.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%; }
|
|
21738
|
+
.annot-canvas { position:absolute; inset:0; width:100%; height:100%; touch-action:none; }
|
|
21156
21739
|
.annot-canvas.tool-text { cursor:text; }
|
|
21157
21740
|
.annot-canvas.tool-rect, .annot-canvas.tool-arrow { cursor:crosshair; }
|
|
21741
|
+
/* Inline text overlay: WYSIWYG twin of the flattened canvas text (same font,
|
|
21742
|
+
line-height and halo), plus an accent ring so the edit region reads. */
|
|
21743
|
+
.annot-text-input { position:absolute; z-index:2; min-width:60px; padding:2px 4px; margin:0; border:0; outline:none;
|
|
21744
|
+
resize:none; overflow:hidden; white-space:pre; background:transparent; border-radius:4px; caret-color:currentColor;
|
|
21745
|
+
font:600 20px/1.3 system-ui, -apple-system, sans-serif; text-shadow:0 1px 2px rgba(0,0,0,.55);
|
|
21746
|
+
box-shadow:0 0 0 1.5px var(--accent), 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent); }
|
|
21158
21747
|
|
|
21159
21748
|
/* ---- transient "uploading" placeholder + unavailable fallback ---- */
|
|
21160
21749
|
.ico.spin { animation:mh-spin .8s linear infinite; }
|
|
@@ -21163,7 +21752,10 @@ var styles_default = ` :root {
|
|
|
21163
21752
|
display:flex; align-items:center; gap:12px; margin:2px 0; max-width:480px; padding:12px 14px;
|
|
21164
21753
|
border:1px solid var(--line-strong); border-radius:var(--radius); background:var(--surface); color:var(--fg-soft); }
|
|
21165
21754
|
.void-uploading { border-style:dashed; }
|
|
21166
|
-
|
|
21755
|
+
/* min-width:0: .void-uploading's vu-name is a direct flex item \u2014 without it a
|
|
21756
|
+
long nowrap filename sets the item's min-content floor and the placeholder
|
|
21757
|
+
card overflows a narrow window instead of ellipsizing. */
|
|
21758
|
+
.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
21759
|
.void-uploading .vu-sub, .void-unavailable .vu-sub { font-size:var(--fs-micro); color:var(--muted); }
|
|
21168
21760
|
.void-unavailable { border-color:var(--danger-soft); color:var(--danger); }
|
|
21169
21761
|
.void-unavailable .ico { color:var(--danger); flex:none; }
|
|
@@ -21209,24 +21801,53 @@ var styles_default = ` :root {
|
|
|
21209
21801
|
/* Chrome rides the theme-tinted glass \u2192 theme-aware (var(--fg)), readable on a
|
|
21210
21802
|
light or dark tint alike. */
|
|
21211
21803
|
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-
|
|
21804
|
+
body.preview-window .lightbox-toolbar > button { color:var(--fg); background:color-mix(in srgb, var(--fg) 10%, transparent); }
|
|
21805
|
+
body.preview-window .lightbox-toolbar > button:hover { background:color-mix(in srgb, var(--fg) 20%, transparent); }
|
|
21806
|
+
body.preview-window .annot-seg { background:color-mix(in srgb, var(--fg) 8%, transparent); }
|
|
21807
|
+
body.preview-window .annot-seg-btn { color:color-mix(in srgb, var(--fg) 65%, transparent); }
|
|
21808
|
+
body.preview-window .annot-seg-btn:hover { color:var(--fg); background:color-mix(in srgb, var(--fg) 10%, transparent); }
|
|
21809
|
+
body.preview-window .annot-seg-btn.on { background:var(--bg); color:var(--fg); box-shadow:var(--shadow-sm); }
|
|
21810
|
+
body.preview-window .annot-div { background:color-mix(in srgb, var(--fg) 15%, transparent); }
|
|
21811
|
+
body.preview-window .annot-swatch { box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--fg) 25%, transparent); }
|
|
21812
|
+
body.preview-window .annot-swatch.on { box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--fg) 25%, transparent),
|
|
21813
|
+
0 0 0 2px var(--bg), 0 0 0 3.5px color-mix(in srgb, var(--fg) 78%, transparent); }
|
|
21814
|
+
body.preview-window .annot-bar .annot-icon-btn { color:var(--fg); background:none; }
|
|
21815
|
+
body.preview-window .annot-bar .annot-icon-btn:hover { background:color-mix(in srgb, var(--fg) 12%, transparent); }
|
|
21816
|
+
body.preview-window .annot-bar .annot-icon-btn:disabled { background:none; }
|
|
21218
21817
|
/* In the frameless window the toolbar doubles as the OS drag region; its
|
|
21219
21818
|
buttons opt back out so they stay clickable. */
|
|
21220
21819
|
.lightbox-toolbar.app-drag { -webkit-app-region:drag; }
|
|
21221
21820
|
.lightbox-toolbar.app-drag button { -webkit-app-region:no-drag; }
|
|
21222
21821
|
|
|
21223
|
-
/* document table (GFM pipe table block)
|
|
21224
|
-
|
|
21822
|
+
/* document table (GFM pipe table block). contain:inline-size zeroes the
|
|
21823
|
+
widget's intrinsic-width contribution \u2014 otherwise the inner
|
|
21824
|
+
width:max-content propagates through overflow-x:auto into .cm-content's
|
|
21825
|
+
min-width:auto floor (intrinsic sizing ignores overflow) and pushes the
|
|
21826
|
+
whole page wider than the viewport on narrow screens. With the intrinsic
|
|
21827
|
+
loop broken, .doc-table-wrap resolves to the doc column width and the
|
|
21828
|
+
scroll box below finally gets to scroll. container-type additionally
|
|
21829
|
+
unlocks cqw for the sticky add-row bar (old engines fall back to contain);
|
|
21830
|
+
position:relative anchors the gutter row handle explicitly rather than
|
|
21831
|
+
leaning on container-type's implied containing block. */
|
|
21832
|
+
.doc-table-wrap { position:relative; contain:inline-size; container-type:inline-size; }
|
|
21225
21833
|
.doc-table-scroll { overflow-x:auto; padding:2px 0; }
|
|
21226
|
-
.doc-table-inner { width:max-content; }
|
|
21834
|
+
.doc-table-inner { position:relative; width:max-content; } /* anchors the drag outline / drop lines so they track horizontal scroll */
|
|
21227
21835
|
.doc-table-row { display:flex; align-items:stretch; }
|
|
21228
21836
|
table.doc-table { border-collapse:separate; border-spacing:0; table-layout:fixed; width:max-content;
|
|
21229
21837
|
border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden; box-shadow:var(--shadow-sm); }
|
|
21838
|
+
/* Default (no manual widths yet): browser auto table layout sizes columns by
|
|
21839
|
+
content, capped to the wrap (cqw works despite the max-content .doc-table-inner;
|
|
21840
|
+
26px keeps the add-col button inside) so it wraps instead of scrolling. The
|
|
21841
|
+
first resizer drag freezes measured widths and drops this class (table-block.tsx). */
|
|
21842
|
+
table.doc-table.autofit { table-layout:auto; width:auto; max-width:calc(100cqw - 26px); }
|
|
21843
|
+
table.doc-table.autofit .doc-td { min-width:48px; } /* empty columns don't collapse */
|
|
21844
|
+
/* Code tokens are atomic: raising the column's min-content makes the auto
|
|
21845
|
+
algorithm shed width into wrappable prose columns instead of breaking
|
|
21846
|
+
\`file-names.md\` at hyphens; an over-long token falls back to the scroll
|
|
21847
|
+
box. Autofit only \u2014 in manual fixed layout nowrap can't grow the column,
|
|
21848
|
+
it would overflow the cell. Phones relax this (see the 560px block):
|
|
21849
|
+
there the atom IS the cap, and it crushes prose columns to their floor. */
|
|
21850
|
+
table.doc-table.autofit .doc-td code { white-space:nowrap; }
|
|
21230
21851
|
table.doc-table td { position:relative; vertical-align:top; padding:0;
|
|
21231
21852
|
border-right:1px solid var(--line); border-bottom:1px solid var(--line); }
|
|
21232
21853
|
table.doc-table tr td:last-child { border-right:0; }
|
|
@@ -21238,24 +21859,59 @@ var styles_default = ` :root {
|
|
|
21238
21859
|
.doc-td b, .doc-td strong { font-weight:700; }
|
|
21239
21860
|
.doc-td code { font-family:var(--mono); background:var(--surface-2); padding:1px 5px; border-radius:4px; font-size:.9em; }
|
|
21240
21861
|
.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
21862
|
.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
|
-
|
|
21863
|
+
/* Row/column pill handles (Notion-style): one per axis, tracked onto the
|
|
21864
|
+
active cell (hovered on desktop, focused on touch) by JS. They live on
|
|
21865
|
+
.doc-table-wrap OUTSIDE .doc-table-scroll so overflow-x can't clip them,
|
|
21866
|
+
straddling the table's left/top border \u2014 never on cell text. Resting =
|
|
21867
|
+
quiet grey pill; pressed/dragging (.grab) = accent pill with light dots
|
|
21868
|
+
plus the source outline + drop line below. touch-action:none so a finger
|
|
21869
|
+
drag on the handle reorders instead of scrolling. Data rows only get a
|
|
21870
|
+
row pill (the block gutter's +/grip pins at the header row's y). */
|
|
21871
|
+
.doc-row-handle, .doc-col-handle { position:absolute; display:grid; place-items:center; z-index:7;
|
|
21872
|
+
color:var(--muted); background:var(--bg); border:1px solid var(--line); box-shadow:var(--shadow-sm);
|
|
21873
|
+
cursor:grab; touch-action:none; animation:gripFade .12s ease both;
|
|
21874
|
+
transition:background .12s,color .12s,border-color .12s; }
|
|
21875
|
+
.doc-row-handle { left:-8px; width:15px; height:28px; border-radius:8px; }
|
|
21876
|
+
.doc-col-handle { top:-6px; width:28px; height:15px; border-radius:8px; transform:translateX(-50%); }
|
|
21877
|
+
.doc-row-handle svg, .doc-col-handle svg { width:13px; height:13px; }
|
|
21878
|
+
.doc-row-handle:hover, .doc-col-handle:hover { background:var(--surface); color:var(--fg); }
|
|
21879
|
+
.doc-row-handle.grab, .doc-col-handle.grab { background:var(--accent); color:var(--accent-fg,#fff);
|
|
21880
|
+
border-color:var(--accent); cursor:grabbing; }
|
|
21881
|
+
.doc-row-handle::after { content:""; position:absolute; inset:-4px; }
|
|
21882
|
+
.doc-col-handle::after { content:""; position:absolute; inset:-4px; }
|
|
21883
|
+
/* drag-reorder overlays (inside .doc-table-inner, so they scroll with the
|
|
21884
|
+
table): accent outline on the source row/column, 2px line at the drop
|
|
21885
|
+
boundary (grid .rowdrop's cousin) */
|
|
21886
|
+
.doc-drag-outline { position:absolute; border:2px solid var(--accent); border-radius:6px;
|
|
21887
|
+
pointer-events:none; z-index:6; box-sizing:border-box; }
|
|
21888
|
+
.doc-rowdrop { position:absolute; height:2px; margin-top:-1px; background:var(--accent); border-radius:1px; pointer-events:none; z-index:6; }
|
|
21889
|
+
.doc-coldrop { position:absolute; width:2px; margin-left:-1px; background:var(--accent); border-radius:1px; pointer-events:none; z-index:6; }
|
|
21890
|
+
/* touch selection action bar: the OSK has no Delete/\u2318C, so the cell
|
|
21891
|
+
rectangle gets on-screen verbs; sits on the wrap layer above the selection */
|
|
21892
|
+
.doc-sel-bar { position:absolute; z-index:8; display:flex; gap:2px; padding:3px;
|
|
21893
|
+
background:var(--accent); border-radius:9px; box-shadow:var(--shadow-sm); animation:gripFade .12s ease both; }
|
|
21894
|
+
.doc-sel-bar button { color:var(--accent-fg,#fff); font-size:13px; line-height:1; padding:5px 9px; border-radius:6px; }
|
|
21895
|
+
.doc-sel-bar button:active { background:color-mix(in srgb, #000 18%, transparent); }
|
|
21253
21896
|
/* add-column (right edge) and add-row (bottom edge) buttons */
|
|
21254
21897
|
.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
|
-
|
|
21898
|
+
border:1px dashed var(--line-strong); border-radius:var(--radius-sm); background:var(--surface); transition:background .12s,color .12s,border-color .12s,opacity .12s; }
|
|
21899
|
+
/* sticky + 100cqw (the wrap is the container): on a wide table the bar pins
|
|
21900
|
+
to the scrollport at any horizontal offset; narrow tables never hit the
|
|
21901
|
+
max-width and keep their table-wide bar. Engines without cqw just ignore
|
|
21902
|
+
the cap and fall back to scrolling with the table. */
|
|
21903
|
+
.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);
|
|
21904
|
+
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
21905
|
.doc-table-addcol:hover, .doc-table-addrow:hover { background:var(--accent-soft); color:var(--accent); border-color:var(--accent); }
|
|
21906
|
+
/* Notion-style: the add bars only materialize while the table is active \u2014
|
|
21907
|
+
hover or keyboard focus on desktop, an editing (focused) cell on touch.
|
|
21908
|
+
Opacity-only so nothing shifts; pointer-events gates off the invisible
|
|
21909
|
+
bars (a touch tap must not hit a bar it can't see). */
|
|
21910
|
+
.doc-table-addrow, .doc-table-addcol { opacity:0; pointer-events:none; }
|
|
21911
|
+
.doc-table-wrap:focus-within :is(.doc-table-addrow, .doc-table-addcol) { opacity:1; pointer-events:auto; }
|
|
21912
|
+
@media (hover:hover) and (pointer:fine) {
|
|
21913
|
+
.doc-table-wrap:hover :is(.doc-table-addrow, .doc-table-addcol) { opacity:1; pointer-events:auto; }
|
|
21914
|
+
}
|
|
21259
21915
|
/* cell range selection (box select): soft fill per cell; the accent rectangle
|
|
21260
21916
|
outline is drawn via inline inset box-shadow on the edge cells (see
|
|
21261
21917
|
cell-select.ts edgeShadow). While selecting, suppress the caret + native
|
|
@@ -21450,7 +22106,7 @@ var styles_default = ` :root {
|
|
|
21450
22106
|
.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
22107
|
.shv-search input { border:0; background:none; outline:none; color:var(--fg); font-size:13px; width:180px; }
|
|
21452
22108
|
|
|
21453
|
-
.shv-list { display:flex; flex-direction:column; gap:8px; }
|
|
22109
|
+
.shv-list { display:flex; flex-direction:column; gap:8px; list-style:none; padding:0; margin:0; }
|
|
21454
22110
|
.shv-row { display:flex; align-items:center; gap:13px; padding:13px 15px; background:var(--bg); border:1px solid var(--line);
|
|
21455
22111
|
border-radius:var(--radius-lg); transition:border-color .12s, box-shadow .12s; }
|
|
21456
22112
|
.shv-row:hover { border-color:var(--line-strong); box-shadow:var(--shadow-sm); }
|
|
@@ -21472,7 +22128,25 @@ var styles_default = ` :root {
|
|
|
21472
22128
|
.shv-exp.dead { text-decoration:line-through; }
|
|
21473
22129
|
.shv-actions { display:flex; align-items:center; gap:6px; flex:none; }
|
|
21474
22130
|
.shv-actions .btn { padding:5px 10px; font-size:12px; }
|
|
21475
|
-
@media (max-width:640px){
|
|
22131
|
+
@media (max-width:640px){
|
|
22132
|
+
/* head stacks: title/desc full row, then segmented tabs, then search */
|
|
22133
|
+
.shares-page .db-head { flex-direction:column; align-items:stretch; gap:12px; }
|
|
22134
|
+
.shv-tools { margin-left:0; width:100%; }
|
|
22135
|
+
.shv-seg { display:flex; width:100%; }
|
|
22136
|
+
.shv-seg-btn { flex:1; text-align:center; }
|
|
22137
|
+
.shv-search { flex:1; }
|
|
22138
|
+
.shv-search input { width:100%; flex:1; }
|
|
22139
|
+
|
|
22140
|
+
/* cards: title clamps to two lines, meta wraps naturally */
|
|
22141
|
+
.shv-row { flex-wrap:wrap; }
|
|
22142
|
+
.shv-title { max-width:100%; white-space:normal; display:-webkit-box;
|
|
22143
|
+
-webkit-line-clamp:2; -webkit-box-orient:vertical; text-align:left; }
|
|
22144
|
+
.shv-meta { white-space:normal; overflow:visible; text-overflow:clip; }
|
|
22145
|
+
|
|
22146
|
+
/* actions: full row of evenly stretched touch targets */
|
|
22147
|
+
.shv-actions { width:100%; }
|
|
22148
|
+
.shv-actions .btn { flex:1; justify-content:center; padding:7px 10px; }
|
|
22149
|
+
}
|
|
21476
22150
|
|
|
21477
22151
|
/* in-context "\u5DF2\u5206\u4EAB" badge on docs / tables / site cards */
|
|
21478
22152
|
.share-badge { display:inline-grid; place-items:center; width:22px; height:22px; border-radius:6px; color:var(--accent);
|
|
@@ -21549,7 +22223,9 @@ var styles_default = ` :root {
|
|
|
21549
22223
|
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
22224
|
.peek.open { transform:none; }
|
|
21551
22225
|
.peek-head { display:flex; align-items:center; gap:4px; padding:12px 16px; border-bottom:1px solid var(--line); }
|
|
21552
|
-
|
|
22226
|
+
/* Same stable-gutter treatment as .content (stable only \u2014 see .content on
|
|
22227
|
+
why both-edges is forbidden). */
|
|
22228
|
+
.peek-body { overflow:auto; scrollbar-gutter:stable; padding:28px 32px; }
|
|
21553
22229
|
.peek h2 { font-size:27px; font-weight:700; letter-spacing:-.02em; margin:0 0 20px; outline:none; }
|
|
21554
22230
|
.proprow { display:flex; gap:8px; align-items:flex-start; margin:1px 0; }
|
|
21555
22231
|
.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 +22282,7 @@ var styles_default = ` :root {
|
|
|
21606
22282
|
.hist-restore { margin-top:8px; align-self:flex-start; }
|
|
21607
22283
|
|
|
21608
22284
|
/* menu / popover */
|
|
21609
|
-
.menu-layer { position:fixed; inset:0; z-index:
|
|
22285
|
+
.menu-layer { position:fixed; inset:0; z-index:115; }
|
|
21610
22286
|
.pop { position:fixed; background:var(--bg); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-lg);
|
|
21611
22287
|
padding:6px; min-width:232px; max-height:70vh; overflow:auto; animation:pop .12s ease; }
|
|
21612
22288
|
@keyframes pop { from { opacity:0; transform:translateY(-4px); } }
|
|
@@ -21624,6 +22300,9 @@ var styles_default = ` :root {
|
|
|
21624
22300
|
.pop .item .meta { flex:1; min-width:0; }
|
|
21625
22301
|
.pop .item .t { font-size:var(--fs-ui); } .pop .item .d { font-size:var(--fs-xs); color:var(--muted); display:block; }
|
|
21626
22302
|
.pop .item .chk { color:var(--accent); margin-left:auto; }
|
|
22303
|
+
/* shortcut badge, mirrors .sb-search kbd; margin-left:auto pins it right (and
|
|
22304
|
+
a trailing .chk, whose own auto then collapses, rides the gap beside it) */
|
|
22305
|
+
.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
22306
|
.pop .item.danger { color:var(--danger); } .pop .item.danger .lico { color:var(--danger); }
|
|
21628
22307
|
.pop .sep { height:1px; background:var(--line); margin:5px 4px; }
|
|
21629
22308
|
.pop .typegrid { display:grid; grid-template-columns:repeat(4,1fr); gap:4px; padding:2px 2px 4px; }
|
|
@@ -21743,24 +22422,28 @@ var styles_default = ` :root {
|
|
|
21743
22422
|
.view-placeholder { padding:40px; text-align:center; border:1px dashed var(--line-strong); border-radius:var(--radius); color:var(--muted); margin-top:8px; }
|
|
21744
22423
|
.ico.flip { transform:scaleX(-1); }
|
|
21745
22424
|
|
|
21746
|
-
/* ---- find in document (Ctrl-F) ---- */
|
|
21747
|
-
/*
|
|
21748
|
-
|
|
21749
|
-
|
|
22425
|
+
/* ---- find in document (Ctrl-F, CM6 bar) ---- */
|
|
22426
|
+
/* Match highlights are CM6 Decoration.marks (.cm-find/.cm-find-cur, editor-theme.ts).
|
|
22427
|
+
Matches INSIDE void widgets (code mirror, table cells) can't be mark
|
|
22428
|
+
decorations \u2014 the block replace widget swallows them \u2014 so those paint via
|
|
22429
|
+
the CSS Custom Highlight API (find.ts collectMatches/applyHighlights). */
|
|
22430
|
+
::highlight(mh-find-all) { background: color-mix(in srgb, var(--accent) 26%, transparent); border-radius: 2px; }
|
|
22431
|
+
::highlight(mh-find-current) { background: var(--accent); color: var(--accent-fg); }
|
|
21750
22432
|
.find-bar {
|
|
21751
|
-
position:fixed; top:58px; right:18px; z-index:
|
|
22433
|
+
position:fixed; top:58px; right:18px; z-index:96; /* below the ~50px topbar */
|
|
21752
22434
|
display:flex; align-items:center; gap:4px;
|
|
21753
22435
|
padding:5px 6px; background:var(--bg); border:1px solid var(--line-strong);
|
|
21754
22436
|
border-radius:var(--radius); box-shadow:var(--shadow-lg);
|
|
21755
22437
|
}
|
|
21756
22438
|
.find-bar .find-ico { color:var(--muted); margin:0 2px; flex:none; }
|
|
21757
22439
|
.find-bar input {
|
|
21758
|
-
width:200px; max-width:42vw; border:0; outline:0; background:transparent;
|
|
22440
|
+
width:200px; max-width:42vw; min-width:0; border:0; outline:0; background:transparent;
|
|
21759
22441
|
color:var(--fg); font-size:13px; padding:3px 2px;
|
|
21760
22442
|
}
|
|
21761
22443
|
.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; }
|
|
22444
|
+
.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
22445
|
.find-opt {
|
|
22446
|
+
flex:none; white-space:nowrap;
|
|
21764
22447
|
border:1px solid var(--line-strong); background:transparent; color:var(--fg-soft);
|
|
21765
22448
|
border-radius:var(--radius-sm); font-size:12px; line-height:1; padding:4px 6px; cursor:pointer;
|
|
21766
22449
|
transition:background .12s, color .12s, border-color .12s;
|
|
@@ -21780,6 +22463,14 @@ var styles_default = ` :root {
|
|
|
21780
22463
|
.find-bar { left:12px; right:12px; }
|
|
21781
22464
|
.find-bar input { width:auto; flex:1; max-width:none; }
|
|
21782
22465
|
}
|
|
22466
|
+
/* Very narrow windows (quicknote resizes down to 280px): drop the reserved
|
|
22467
|
+
count width and tighten gaps so the option buttons never get crushed. */
|
|
22468
|
+
@media (max-width:360px) {
|
|
22469
|
+
.find-bar { gap:2px; padding:5px 4px; }
|
|
22470
|
+
.find-count { min-width:0; }
|
|
22471
|
+
}
|
|
22472
|
+
/* Quicknote window: its qn-bar is ~34px, not the main ~50px topbar. */
|
|
22473
|
+
body.quicknote .find-bar { top:42px; }
|
|
21783
22474
|
|
|
21784
22475
|
/* ---- board (kanban) view ---- */
|
|
21785
22476
|
.board-wrap { margin-top:4px; }
|
|
@@ -21936,8 +22627,7 @@ var styles_default = ` :root {
|
|
|
21936
22627
|
.navitem { padding:9px 8px; font-size:16px; }
|
|
21937
22628
|
.navitem .emoji { font-size:18px; }
|
|
21938
22629
|
.navitem .tw { width:26px; height:26px; }
|
|
21939
|
-
.sb-
|
|
21940
|
-
.sb-search kbd { display:none; } /* no hardware keyboard, no shortcut hint */
|
|
22630
|
+
.sb-search kbd, .pop .item kbd { display:none; } /* no hardware keyboard, no shortcut hint */
|
|
21941
22631
|
/* Search box is hidden until the header search button reveals it; it then
|
|
21942
22632
|
slides+fades down in. Desktop base (.sb-search{display:flex}) is untouched,
|
|
21943
22633
|
so search stays always-on there. reduced-motion is handled globally. */
|
|
@@ -21952,17 +22642,26 @@ var styles_default = ` :root {
|
|
|
21952
22642
|
/* Document: bigger body text, tighter side padding for narrow screens. */
|
|
21953
22643
|
.doc { padding:24px 18px calc(36vh + env(safe-area-inset-bottom)); }
|
|
21954
22644
|
.doc-title { font-size:30px; }
|
|
21955
|
-
|
|
22645
|
+
/* CM6 editor (single-document): its .cm-editor inherits from here, so the whole
|
|
22646
|
+
body incl. table cells gets 16px on mobile \u2014 same iOS anti-zoom reason. */
|
|
22647
|
+
.doc .cm-doc-body { font-size:16px; }
|
|
21956
22648
|
/* 16px (not 14): a focused code textarea below 16px makes iOS auto-zoom. */
|
|
21957
22649
|
.codeblock { --code-fs:16px; }
|
|
21958
|
-
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
22650
|
+
/* CM6 hover gutter: no hover on touch, and -52px overlays would overflow
|
|
22651
|
+
the tight mobile margins \u2014 hide. */
|
|
22652
|
+
.cm-gutter-layer { display:none; }
|
|
22653
|
+
/* Autofit tables: on phones the atomic code span IS the width cap \u2014 one
|
|
22654
|
+
long \`file-name.md\` becomes the column's min-content, eats the whole
|
|
22655
|
+
100cqw budget and crushes prose columns to their 48px floor. Let code
|
|
22656
|
+
wrap at its natural break points (hyphens/slashes) instead; wider
|
|
22657
|
+
layouts keep the atomic-code tradeoff (see the autofit rules). */
|
|
22658
|
+
table.doc-table.autofit .doc-td code { white-space:pre-wrap; overflow-wrap:normal; word-break:normal; }
|
|
21962
22659
|
/* Find bar clears the taller mobile topbar (49px min + notch inset). */
|
|
21963
22660
|
.find-bar { top:calc(49px + env(safe-area-inset-top) + 8px); }
|
|
21964
22661
|
/* narrower handle gutter on phones; .db left padding must be \u2265 --grip-gutter */
|
|
21965
22662
|
.db { padding:16px 14px 90px 24px; }
|
|
22663
|
+
/* pages reusing .db without row grips don't need the left gutter \u2014 keep padding symmetric */
|
|
22664
|
+
.shares-page.db { padding-left:14px; }
|
|
21966
22665
|
.gridhost { --grip-gutter:24px; }
|
|
21967
22666
|
.peek { width:100%; }
|
|
21968
22667
|
/* history drawer: the side-by-side list/preview doesn't fit a phone \u2014 stack. */
|
|
@@ -21997,13 +22696,19 @@ var styles_default = ` :root {
|
|
|
21997
22696
|
@media (pointer: coarse) {
|
|
21998
22697
|
/* iOS Safari auto-zooms the page when a focused field is below 16px; the
|
|
21999
22698
|
class selectors out-specify the element-level sizes set on these. */
|
|
22000
|
-
input, textarea, .text-input,
|
|
22699
|
+
input, textarea, .text-input, input.inlineedit { font-size:16px; }
|
|
22001
22700
|
.navitem .acts button { width:34px; height:34px; }
|
|
22701
|
+
.sb-tab { min-height:40px; padding:8px 10px; }
|
|
22702
|
+
.sb-tabs .add { width:40px; height:40px; display:grid; place-items:center; padding:0; }
|
|
22002
22703
|
.iconbtn { width:40px; height:40px; }
|
|
22003
22704
|
.btn { padding:9px 14px; min-height:40px; }
|
|
22004
22705
|
.pop .item { padding:11px 10px; }
|
|
22005
22706
|
/* Column/sidebar resize handles: 7px is a mouse affordance, not a finger's. */
|
|
22006
22707
|
.col-resizer, .doc-col-resizer, .sb-resizer { width:16px; right:-8px; }
|
|
22708
|
+
/* cell-rectangle corner handle: big enough to grab with a finger (it is the
|
|
22709
|
+
only way to grow a touch selection \u2014 pan gestures stay scrolls) */
|
|
22710
|
+
.doc-cell-handle { width:12px; height:12px; right:-6px; bottom:-6px; }
|
|
22711
|
+
.doc-cell-handle::after { inset:-10px; }
|
|
22007
22712
|
}
|
|
22008
22713
|
|
|
22009
22714
|
/* No hover available (touch): controls that only materialize on :hover are
|
|
@@ -22012,9 +22717,11 @@ var styles_default = ` :root {
|
|
|
22012
22717
|
@media (hover: none) {
|
|
22013
22718
|
.code-tools { opacity:1; transform:none; pointer-events:auto; }
|
|
22014
22719
|
.rowactions { opacity:1; pointer-events:auto; }
|
|
22015
|
-
.
|
|
22016
|
-
.navitem .acts
|
|
22017
|
-
|
|
22720
|
+
.cal-add, .filerow .facts,
|
|
22721
|
+
.navitem .acts { opacity:1; }
|
|
22722
|
+
/* the pill handles survive on touch (focus-driven, not hover-driven), but
|
|
22723
|
+
sticky tap-hover must not tint them */
|
|
22724
|
+
.doc-row-handle:hover, .doc-col-handle:hover { background:var(--bg); color:var(--muted); }
|
|
22018
22725
|
.selcell input { opacity:1; }
|
|
22019
22726
|
.tl-item .tl-h { opacity:1; }
|
|
22020
22727
|
/* Neutralize the stickiest tap-residue tints (a tap "hovers" until the
|
|
@@ -22026,12 +22733,16 @@ var styles_default = ` :root {
|
|
|
22026
22733
|
.filerow:hover { background:none; }
|
|
22027
22734
|
.cal-day:hover { background:none; }
|
|
22028
22735
|
.cal-day.dim:hover { background:var(--surface); }
|
|
22736
|
+
.doc-table-addcol:hover, .doc-table-addrow:hover { background:var(--surface); color:var(--muted); border-color:var(--line-strong); }
|
|
22029
22737
|
}
|
|
22030
22738
|
|
|
22031
22739
|
/* Quick Notes window (desktop, loaded at #quick). A transparent body lets the
|
|
22032
22740
|
macOS vibrancy blur show through; a translucent panel keeps text readable.
|
|
22033
22741
|
The top bar is the window drag region; its buttons opt back out. */
|
|
22034
|
-
|
|
22742
|
+
/* --pop-top-inset: editor popups (slash menu) clamp their upward growth below
|
|
22743
|
+
this line, so they never cover the drag bar / macOS traffic lights (bar =
|
|
22744
|
+
26px actions + 2\xD74px padding). */
|
|
22745
|
+
body.quicknote { background:transparent; --pop-top-inset:38px; }
|
|
22035
22746
|
/* html carries an opaque bg for the iOS 26 Safari toolbar tint; the vibrancy
|
|
22036
22747
|
window needs the root transparent again or the blur is walled off. */
|
|
22037
22748
|
html:has(body.quicknote) { background:transparent; }
|
|
@@ -22051,22 +22762,32 @@ var styles_default = ` :root {
|
|
|
22051
22762
|
.qn-actions { display:flex; gap:2px; -webkit-app-region:no-drag; }
|
|
22052
22763
|
.qn-actions .iconbtn { width:26px; height:26px; }
|
|
22053
22764
|
.qn-actions .iconbtn.active { color:var(--accent); background:var(--hover-2); }
|
|
22054
|
-
|
|
22055
|
-
|
|
22765
|
+
/* Quicknote scrolling: the app-wide thin translucent scrollbar (transparent
|
|
22766
|
+
track, so the reserved lane just reads as right margin). stable reserves
|
|
22767
|
+
the lane up front so width never jumps when the doc grows past one
|
|
22768
|
+
screen; the doc's right padding + lane \u2248 left padding, keeping the prose
|
|
22769
|
+
visually centered. stable only, NEVER both-edges \u2014 see .content: a left
|
|
22770
|
+
gutter desyncs Chromium's scroll cull rect and paints a blank stripe over
|
|
22771
|
+
the rightmost 15px of content (it sliced this window's file cards). */
|
|
22772
|
+
/* scrollbar-width does NOT inherit (unlike scrollbar-color): without an
|
|
22773
|
+
explicit thin here the lane is the classic 15px. thin = 11px, so the
|
|
22774
|
+
doc's 1px right padding + 11px lane mirrors the 12px left padding. */
|
|
22775
|
+
.qn-body { flex:1; overflow:auto; scrollbar-gutter:stable; scrollbar-width:thin; -webkit-app-region:no-drag; }
|
|
22776
|
+
.qn-body .doc { max-width:none; margin:0; padding:14px 1px 30vh 12px; }
|
|
22056
22777
|
.qn-body .doc-title { font-size:24px; margin-bottom:2px; }
|
|
22057
22778
|
.qn-body .doc-meta { display:none; }
|
|
22058
|
-
/* The
|
|
22059
|
-
desktop window never matches \u2014 re-hide explicitly: quick notes offer no
|
|
22779
|
+
/* The CM6 hover gutter never fits the compact window: quick notes offer no
|
|
22060
22780
|
block insert/drag handles (Enter / slash commands cover insertion). */
|
|
22061
|
-
body.quicknote .
|
|
22781
|
+
body.quicknote .cm-gutter-layer { display:none; }
|
|
22062
22782
|
/* A native checkbox paints an opaque white box when unchecked, which breaks
|
|
22063
22783
|
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
|
-
|
|
22784
|
+
stays transparent; checked falls back to the accent fill + checkmark.
|
|
22785
|
+
(Same treatment main gave \`.b-todo .marker input\`.) */
|
|
22786
|
+
body.quicknote .cm-todo-box {
|
|
22066
22787
|
appearance:none; -webkit-appearance:none; box-sizing:border-box;
|
|
22067
22788
|
border:1.5px solid color-mix(in srgb, var(--fg-soft) 55%, transparent);
|
|
22068
22789
|
border-radius:4px; background:transparent; }
|
|
22069
|
-
body.quicknote .
|
|
22790
|
+
body.quicknote .cm-todo-box:checked {
|
|
22070
22791
|
border-color:var(--accent);
|
|
22071
22792
|
background:var(--accent) center/10px no-repeat
|
|
22072
22793
|
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 +22809,11 @@ var styles_default = ` :root {
|
|
|
22088
22809
|
.qn-set-main { flex:1; min-width:0; }
|
|
22089
22810
|
.qn-set-name { font-weight:600; }
|
|
22090
22811
|
.qn-set-desc { color:var(--muted); font-size:13px; margin-top:2px; }
|
|
22091
|
-
.qn-shortcut { min-width:140px; justify-content:center; font-family
|
|
22812
|
+
.qn-shortcut { min-width:140px; justify-content:center; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",sans-serif; }
|
|
22092
22813
|
.qn-shortcut.capturing { outline:2px solid var(--accent); color:var(--accent); }
|
|
22814
|
+
.qn-keys { display:inline-flex; align-items:center; gap:6px; }
|
|
22815
|
+
.qn-key { line-height:1; }
|
|
22816
|
+
.qn-key.sym { font-size:1.15em; line-height:1; } /* \u2318 \u21E7 glyphs render smaller \u2014 bump to match letters */
|
|
22093
22817
|
|
|
22094
22818
|
/* No-origin bootstrap: enroll screen + hydration splash (data-blind shell). */
|
|
22095
22819
|
.enroll-splash { min-height:100dvh; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px; padding:24px; }
|
|
@@ -22103,6 +22827,44 @@ var styles_default = ` :root {
|
|
|
22103
22827
|
.enroll-err { color:var(--danger); font-size:13px; margin-top:12px; }
|
|
22104
22828
|
.enroll-go { width:100%; justify-content:center; margin-top:18px; }
|
|
22105
22829
|
.enroll-hint { font-size:12px; margin-top:14px; line-height:1.5; }
|
|
22830
|
+
.enroll-card { animation:enroll-in .35s ease both; }
|
|
22831
|
+
@keyframes enroll-in { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
|
|
22832
|
+
|
|
22833
|
+
/* Scan-first enroll: square viewfinder (corner frame + sweeping accent beam)
|
|
22834
|
+
over the live camera feed. The chip stays black in both themes \u2014 the feed
|
|
22835
|
+
brings its own colors; accent carries the beam and the success flash. */
|
|
22836
|
+
.enroll-scan { position:relative; aspect-ratio:1/1; margin:2px 0 12px; border-radius:16px; overflow:hidden; background:#0b0b0e; }
|
|
22837
|
+
.enroll-scan video { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
|
|
22838
|
+
.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); }
|
|
22839
|
+
.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; }
|
|
22840
|
+
.enroll-scan-beam { position:absolute; left:9%; right:9%; top:12%; height:2px; border-radius:2px; pointer-events:none; opacity:.85;
|
|
22841
|
+
background:linear-gradient(90deg, transparent, var(--accent), transparent);
|
|
22842
|
+
animation:enroll-beam 2.4s ease-in-out infinite alternate; }
|
|
22843
|
+
@keyframes enroll-beam { to { top:86%; } }
|
|
22844
|
+
.enroll-scan.hit .enroll-scan-frame { stroke:var(--accent); }
|
|
22845
|
+
.enroll-scan.hit::after { animation:enroll-hit .38s ease-out; }
|
|
22846
|
+
@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); } }
|
|
22847
|
+
.enroll-scan-wait { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:rgba(255,255,255,.45); }
|
|
22848
|
+
.enroll-scan-waitico { width:30px; height:30px; animation:enroll-wait 1.6s ease-in-out infinite; }
|
|
22849
|
+
@keyframes enroll-wait { 0%,100% { opacity:.4; } 50% { opacity:.9; } }
|
|
22850
|
+
.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; }
|
|
22851
|
+
.enroll-cam-err .muted { color:rgba(255,255,255,.55); font-size:12px; }
|
|
22852
|
+
.enroll-cam-errico { width:26px; height:26px; opacity:.7; }
|
|
22853
|
+
.enroll-cam-err .btn { margin-top:8px; }
|
|
22854
|
+
.enroll-paste { width:100%; font-size:13px; }
|
|
22855
|
+
.enroll-switch { display:flex; justify-content:center; align-items:center; gap:10px; margin-top:14px; }
|
|
22856
|
+
.enroll-switch-dot { color:var(--muted); }
|
|
22857
|
+
.enroll-link { background:none; border:0; padding:0; font:inherit; font-size:13px; color:var(--accent); cursor:pointer; }
|
|
22858
|
+
.enroll-link:hover { text-decoration:underline; }
|
|
22859
|
+
/* Unlock step: compact bucket summary chip above the passphrase field. */
|
|
22860
|
+
.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); }
|
|
22861
|
+
.enroll-bucket-ico { width:18px; height:18px; color:var(--muted); }
|
|
22862
|
+
.enroll-bucket-meta { flex:1; min-width:0; }
|
|
22863
|
+
.enroll-bucket-name { font-weight:600; font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
22864
|
+
.enroll-bucket-host { font-family:var(--mono); font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
22865
|
+
.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;
|
|
22866
|
+
border:1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
|
|
22867
|
+
.enroll-bucket-badge .ico { width:12px; height:12px; }
|
|
22106
22868
|
|
|
22107
22869
|
/* Enroll QR (settings \u2192 \u540C\u6B65\u5B58\u50A8 \u2192 \u5728\u624B\u673A\u4E0A\u6253\u5F00). The QR inverts with the
|
|
22108
22870
|
theme: light = dark modules on a light chip, dark = light modules on a dark
|
|
@@ -22305,6 +23067,42 @@ var ICON_192 = "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAAXNSR0IArs4c6QAA
|
|
|
22305
23067
|
var ICON_512 = "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";
|
|
22306
23068
|
var ICON_180 = "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";
|
|
22307
23069
|
|
|
23070
|
+
// src/webui/fmt/manifest.ts
|
|
23071
|
+
var FMT_PROVIDERS = [
|
|
23072
|
+
{ id: "core", js: "/webui-fmt.js", entry: "fmt/fmt-core.ts" },
|
|
23073
|
+
{
|
|
23074
|
+
id: "ruff",
|
|
23075
|
+
js: "/webui-fmt-ruff.js",
|
|
23076
|
+
entry: "fmt/fmt-ruff.ts",
|
|
23077
|
+
wasm: { route: "/webui-fmt-ruff.wasm", pkg: "@wasm-fmt/ruff_fmt", file: "ruff_fmt_bg.wasm" }
|
|
23078
|
+
},
|
|
23079
|
+
{
|
|
23080
|
+
id: "gofmt",
|
|
23081
|
+
js: "/webui-fmt-gofmt.js",
|
|
23082
|
+
entry: "fmt/fmt-gofmt.ts",
|
|
23083
|
+
wasm: { route: "/webui-fmt-gofmt.wasm", pkg: "@wasm-fmt/gofmt", file: "gofmt.wasm" }
|
|
23084
|
+
},
|
|
23085
|
+
{
|
|
23086
|
+
id: "clang",
|
|
23087
|
+
js: "/webui-fmt-clang.js",
|
|
23088
|
+
entry: "fmt/fmt-clang.ts",
|
|
23089
|
+
wasm: { route: "/webui-fmt-clang.wasm", pkg: "@wasm-fmt/clang-format", file: "clang-format.wasm" }
|
|
23090
|
+
},
|
|
23091
|
+
{
|
|
23092
|
+
id: "lua",
|
|
23093
|
+
js: "/webui-fmt-lua.js",
|
|
23094
|
+
entry: "fmt/fmt-lua.ts",
|
|
23095
|
+
wasm: { route: "/webui-fmt-lua.wasm", pkg: "@wasm-fmt/lua_fmt", file: "lua_fmt_bg.wasm" }
|
|
23096
|
+
},
|
|
23097
|
+
{
|
|
23098
|
+
id: "taplo",
|
|
23099
|
+
js: "/webui-fmt-taplo.js",
|
|
23100
|
+
entry: "fmt/fmt-taplo.ts",
|
|
23101
|
+
wasm: { route: "/webui-fmt-taplo.wasm", pkg: "@wasm-fmt/taplo_fmt", file: "taplo_fmt_bg.wasm" }
|
|
23102
|
+
},
|
|
23103
|
+
{ id: "sh", js: "/webui-fmt-sh.js", entry: "fmt/fmt-sh.ts" }
|
|
23104
|
+
];
|
|
23105
|
+
|
|
22308
23106
|
// src/webui/server/assets.ts
|
|
22309
23107
|
var RUNNING_FROM_SOURCE = import.meta.url.includes("/src/webui/");
|
|
22310
23108
|
var HTML2 = `<!doctype html>
|
|
@@ -22349,14 +23147,25 @@ var injected = {
|
|
|
22349
23147
|
sdk: null
|
|
22350
23148
|
};
|
|
22351
23149
|
var injectedWasm = null;
|
|
23150
|
+
var injectedFmtWasm = {};
|
|
23151
|
+
function toArrayBuffer(w) {
|
|
23152
|
+
return w.byteOffset === 0 && w.byteLength === w.buffer.byteLength ? w.buffer : w.slice().buffer;
|
|
23153
|
+
}
|
|
22352
23154
|
function setWebuiBundle(bundle) {
|
|
22353
23155
|
injected.js = bundle.js;
|
|
22354
23156
|
injected.sw = bundle.sw;
|
|
22355
23157
|
injected.dbWorker = bundle.dbWorker;
|
|
22356
23158
|
injected.runtime = bundle.runtime;
|
|
22357
23159
|
injected.sdk = bundle.sdk;
|
|
22358
|
-
|
|
22359
|
-
|
|
23160
|
+
injectedWasm = toArrayBuffer(bundle.wasm);
|
|
23161
|
+
for (const p of FMT_PROVIDERS) {
|
|
23162
|
+
const js = bundle.fmt?.[p.js];
|
|
23163
|
+
if (js != null)
|
|
23164
|
+
injected[`fmt_${p.id}`] = js;
|
|
23165
|
+
const w = p.wasm && bundle.fmtWasm?.[p.wasm.route];
|
|
23166
|
+
if (p.wasm && w)
|
|
23167
|
+
injectedFmtWasm[p.wasm.route] = toArrayBuffer(w);
|
|
23168
|
+
}
|
|
22360
23169
|
}
|
|
22361
23170
|
function newestSourceMtime(dir) {
|
|
22362
23171
|
let newest = 0;
|
|
@@ -22406,6 +23215,24 @@ function bundleGetter(opts) {
|
|
|
22406
23215
|
}
|
|
22407
23216
|
var getJs = bundleGetter({ key: "js", entry: "../app.tsx", dist: "webui.js" });
|
|
22408
23217
|
var getSwRaw = bundleGetter({ key: "sw", entry: "../sw.ts", dist: "sw.js" });
|
|
23218
|
+
var fmtJs = {};
|
|
23219
|
+
for (const p of FMT_PROVIDERS) {
|
|
23220
|
+
injected[`fmt_${p.id}`] = null;
|
|
23221
|
+
fmtJs[p.js] = bundleGetter({ key: `fmt_${p.id}`, entry: `../${p.entry}`, dist: p.js.slice(1) });
|
|
23222
|
+
}
|
|
23223
|
+
var fmtWasmByRoute = Object.fromEntries(FMT_PROVIDERS.filter((p) => p.wasm).map((p) => [p.wasm.route, p]));
|
|
23224
|
+
async function getFmtWasm(p) {
|
|
23225
|
+
const w = p.wasm;
|
|
23226
|
+
const embedded = injectedFmtWasm[w.route];
|
|
23227
|
+
if (embedded != null)
|
|
23228
|
+
return embedded;
|
|
23229
|
+
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));
|
|
23230
|
+
const f = Bun.file(path);
|
|
23231
|
+
if (!await f.exists()) {
|
|
23232
|
+
throw new Error(`${w.route} missing at ${path} \u2014 run \`bun install\` / \`bun run build\``);
|
|
23233
|
+
}
|
|
23234
|
+
return f.arrayBuffer();
|
|
23235
|
+
}
|
|
22409
23236
|
var getDbWorker = bundleGetter({ key: "dbWorker", entry: "../data/db-worker.ts", dist: "db-worker.js" });
|
|
22410
23237
|
var getRuntime = bundleGetter({ key: "runtime", entry: "../runtime.ts", dist: "mh-runtime.js" });
|
|
22411
23238
|
var getSdk = bundleGetter({ key: "sdk", entry: "../../sdk/client.ts", dist: "metahub-sdk.js", extraDirs: ["../../sdk"] });
|
|
@@ -22487,12 +23314,24 @@ async function serveWebui(req) {
|
|
|
22487
23314
|
return new Response(`sqlite3.wasm unavailable: ${e}`, { status: 500 });
|
|
22488
23315
|
}
|
|
22489
23316
|
}
|
|
23317
|
+
const fmtWasmProvider = fmtWasmByRoute[pathname];
|
|
23318
|
+
if (fmtWasmProvider) {
|
|
23319
|
+
try {
|
|
23320
|
+
return new Response(await getFmtWasm(fmtWasmProvider), {
|
|
23321
|
+
headers: { "content-type": "application/wasm", "cache-control": "public, max-age=86400" }
|
|
23322
|
+
});
|
|
23323
|
+
} catch (e) {
|
|
23324
|
+
console.error(`[webui] failed to serve ${pathname} \u2014`, e);
|
|
23325
|
+
return new Response(`${pathname} unavailable: ${e}`, { status: 500 });
|
|
23326
|
+
}
|
|
23327
|
+
}
|
|
22490
23328
|
const script = {
|
|
22491
23329
|
"/webui.js": getJsStamped,
|
|
22492
23330
|
"/sw.js": getSw,
|
|
22493
23331
|
"/db-worker.js": getDbWorker,
|
|
22494
23332
|
"/mh-runtime.js": getRuntime,
|
|
22495
|
-
"/metahub-sdk.js": getSdk
|
|
23333
|
+
"/metahub-sdk.js": getSdk,
|
|
23334
|
+
...fmtJs
|
|
22496
23335
|
};
|
|
22497
23336
|
const getter = script[pathname];
|
|
22498
23337
|
if (getter) {
|
|
@@ -24370,6 +25209,7 @@ export {
|
|
|
24370
25209
|
ensureDirs,
|
|
24371
25210
|
emitFields,
|
|
24372
25211
|
emit,
|
|
25212
|
+
editDocumentBatch,
|
|
24373
25213
|
editDocument,
|
|
24374
25214
|
duplicateDocument,
|
|
24375
25215
|
duplicateDatabase,
|
|
@@ -24422,4 +25262,4 @@ export {
|
|
|
24422
25262
|
BLOB_HASH_HEX
|
|
24423
25263
|
};
|
|
24424
25264
|
|
|
24425
|
-
//# debugId=
|
|
25265
|
+
//# debugId=F05E548741611AE364756E2164756E21
|