@tensorix/metahub 0.5.6 → 0.5.8

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.
@@ -1850,6 +1850,10 @@ var EN = {
1850
1850
  "(空文档)": "(empty document)",
1851
1851
  文档: "Document",
1852
1852
  表格: "Table",
1853
+ 记录: "Record",
1854
+ 关闭: "Close",
1855
+ 查看全文: "Read full text",
1856
+ "查看「{name}」全文": "Read full {name}",
1853
1857
  编辑: "Edit",
1854
1858
  编辑文档: "Edit document",
1855
1859
  保存: "Save",
package/dist/index.js CHANGED
@@ -9389,7 +9389,7 @@ var package_default;
9389
9389
  var init_package = __esm(() => {
9390
9390
  package_default = {
9391
9391
  name: "@tensorix/metahub",
9392
- version: "0.5.6",
9392
+ version: "0.5.8",
9393
9393
  description: "Metahub core library and CLI",
9394
9394
  license: "AGPL-3.0-only",
9395
9395
  type: "module",
@@ -12038,7 +12038,7 @@ var init_storage_s3_sign = __esm(() => {
12038
12038
 
12039
12039
  // src/core/sync/storage-s3-bun.ts
12040
12040
  var {S3Client } = globalThis.Bun;
12041
- function makeClient(config) {
12041
+ function makeBunStorageClient(config) {
12042
12042
  const vhost = isVirtualHostedStyle(config);
12043
12043
  const s3 = new S3Client({
12044
12044
  accessKeyId: config.accessKeyId,
@@ -12082,7 +12082,7 @@ function makeClient(config) {
12082
12082
  if (opts?.ifMatch) {
12083
12083
  const res = await aws().fetch(objectUrl(key), {
12084
12084
  method: "PUT",
12085
- body,
12085
+ body: new Uint8Array(body).buffer,
12086
12086
  headers: {
12087
12087
  "if-match": opts.ifMatch,
12088
12088
  ...opts.contentType ? { "content-type": opts.contentType } : {}
@@ -12124,7 +12124,7 @@ var init_storage_s3_bun = __esm(() => {
12124
12124
  init_errors();
12125
12125
  init_storage();
12126
12126
  init_storage_s3_sign();
12127
- setStorageClientFactory(makeClient);
12127
+ setStorageClientFactory(makeBunStorageClient);
12128
12128
  setBucketCorsAdmin(putBucketCors);
12129
12129
  });
12130
12130
 
@@ -13280,6 +13280,10 @@ var init_page_messages = __esm(() => {
13280
13280
  "\uFF08\u7A7A\u6587\u6863\uFF09": "(empty document)",
13281
13281
  \u{6587}\u{6863}: "Document",
13282
13282
  \u{8868}\u{683c}: "Table",
13283
+ \u{8bb0}\u{5f55}: "Record",
13284
+ \u{5173}\u{95ed}: "Close",
13285
+ \u{67e5}\u{770b}\u{5168}\u{6587}: "Read full text",
13286
+ "\u67E5\u770B\u300C{name}\u300D\u5168\u6587": "Read full {name}",
13283
13287
  \u{7f16}\u{8f91}: "Edit",
13284
13288
  \u{7f16}\u{8f91}\u{6587}\u{6863}: "Edit document",
13285
13289
  \u{4fdd}\u{5b58}: "Save",
@@ -14972,7 +14976,7 @@ function copyScript(locale = "zh-CN") {
14972
14976
  var btn=document.getElementById('mh-copy');if(!btn)return;var L=${L};
14973
14977
  var label=btn.querySelector('span');var timer=0;
14974
14978
  function absolutize(root){root.querySelectorAll('img[src],a[href],video[src],audio[src]').forEach(function(el){var a=el.tagName==='A'?'href':'src';var v=el.getAttribute(a);if(v&&!/^(blob|data):/.test(v))el.setAttribute(a,new URL(v,location.href).href);});}
14975
- function payload(){var src=document.getElementById('mh-src');var text=src?src.content.textContent:'';var body=document.querySelector('article.doc')||document.querySelector('.table-wrap');var html='';if(body){var c=body.cloneNode(true);absolutize(c);html=c.innerHTML;}return{text:text,html:html};}
14979
+ function payload(){var src=document.getElementById('mh-src');var text=src?src.content.textContent:'';var body=document.querySelector('article.doc')||document.querySelector('.table-wrap');var html='';if(body){var c=body.cloneNode(true);c.querySelectorAll('.share-cell-expand').forEach(function(el){el.remove();});c.querySelectorAll('.share-cell-value').forEach(function(el){el.removeAttribute('contenteditable');el.removeAttribute('class');el.removeAttribute('style');});c.querySelectorAll('.share-table').forEach(function(el){el.removeAttribute('data-ready');});absolutize(c);html=c.innerHTML;}return{text:text,html:html};}
14976
14980
  function legacy(text){var ta=document.createElement('textarea');ta.value=text;ta.setAttribute('readonly','');ta.style.cssText='position:fixed;top:0;left:0;opacity:0';document.body.appendChild(ta);ta.select();var ok=false;try{ok=document.execCommand('copy');}catch(e){}ta.remove();return ok?Promise.resolve():Promise.reject(new Error('copy'));}
14977
14981
  function write(p){var nc=navigator.clipboard;if(nc&&nc.write&&window.ClipboardItem){try{return nc.write([new ClipboardItem({'text/plain':new Blob([p.text],{type:'text/plain'}),'text/html':new Blob([p.html],{type:'text/html'})})]);}catch(e){}}if(nc&&nc.writeText)return nc.writeText(p.text);return legacy(p.text);}
14978
14982
  function settle(cls,txt){clearTimeout(timer);btn.classList.remove('done','fail');void btn.offsetWidth;btn.classList.add(cls);if(label)label.textContent=txt;timer=setTimeout(function(){btn.classList.remove(cls);if(label)label.textContent=L.idle;},1600);}
@@ -14996,6 +15000,140 @@ var init_share_copy = __esm(() => {
14996
15000
  init_page_messages();
14997
15001
  });
14998
15002
 
15003
+ // src/core/sync/share-table-client.js
15004
+ function mountShareTable(labels) {
15005
+ const table = document.querySelector("table.share-table");
15006
+ const peek = document.querySelector(".share-text-peek");
15007
+ if (!table || !peek || typeof peek.showModal !== "function")
15008
+ return;
15009
+ const heading = peek.querySelector("h2");
15010
+ const record = peek.querySelector(".share-text-record");
15011
+ const body = peek.querySelector(".share-text-body");
15012
+ const feedback = peek.querySelector(".share-text-feedback");
15013
+ let restore = null;
15014
+ let selected = null;
15015
+ const select = (cell) => {
15016
+ selected?.classList.remove("is-selected");
15017
+ selected = cell;
15018
+ cell.classList.add("is-selected");
15019
+ };
15020
+ const measure = (value) => {
15021
+ const button = value.parentElement.querySelector(".share-cell-expand");
15022
+ button.hidden = !(value.scrollHeight > value.clientHeight + 1 || value.scrollWidth > value.clientWidth + 1);
15023
+ };
15024
+ table.dataset.ready = "";
15025
+ const values = Array.from(table.querySelectorAll(".share-cell-value"));
15026
+ const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver((entries) => {
15027
+ for (const entry of entries)
15028
+ measure(entry.target);
15029
+ }) : null;
15030
+ for (const value of values) {
15031
+ measure(value);
15032
+ observer?.observe(value);
15033
+ value.addEventListener("input", () => measure(value));
15034
+ value.addEventListener("blur", () => requestAnimationFrame(() => measure(value)));
15035
+ }
15036
+ document.fonts?.ready.then(() => values.forEach(measure));
15037
+ table.addEventListener("click", (event) => {
15038
+ const target = event.target;
15039
+ const cell = target.closest("td");
15040
+ if (!cell)
15041
+ return;
15042
+ select(cell);
15043
+ const button = target.closest(".share-cell-expand");
15044
+ if (!button)
15045
+ return;
15046
+ const column = Array.from(cell.parentElement.children).indexOf(cell);
15047
+ heading.textContent = table.querySelectorAll("thead th")[column].textContent;
15048
+ record.textContent = cell.parentElement.querySelector(".share-cell-value")?.textContent || labels.record;
15049
+ body.textContent = cell.querySelector(".share-cell-value").textContent;
15050
+ feedback.textContent = "";
15051
+ restore = button;
15052
+ peek.showModal();
15053
+ body.scrollTop = 0;
15054
+ });
15055
+ peek.querySelector(".share-text-close").addEventListener("click", () => peek.close());
15056
+ peek.addEventListener("click", (event) => {
15057
+ const rect = peek.getBoundingClientRect();
15058
+ if (event.target === peek && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom))
15059
+ peek.close();
15060
+ });
15061
+ peek.addEventListener("close", () => restore?.focus({ preventScroll: true }));
15062
+ peek.querySelector(".share-text-copy").addEventListener("click", async () => {
15063
+ const text = body.textContent ?? "";
15064
+ try {
15065
+ await navigator.clipboard.writeText(text);
15066
+ feedback.textContent = labels.copied;
15067
+ } catch {
15068
+ const input = document.createElement("textarea");
15069
+ input.value = text;
15070
+ input.readOnly = true;
15071
+ input.style.cssText = "position:fixed;opacity:0;top:0;left:0";
15072
+ peek.append(input);
15073
+ input.select();
15074
+ try {
15075
+ feedback.textContent = document.execCommand("copy") ? labels.copied : labels.failed;
15076
+ } catch {
15077
+ feedback.textContent = labels.failed;
15078
+ }
15079
+ input.remove();
15080
+ peek.querySelector(".share-text-copy").focus();
15081
+ }
15082
+ });
15083
+ }
15084
+
15085
+ // src/core/sync/share-table.ts
15086
+ function shareCellHtml(html, name, locale, editAttrs = "") {
15087
+ return `<td><div class="share-cell-value"${editAttrs ? ` ${editAttrs}` : ""}>${html}</div><button type="button" class="share-cell-expand" hidden aria-label="${escapeHtml(pm(locale, "\u67E5\u770B\u300C{name}\u300D\u5168\u6587", { name }))}" title="${escapeHtml(pm(locale, "\u67E5\u770B\u5168\u6587"))}">${expandIcon}</button></td>`;
15088
+ }
15089
+ function shareTableHtml(columns, rows, locale) {
15090
+ const widths = columns.map((p) => typeof p.width === "number" && Number.isFinite(p.width) ? Math.max(80, Math.min(640, p.width)) : 180);
15091
+ return `<div class="table-wrap"><table class="db share-table" style="width:${widths.reduce((a, b) => a + b, 0)}px"><colgroup>${widths.map((w) => `<col style="width:${w}px">`).join("")}</colgroup><thead><tr>${columns.map((p) => `<th scope="col" title="${escapeHtml(p.name)}">${escapeHtml(p.name)}</th>`).join("")}</tr></thead><tbody>${rows}</tbody></table></div>
15092
+ <dialog class="share-text-peek" aria-labelledby="share-text-heading">
15093
+ <div class="share-text-toolbar"><button type="button" class="share-text-close" aria-label="${escapeHtml(pm(locale, "\u5173\u95ED"))}" autofocus>${closeIcon}</button><button type="button" class="copy-btn share-text-copy">${escapeHtml(pm(locale, "\u590D\u5236\u5168\u6587"))}</button><span class="share-text-feedback" role="status"></span></div>
15094
+ <div class="share-text-heading"><h2 id="share-text-heading"></h2><p class="share-text-record"></p></div>
15095
+ <div class="share-text-body" tabindex="0"></div>
15096
+ </dialog>`;
15097
+ }
15098
+ function shareTableScript(locale) {
15099
+ return `(${mountShareTable.toString()})(${JSON.stringify({ copied: pm(locale, "\u5DF2\u590D\u5236"), failed: pm(locale, "\u590D\u5236\u5931\u8D25"), record: pm(locale, "\u8BB0\u5F55") })});`;
15100
+ }
15101
+ var expandIcon = '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16"/></svg>', closeIcon = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg>', SHARE_TABLE_CSS = `
15102
+ #app:has(.share-table),.wrap:has(.share-table){max-width:1440px}
15103
+ .table-wrap:has(.share-table){max-width:100%;overflow:auto;overscroll-behavior-x:contain}
15104
+ table.share-table{table-layout:fixed;min-width:100%}
15105
+ table.share-table th{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500;color:var(--muted)}
15106
+ table.share-table td{position:relative;vertical-align:top}
15107
+ .share-cell-value{min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.55}
15108
+ .share-table[data-ready] .share-cell-value{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;max-height:4.65em}
15109
+ .share-table .tag{max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;white-space:nowrap}
15110
+ .share-cell-expand{position:absolute;top:5px;right:5px;display:flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:1px solid var(--line);border-radius:5px;background:var(--bg);color:var(--fg);cursor:pointer;opacity:0;pointer-events:none}
15111
+ .share-cell-expand[hidden]{display:none}
15112
+ .share-cell-expand svg,.share-text-close svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
15113
+ .share-table td:is(:hover,:focus-within,.is-selected) .share-cell-expand{opacity:1;pointer-events:auto}
15114
+ .share-table td.is-selected{box-shadow:inset 0 0 0 1.5px var(--accent);background:var(--accent-soft)}
15115
+ .share-cell-expand:hover,.share-text-close:hover{background:var(--card)}
15116
+ .share-cell-expand:focus-visible,.share-text-close:focus-visible,.share-text-body:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
15117
+ .share-table[data-ready] [contenteditable]:focus{display:block;-webkit-line-clamp:unset;max-height:min(320px,40vh);overflow:auto;outline:2px solid var(--accent);outline-offset:2px}
15118
+ .share-cell-value.saving{opacity:.5}.share-cell-value.saved{background:color-mix(in srgb,var(--success) 18%,transparent)}
15119
+ .share-text-peek{position:fixed;inset:0 0 0 auto;margin:0;padding:0;width:min(620px,100vw);max-width:100vw;height:100vh;height:100dvh;max-height:100dvh;border:0;border-inline-start:1px solid var(--line);background:var(--bg);color:var(--fg);font:inherit;overflow:hidden}
15120
+ .share-text-peek[open]{display:flex;flex-direction:column}
15121
+ .share-text-peek::backdrop{background:rgba(15,15,25,.3)}
15122
+ body:has(.share-text-peek[open]){overflow:hidden}
15123
+ .share-text-toolbar{display:flex;align-items:center;gap:12px;flex:none;padding:12px 20px;border-bottom:1px solid var(--line)}
15124
+ .share-text-close{display:flex;align-items:center;justify-content:center;width:32px;height:32px;margin-inline-end:auto;border:0;border-radius:5px;background:transparent;color:var(--muted);cursor:pointer}
15125
+ .share-text-feedback{font-size:13px;color:var(--muted)}.share-text-feedback:empty{display:none}
15126
+ .share-text-heading{flex:none;padding:24px 32px 16px;min-width:0}
15127
+ .share-text-heading h2{margin:0;font-size:20px;line-height:1.4;overflow-wrap:anywhere}
15128
+ .share-text-record{margin:6px 0 0;font-size:13px;color:var(--muted);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere}
15129
+ .share-text-body{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:stable;padding:0 32px 40px;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.75;user-select:text}
15130
+ @media(max-width:600px){.share-text-peek{width:100vw}.share-text-toolbar{padding:10px 14px}.share-text-heading{padding:20px 20px 16px}.share-text-body{padding:0 20px 32px;font-size:16px}}
15131
+ @media(hover:none),(pointer:coarse){.share-cell-expand{opacity:1;pointer-events:auto;width:44px;height:44px;top:0;right:0}.share-table td:has(.share-cell-expand:not([hidden])) .share-cell-value{min-height:44px}.share-text-close{width:44px;height:44px}}
15132
+ `;
15133
+ var init_share_table = __esm(() => {
15134
+ init_page_messages();
15135
+ });
15136
+
14999
15137
  // src/core/sync/share-serve.ts
15000
15138
  function notFound() {
15001
15139
  return new Response("not found", { status: 404 });
@@ -15162,19 +15300,18 @@ async function serveTable(ctx, req, share, sub, locale) {
15162
15300
  }
15163
15301
  const titlesFor = (p) => p.type === "relation" ? relTitles.get(p.config?.database ?? "") : undefined;
15164
15302
  const editable = share.permission === "edit";
15165
- const head = props.map((p) => `<th>${escapeHtml(p.name)}</th>`).join("");
15166
15303
  const rows = records.map((r) => {
15167
15304
  const tds = props.map((p) => {
15168
15305
  const v = r.cells[p.id];
15169
15306
  if (editable && editableType(p.type)) {
15170
- return `<td contenteditable data-rec="${escapeHtml(r.id)}" data-prop="${escapeHtml(p.id)}" data-type="${p.type}">${escapeHtml(cellText(p, v))}</td>`;
15307
+ return shareCellHtml(escapeHtml(cellText(p, v)), p.name, locale, `contenteditable aria-label="${escapeHtml(p.name)}" data-rec="${escapeHtml(r.id)}" data-prop="${escapeHtml(p.id)}" data-type="${p.type}"`);
15171
15308
  }
15172
- return `<td>${cellHtml(p, v, titlesFor(p))}</td>`;
15309
+ return shareCellHtml(cellHtml(p, v, titlesFor(p)), p.name, locale);
15173
15310
  }).join("");
15174
15311
  return `<tr>${tds}</tr>`;
15175
15312
  }).join("");
15176
- const inner = `<div class="table-wrap"><table class="db"><thead><tr>${head}</tr></thead><tbody>${rows}</tbody></table></div>`;
15177
- const script = editable ? tableEditScript(locale, share.slug) : "";
15313
+ const inner = shareTableHtml(props.map((p) => ({ name: p.name, width: p.config?.width })), rows, locale);
15314
+ const script = shareTableScript(locale) + (editable ? tableEditScript(locale, share.slug) : "");
15178
15315
  const copySource = markdownTable(props.map((p) => p.name), records.map((r) => props.map((p) => cellText(p, r.cells[p.id], titlesFor(p)))));
15179
15316
  return new Response(pageShell(dbRow.name || pm(locale, "\u8868\u683C"), inner, {
15180
15317
  lang: locale,
@@ -15186,7 +15323,7 @@ async function serveTable(ctx, req, share, sub, locale) {
15186
15323
  function tableEditScript(locale, slug) {
15187
15324
  return `
15188
15325
  const L=${JSON.stringify({ failed: pm(locale, "\u4FDD\u5B58\u5931\u8D25") })};
15189
- document.querySelectorAll('td[contenteditable]').forEach(td=>{
15326
+ document.querySelectorAll('.share-cell-value[contenteditable]').forEach(td=>{
15190
15327
  let orig=td.textContent;
15191
15328
  td.addEventListener('blur', async ()=>{
15192
15329
  const val=td.textContent;
@@ -15412,6 +15549,7 @@ function pageShell(title, inner, opts) {
15412
15549
  header.mh h1.title{font-size:22px;margin:0}
15413
15550
  .edit-btn{background:var(--accent);color:var(--accent-fg);border:0;border-radius:7px;height:32px;padding:0 14px;cursor:pointer;font-size:13px;font-weight:500;font-family:inherit}
15414
15551
  ${COPY_CSS}
15552
+ ${SHARE_TABLE_CSS}
15415
15553
  .hint{color:var(--muted);font-size:13px;margin:0 0 14px}
15416
15554
  article.doc h1,article.doc h2,article.doc h3{line-height:1.3;margin:1.4em 0 .5em}
15417
15555
  article.doc h1{font-size:1.7em} article.doc h2{font-size:1.4em} article.doc h3{font-size:1.2em}
@@ -15471,6 +15609,7 @@ var init_share_serve = __esm(() => {
15471
15609
  init_share_render();
15472
15610
  init_share_copy();
15473
15611
  init_page_messages();
15612
+ init_share_table();
15474
15613
  HTML = { "content-type": "text/html; charset=utf-8" };
15475
15614
  HASH_RE2 = /^[0-9a-f]{16,64}$/;
15476
15615
  SHARE_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
@@ -17073,9 +17212,30 @@ var styles_default = ` :root {
17073
17212
  .theme-card.sel .tc-ico { background:var(--selected-icon-bg); color:var(--selected-icon-fg); }
17074
17213
  .theme-card.sel .tc-check { opacity:1; transform:scale(1); }
17075
17214
  @media (max-width:560px) { .theme-grid { grid-template-columns:1fr; } }
17076
- .brand-grid { grid-template-columns:repeat(auto-fit,minmax(min(100%,120px),1fr)); }
17077
- .brand-preview { display:block; width:56px; height:56px; }
17078
- .brand-caption { margin:12px 0 0; color:var(--muted); font-size:12px; line-height:1.5; }
17215
+ .brand-appearance { container:brand-appearance / inline-size; border:1px solid var(--line); border-radius:var(--radius-lg); background:var(--bg); }
17216
+ .brand-controls { display:grid; grid-template-columns:64px minmax(0,1fr); align-items:center; gap:24px; padding:18px 24px; }
17217
+ .brand-preview { display:block; width:64px; height:64px; }
17218
+ .brand-options { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; min-width:0; padding-left:24px; border-left:1px solid var(--line); }
17219
+ .brand-option-row { display:grid; align-content:center; gap:6px; min-width:0; }
17220
+ .brand-option-label { color:var(--fg-soft); font-size:var(--fs-ui); }
17221
+ .brand-segments { display:flex; gap:2px; padding:3px; border-radius:var(--radius); background:var(--surface-2); }
17222
+ .brand-segment { position:relative; flex:1; min-width:0; text-align:center; }
17223
+ .brand-segment input { position:absolute; inset:0; width:100%; height:100%; opacity:0; margin:0; cursor:pointer; }
17224
+ .brand-segment > span { display:flex; align-items:center; justify-content:center; min-height:32px; padding:4px 12px;
17225
+ border-radius:var(--radius-sm); color:var(--muted); font-size:var(--fs-ui); pointer-events:none;
17226
+ transition:background .14s,color .14s; }
17227
+ .brand-segment:hover > span { color:var(--fg); }
17228
+ .brand-segment input:checked + span { background:var(--bg); color:var(--accent); font-weight:600; }
17229
+ .brand-segment input:focus-visible + span { outline:2px solid var(--accent); outline-offset:2px; }
17230
+ @container brand-appearance (max-width:480px) {
17231
+ .brand-controls { grid-template-columns:48px minmax(0,1fr); gap:16px; padding:16px; }
17232
+ .brand-preview { width:48px; height:48px; }
17233
+ .brand-options { grid-template-columns:minmax(0,1fr); gap:10px; padding-left:16px; }
17234
+ .brand-option-row { gap:4px; }
17235
+ .brand-option-label { font-size:var(--fs-sm); }
17236
+ .brand-segment > span { padding:4px 8px; }
17237
+ }
17238
+ @media (pointer:coarse) { .brand-segment > span { min-height:44px; } }
17079
17239
  .theme-card:disabled { opacity:.55; cursor:not-allowed; }
17080
17240
  .theme-card:disabled:hover { transform:none; border-color:var(--line-strong); }
17081
17241
 
@@ -17823,10 +17983,9 @@ var styles_default = ` :root {
17823
17983
  .proprow-v .x:disabled:hover { background:none; color:var(--muted); }
17824
17984
  .tablewrap { border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
17825
17985
  .tablescroll { overflow-x:auto; }
17826
- /* fixed layout + max-content: each <col> width is authoritative, so the table
17827
- is exactly the sum of its columns and resizing one column never redistributes
17828
- width to its neighbours. min-width:100% fills the container when columns are
17829
- narrow, with the slack absorbed only by the trailing auto (filler) column. */
17986
+ /* The grid supplies a definite width (sum of its columns): max-content alone
17987
+ lets intrinsic long text override fixed layout in some engines. min-width
17988
+ fills the container, with slack absorbed by the trailing filler column. */
17830
17989
  table.grid { border-collapse:collapse; table-layout:fixed; width:max-content; min-width:100%; }
17831
17990
  table.grid th, table.grid td { border-right:1px solid var(--line); border-bottom:1px solid var(--line); padding:0; text-align:left; }
17832
17991
  table.grid th:last-child, table.grid td:last-child { border-right:0; }
@@ -17836,6 +17995,7 @@ var styles_default = ` :root {
17836
17995
  .colhead:hover { background:var(--hover); color:var(--fg-soft); }
17837
17996
  .colhead .ti { color:var(--muted); display:flex; } .colhead .ti svg { width:14px; height:14px; }
17838
17997
  .colhead .nm { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
17998
+ .colhead, .colhead .nm { min-width:0; }
17839
17999
  .colhead .sortmark { width:12px; height:12px; color:var(--accent); flex:none; }
17840
18000
  .colhead .caret { width:12px; height:12px; color:var(--muted); flex:none; opacity:0; transition:opacity .12s; }
17841
18001
  .colhead:hover .caret { opacity:1; }
@@ -17856,9 +18016,9 @@ var styles_default = ` :root {
17856
18016
  table.grid td.filler { border-right:0; border-bottom:0; background:transparent; }
17857
18017
  body.col-resizing { user-select:none; cursor:col-resize; }
17858
18018
  table.grid .selcell { width:38px; min-width:38px; max-width:38px; text-align:center; }
17859
- /* relative: anchors the .celledit overlay editor to the cell */
18019
+ /* relative: anchors preview actions and the selection fill handle */
17860
18020
  td.cell-td { position:relative; vertical-align:top; user-select:none; }
17861
- td.cell-td .celledit, td.cell-td .celledit * { user-select:text; }
18021
+ .celledit, .celledit * { user-select:text; }
17862
18022
  .cell { padding:8px 11px; min-height:37px; cursor:text; word-break:break-word; display:flex; flex-wrap:wrap; gap:3px; align-items:center; }
17863
18023
  /* hover/active tint goes on the <td> (always full row height) not the inner
17864
18024
  .cell (only as tall as its content), so a taller sibling row never leaves a
@@ -17942,18 +18102,36 @@ var styles_default = ` :root {
17942
18102
  .inlineedit { width:100%; border:0; outline:2px solid var(--accent); border-radius:0; padding:7px 9px; background:var(--bg); color:var(--fg); font:inherit; }
17943
18103
  textarea.inlineedit { resize:none; overflow:hidden; line-height:1.45; display:block; }
17944
18104
  .inlineedit.num { text-align:right; font-variant-numeric:tabular-nums; }
17945
- /* Overlay cell editor: floats over the td; the display content underneath
17946
- stays in flow so the row height never changes while editing. Padding on the
18105
+ /* Overlay cell editor: portaled to body to escape the table scrollport.
18106
+ The display stays in flow so the row height never changes. Padding on the
17947
18107
  input matches .cell (8px 11px) so the text doesn't shift on entering edit;
17948
18108
  a multi-line text editor grows downward over the rows below. */
17949
- .celledit { position:absolute; top:0; left:0; width:100%; min-height:100%; z-index:10;
18109
+ .celledit { position:fixed; z-index:60;
17950
18110
  display:flex; align-items:stretch; background:var(--bg); border-radius:0;
17951
18111
  box-shadow:inset 0 0 0 2px var(--accent), var(--shadow-md); }
17952
18112
  .celledit .inlineedit { width:100%; min-width:0; border:0; outline:0; border-radius:inherit;
17953
18113
  padding:8px 11px; background:transparent; }
17954
- .celledit textarea.inlineedit { min-height:37px; }
18114
+ .celledit textarea.inlineedit { min-height:0; max-height:min(320px, 40vh, var(--cell-editor-max-height)); }
17955
18115
  .cell.num { justify-content:flex-end; font-variant-numeric:tabular-nums; }
17956
18116
  .cell .txt { white-space:pre-wrap; }
18117
+ /* Grid text is a preview; full text belongs in the record drawer. Never
18118
+ truncate stored values or let a long token determine the column width. */
18119
+ .cell-preview { width:100%; min-width:0; }
18120
+ .cell .cell-preview-text { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3;
18121
+ overflow:hidden; overflow-wrap:anywhere; line-height:1.55; max-height:4.65em; }
18122
+ table.grid.nowrap .cell .cell-preview-text { display:block; -webkit-line-clamp:unset; max-height:1.55em; }
18123
+ /* Overlay the action so revealing it never changes row height. Opacity keeps
18124
+ it keyboard reachable; focus-within reveals it before activation. */
18125
+ .cell-read-more { position:absolute; top:6px; right:6px; z-index:2; display:flex; align-items:center; justify-content:center;
18126
+ width:24px; height:24px; padding:0; border:1px solid var(--line); border-radius:var(--radius-sm);
18127
+ background:var(--bg); color:var(--fg-soft); cursor:pointer; opacity:0; pointer-events:none; }
18128
+ td.cell-td:is(:hover, .cellsel, :focus-within) .cell-read-more { opacity:1; pointer-events:auto; }
18129
+ .firstcell:has(.cell-read-more) .rowactions { display:none; }
18130
+ .cell-read-more svg { width:14px; height:14px; flex:none; }
18131
+ .cell-read-more:hover { background:var(--surface-2); color:var(--fg); border-color:var(--line-strong); }
18132
+ .cell-read-more:focus-visible, .peek-copy:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
18133
+ textarea.inlineedit { max-height:min(320px, 40vh); overflow-y:auto; overflow-wrap:anywhere; }
18134
+ table.grid .chip { max-width:100%; overflow:hidden; text-overflow:ellipsis; }
17957
18135
  .cell-url { display:inline-flex; align-items:center; gap:4px; max-width:100%; min-width:0; color:var(--accent); text-decoration:none; }
17958
18136
  .cell-url span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
17959
18137
  .cell-url svg { flex:none; width:12px; height:12px; opacity:0; transition:opacity .1s; }
@@ -18395,6 +18573,33 @@ var styles_default = ` :root {
18395
18573
  .peek .proprow .k { cursor:pointer; }
18396
18574
  .proprow .v { flex:1; padding:7px 8px; border-radius:var(--radius-sm); cursor:text; min-height:33px; display:flex; flex-wrap:wrap; gap:3px; align-items:center; }
18397
18575
  .proprow .v:hover { background:var(--hover); }
18576
+ .peek h2, .proprow .v { min-width:0; overflow-wrap:anywhere; }
18577
+ .proprow .v .txt { white-space:pre-wrap; overflow-wrap:anywhere; max-width:100%; user-select:text; }
18578
+ .proprow .k { min-width:0; }
18579
+ .proprow .k > svg { flex:none; }
18580
+ .proprow .k > span { min-width:0; overflow-wrap:anywhere; }
18581
+ .proprow-long { flex-direction:column; gap:0; margin-block:18px; scroll-margin-top:8px; }
18582
+ .proprow-long .k { width:100%; }
18583
+ .proprow-long .v { width:100%; line-height:1.7; }
18584
+ .proprow:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:var(--radius-sm); }
18585
+ .peek-copy { display:inline-flex; align-items:center; justify-content:center; flex:none; width:24px; height:24px;
18586
+ margin-inline-start:auto; border-radius:var(--radius-sm); color:var(--muted); cursor:pointer; }
18587
+ .peek-copy:hover { background:var(--hover-2); color:var(--fg); }
18588
+ @media (max-width:560px) {
18589
+ .peek-body { padding:20px 16px; }
18590
+ .peek .proprow { flex-direction:column; gap:0; margin-block:12px; }
18591
+ .peek .proprow .k, .peek .proprow .v { width:100%; }
18592
+ .peek .proprow .v { font-size:16px; }
18593
+ .peek textarea.inlineedit { font-size:16px; }
18594
+ }
18595
+ @media (pointer:coarse) {
18596
+ .cell:has(.cell-read-more) { min-height:44px; }
18597
+ .cell-read-more { width:44px; height:44px; top:0; right:0; }
18598
+ .peek-copy { width:44px; height:44px; }
18599
+ }
18600
+ @media (hover:none), (pointer:coarse) {
18601
+ .cell-read-more { opacity:1; pointer-events:auto; }
18602
+ }
18398
18603
  .peek-divider { height:1px; background:var(--line); margin:22px 0 18px; }
18399
18604
  .peek-hidden-toggle { display:flex; align-items:center; gap:6px; padding:7px 8px; margin:2px 0; border-radius:var(--radius-sm); color:var(--muted); font-size:13px; width:100%; text-align:left; }
18400
18605
  .peek-hidden-toggle:hover { background:var(--hover); color:var(--fg-soft); }
@@ -18702,14 +18907,22 @@ var styles_default = ` :root {
18702
18907
  .tmpl .tt { font-weight:600; font-size:var(--fs-ui); } .tmpl .td { font-size:var(--fs-xs); color:var(--muted); margin-top:2px; }
18703
18908
 
18704
18909
  /* toast */
18705
- .toasts { position:fixed; bottom:20px; left:20px; z-index:110; display:flex; flex-direction:column; gap:8px; }
18706
- .toast { background:var(--fg); color:var(--bg); padding:10px 14px; border-radius:9px; font-size:13px; box-shadow:var(--shadow-lg);
18910
+ .toasts { position:fixed; bottom:20px; left:20px; max-width:calc(100vw - 40px); z-index:110; display:flex; flex-direction:column; gap:8px; }
18911
+ .toast { position:relative; box-sizing:border-box; touch-action:pan-y; user-select:none; transform:translateX(var(--toast-offset, 0px)); transition:transform .18s ease-out, opacity .16s ease; background:var(--fg); color:var(--bg); padding:10px 14px; border-radius:9px; font-size:13px; box-shadow:var(--shadow-lg);
18707
18912
  display:flex; align-items:center; gap:8px; animation:slidein .2s ease; }
18708
18913
  @keyframes slidein { from { opacity:0; transform:translateX(-12px); } }
18709
18914
  .toast.error { background:var(--danger); color:var(--danger-fg); }
18710
18915
  .toast.leaving { animation:toast-out .16s ease-in forwards; pointer-events:none; }
18711
18916
  @keyframes toast-out { to { opacity:0; transform:translateX(-8px); } }
18712
- .toast-msg { min-width:0; }
18917
+ .toast-msg { min-width:0; overflow-wrap:anywhere; }
18918
+ .toast > .ico, .toast-act { flex-shrink:0; }
18919
+ .toast.dragging { animation:none; transition:none; cursor:grabbing; }
18920
+ .toast.leaving.swipe-out { animation:none; transition:transform .16s ease-in, opacity .16s ease-in; }
18921
+ .toast-act:focus-visible { outline:2px solid currentColor; outline-offset:2px; }
18922
+ @media (prefers-reduced-motion:reduce) {
18923
+ .toast, .toast.dragging, .toast.leaving, .toast.leaving.swipe-out { animation:none; transition:none; transform:none; }
18924
+ .toast.leaving { opacity:0; }
18925
+ }
18713
18926
  .toast-act { margin-left:4px; padding:3px 9px; border-radius:6px; font-weight:600; color:inherit; background:color-mix(in srgb, var(--bg) 16%, transparent); white-space:nowrap; }
18714
18927
  .toast-act:hover { background:color-mix(in srgb, var(--bg) 28%, transparent); }
18715
18928
 
@@ -25344,4 +25557,4 @@ export {
25344
25557
  writeTx
25345
25558
  };
25346
25559
 
25347
- //# debugId=39484D6BDF841CDA64756E2164756E21
25560
+ //# debugId=049F5CBBC74592BC64756E2164756E21