@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.
package/dist/cli.js CHANGED
@@ -801,7 +801,7 @@ var package_default;
801
801
  var init_package = __esm(() => {
802
802
  package_default = {
803
803
  name: "@tensorix/metahub",
804
- version: "0.5.6",
804
+ version: "0.5.8",
805
805
  description: "Metahub core library and CLI",
806
806
  license: "AGPL-3.0-only",
807
807
  type: "module",
@@ -7742,7 +7742,7 @@ var init_storage_s3_sign = __esm(() => {
7742
7742
 
7743
7743
  // src/core/sync/storage-s3-bun.ts
7744
7744
  var {S3Client } = globalThis.Bun;
7745
- function makeClient(config) {
7745
+ function makeBunStorageClient(config) {
7746
7746
  const vhost = isVirtualHostedStyle(config);
7747
7747
  const s3 = new S3Client({
7748
7748
  accessKeyId: config.accessKeyId,
@@ -7786,7 +7786,7 @@ function makeClient(config) {
7786
7786
  if (opts?.ifMatch) {
7787
7787
  const res = await aws().fetch(objectUrl(key), {
7788
7788
  method: "PUT",
7789
- body,
7789
+ body: new Uint8Array(body).buffer,
7790
7790
  headers: {
7791
7791
  "if-match": opts.ifMatch,
7792
7792
  ...opts.contentType ? { "content-type": opts.contentType } : {}
@@ -7828,7 +7828,7 @@ var init_storage_s3_bun = __esm(() => {
7828
7828
  init_errors();
7829
7829
  init_storage();
7830
7830
  init_storage_s3_sign();
7831
- setStorageClientFactory(makeClient);
7831
+ setStorageClientFactory(makeBunStorageClient);
7832
7832
  setBucketCorsAdmin(putBucketCors);
7833
7833
  });
7834
7834
 
@@ -13999,6 +13999,10 @@ var init_page_messages = __esm(() => {
13999
13999
  "\uFF08\u7A7A\u6587\u6863\uFF09": "(empty document)",
14000
14000
  \u{6587}\u{6863}: "Document",
14001
14001
  \u{8868}\u{683c}: "Table",
14002
+ \u{8bb0}\u{5f55}: "Record",
14003
+ \u{5173}\u{95ed}: "Close",
14004
+ \u{67e5}\u{770b}\u{5168}\u{6587}: "Read full text",
14005
+ "\u67E5\u770B\u300C{name}\u300D\u5168\u6587": "Read full {name}",
14002
14006
  \u{7f16}\u{8f91}: "Edit",
14003
14007
  \u{7f16}\u{8f91}\u{6587}\u{6863}: "Edit document",
14004
14008
  \u{4fdd}\u{5b58}: "Save",
@@ -16511,7 +16515,7 @@ function copyScript(locale = "zh-CN") {
16511
16515
  var btn=document.getElementById('mh-copy');if(!btn)return;var L=${L};
16512
16516
  var label=btn.querySelector('span');var timer=0;
16513
16517
  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);});}
16514
- 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};}
16518
+ 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};}
16515
16519
  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'));}
16516
16520
  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);}
16517
16521
  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);}
@@ -16535,6 +16539,140 @@ var init_share_copy = __esm(() => {
16535
16539
  init_page_messages();
16536
16540
  });
16537
16541
 
16542
+ // src/core/sync/share-table-client.js
16543
+ function mountShareTable(labels) {
16544
+ const table = document.querySelector("table.share-table");
16545
+ const peek = document.querySelector(".share-text-peek");
16546
+ if (!table || !peek || typeof peek.showModal !== "function")
16547
+ return;
16548
+ const heading = peek.querySelector("h2");
16549
+ const record = peek.querySelector(".share-text-record");
16550
+ const body = peek.querySelector(".share-text-body");
16551
+ const feedback = peek.querySelector(".share-text-feedback");
16552
+ let restore = null;
16553
+ let selected = null;
16554
+ const select = (cell) => {
16555
+ selected?.classList.remove("is-selected");
16556
+ selected = cell;
16557
+ cell.classList.add("is-selected");
16558
+ };
16559
+ const measure = (value) => {
16560
+ const button = value.parentElement.querySelector(".share-cell-expand");
16561
+ button.hidden = !(value.scrollHeight > value.clientHeight + 1 || value.scrollWidth > value.clientWidth + 1);
16562
+ };
16563
+ table.dataset.ready = "";
16564
+ const values = Array.from(table.querySelectorAll(".share-cell-value"));
16565
+ const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver((entries) => {
16566
+ for (const entry of entries)
16567
+ measure(entry.target);
16568
+ }) : null;
16569
+ for (const value of values) {
16570
+ measure(value);
16571
+ observer?.observe(value);
16572
+ value.addEventListener("input", () => measure(value));
16573
+ value.addEventListener("blur", () => requestAnimationFrame(() => measure(value)));
16574
+ }
16575
+ document.fonts?.ready.then(() => values.forEach(measure));
16576
+ table.addEventListener("click", (event) => {
16577
+ const target = event.target;
16578
+ const cell = target.closest("td");
16579
+ if (!cell)
16580
+ return;
16581
+ select(cell);
16582
+ const button = target.closest(".share-cell-expand");
16583
+ if (!button)
16584
+ return;
16585
+ const column = Array.from(cell.parentElement.children).indexOf(cell);
16586
+ heading.textContent = table.querySelectorAll("thead th")[column].textContent;
16587
+ record.textContent = cell.parentElement.querySelector(".share-cell-value")?.textContent || labels.record;
16588
+ body.textContent = cell.querySelector(".share-cell-value").textContent;
16589
+ feedback.textContent = "";
16590
+ restore = button;
16591
+ peek.showModal();
16592
+ body.scrollTop = 0;
16593
+ });
16594
+ peek.querySelector(".share-text-close").addEventListener("click", () => peek.close());
16595
+ peek.addEventListener("click", (event) => {
16596
+ const rect = peek.getBoundingClientRect();
16597
+ if (event.target === peek && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom))
16598
+ peek.close();
16599
+ });
16600
+ peek.addEventListener("close", () => restore?.focus({ preventScroll: true }));
16601
+ peek.querySelector(".share-text-copy").addEventListener("click", async () => {
16602
+ const text = body.textContent ?? "";
16603
+ try {
16604
+ await navigator.clipboard.writeText(text);
16605
+ feedback.textContent = labels.copied;
16606
+ } catch {
16607
+ const input = document.createElement("textarea");
16608
+ input.value = text;
16609
+ input.readOnly = true;
16610
+ input.style.cssText = "position:fixed;opacity:0;top:0;left:0";
16611
+ peek.append(input);
16612
+ input.select();
16613
+ try {
16614
+ feedback.textContent = document.execCommand("copy") ? labels.copied : labels.failed;
16615
+ } catch {
16616
+ feedback.textContent = labels.failed;
16617
+ }
16618
+ input.remove();
16619
+ peek.querySelector(".share-text-copy").focus();
16620
+ }
16621
+ });
16622
+ }
16623
+
16624
+ // src/core/sync/share-table.ts
16625
+ function shareCellHtml(html, name, locale, editAttrs = "") {
16626
+ 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>`;
16627
+ }
16628
+ function shareTableHtml(columns, rows, locale) {
16629
+ const widths = columns.map((p) => typeof p.width === "number" && Number.isFinite(p.width) ? Math.max(80, Math.min(640, p.width)) : 180);
16630
+ 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>
16631
+ <dialog class="share-text-peek" aria-labelledby="share-text-heading">
16632
+ <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>
16633
+ <div class="share-text-heading"><h2 id="share-text-heading"></h2><p class="share-text-record"></p></div>
16634
+ <div class="share-text-body" tabindex="0"></div>
16635
+ </dialog>`;
16636
+ }
16637
+ function shareTableScript(locale) {
16638
+ return `(${mountShareTable.toString()})(${JSON.stringify({ copied: pm(locale, "\u5DF2\u590D\u5236"), failed: pm(locale, "\u590D\u5236\u5931\u8D25"), record: pm(locale, "\u8BB0\u5F55") })});`;
16639
+ }
16640
+ 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 = `
16641
+ #app:has(.share-table),.wrap:has(.share-table){max-width:1440px}
16642
+ .table-wrap:has(.share-table){max-width:100%;overflow:auto;overscroll-behavior-x:contain}
16643
+ table.share-table{table-layout:fixed;min-width:100%}
16644
+ table.share-table th{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500;color:var(--muted)}
16645
+ table.share-table td{position:relative;vertical-align:top}
16646
+ .share-cell-value{min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.55}
16647
+ .share-table[data-ready] .share-cell-value{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;max-height:4.65em}
16648
+ .share-table .tag{max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;white-space:nowrap}
16649
+ .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}
16650
+ .share-cell-expand[hidden]{display:none}
16651
+ .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}
16652
+ .share-table td:is(:hover,:focus-within,.is-selected) .share-cell-expand{opacity:1;pointer-events:auto}
16653
+ .share-table td.is-selected{box-shadow:inset 0 0 0 1.5px var(--accent);background:var(--accent-soft)}
16654
+ .share-cell-expand:hover,.share-text-close:hover{background:var(--card)}
16655
+ .share-cell-expand:focus-visible,.share-text-close:focus-visible,.share-text-body:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
16656
+ .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}
16657
+ .share-cell-value.saving{opacity:.5}.share-cell-value.saved{background:color-mix(in srgb,var(--success) 18%,transparent)}
16658
+ .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}
16659
+ .share-text-peek[open]{display:flex;flex-direction:column}
16660
+ .share-text-peek::backdrop{background:rgba(15,15,25,.3)}
16661
+ body:has(.share-text-peek[open]){overflow:hidden}
16662
+ .share-text-toolbar{display:flex;align-items:center;gap:12px;flex:none;padding:12px 20px;border-bottom:1px solid var(--line)}
16663
+ .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}
16664
+ .share-text-feedback{font-size:13px;color:var(--muted)}.share-text-feedback:empty{display:none}
16665
+ .share-text-heading{flex:none;padding:24px 32px 16px;min-width:0}
16666
+ .share-text-heading h2{margin:0;font-size:20px;line-height:1.4;overflow-wrap:anywhere}
16667
+ .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}
16668
+ .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}
16669
+ @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}}
16670
+ @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}}
16671
+ `;
16672
+ var init_share_table = __esm(() => {
16673
+ init_page_messages();
16674
+ });
16675
+
16538
16676
  // src/core/sync/share-serve.ts
16539
16677
  function notFound() {
16540
16678
  return new Response("not found", { status: 404 });
@@ -16701,19 +16839,18 @@ async function serveTable(ctx, req, share, sub, locale) {
16701
16839
  }
16702
16840
  const titlesFor = (p) => p.type === "relation" ? relTitles.get(p.config?.database ?? "") : undefined;
16703
16841
  const editable = share.permission === "edit";
16704
- const head = props.map((p) => `<th>${escapeHtml(p.name)}</th>`).join("");
16705
16842
  const rows = records.map((r) => {
16706
16843
  const tds = props.map((p) => {
16707
16844
  const v = r.cells[p.id];
16708
16845
  if (editable && editableType(p.type)) {
16709
- return `<td contenteditable data-rec="${escapeHtml(r.id)}" data-prop="${escapeHtml(p.id)}" data-type="${p.type}">${escapeHtml(cellText(p, v))}</td>`;
16846
+ 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}"`);
16710
16847
  }
16711
- return `<td>${cellHtml(p, v, titlesFor(p))}</td>`;
16848
+ return shareCellHtml(cellHtml(p, v, titlesFor(p)), p.name, locale);
16712
16849
  }).join("");
16713
16850
  return `<tr>${tds}</tr>`;
16714
16851
  }).join("");
16715
- const inner = `<div class="table-wrap"><table class="db"><thead><tr>${head}</tr></thead><tbody>${rows}</tbody></table></div>`;
16716
- const script = editable ? tableEditScript(locale, share.slug) : "";
16852
+ const inner = shareTableHtml(props.map((p) => ({ name: p.name, width: p.config?.width })), rows, locale);
16853
+ const script = shareTableScript(locale) + (editable ? tableEditScript(locale, share.slug) : "");
16717
16854
  const copySource = markdownTable(props.map((p) => p.name), records.map((r) => props.map((p) => cellText(p, r.cells[p.id], titlesFor(p)))));
16718
16855
  return new Response(pageShell(dbRow.name || pm(locale, "\u8868\u683C"), inner, {
16719
16856
  lang: locale,
@@ -16725,7 +16862,7 @@ async function serveTable(ctx, req, share, sub, locale) {
16725
16862
  function tableEditScript(locale, slug) {
16726
16863
  return `
16727
16864
  const L=${JSON.stringify({ failed: pm(locale, "\u4FDD\u5B58\u5931\u8D25") })};
16728
- document.querySelectorAll('td[contenteditable]').forEach(td=>{
16865
+ document.querySelectorAll('.share-cell-value[contenteditable]').forEach(td=>{
16729
16866
  let orig=td.textContent;
16730
16867
  td.addEventListener('blur', async ()=>{
16731
16868
  const val=td.textContent;
@@ -16951,6 +17088,7 @@ function pageShell(title, inner, opts) {
16951
17088
  header.mh h1.title{font-size:22px;margin:0}
16952
17089
  .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}
16953
17090
  ${COPY_CSS}
17091
+ ${SHARE_TABLE_CSS}
16954
17092
  .hint{color:var(--muted);font-size:13px;margin:0 0 14px}
16955
17093
  article.doc h1,article.doc h2,article.doc h3{line-height:1.3;margin:1.4em 0 .5em}
16956
17094
  article.doc h1{font-size:1.7em} article.doc h2{font-size:1.4em} article.doc h3{font-size:1.2em}
@@ -17010,6 +17148,7 @@ var init_share_serve = __esm(() => {
17010
17148
  init_share_render();
17011
17149
  init_share_copy();
17012
17150
  init_page_messages();
17151
+ init_share_table();
17013
17152
  HTML = { "content-type": "text/html; charset=utf-8" };
17014
17153
  HASH_RE3 = /^[0-9a-f]{16,64}$/;
17015
17154
  SHARE_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
@@ -28799,9 +28938,30 @@ var styles_default = ` :root {
28799
28938
  .theme-card.sel .tc-ico { background:var(--selected-icon-bg); color:var(--selected-icon-fg); }
28800
28939
  .theme-card.sel .tc-check { opacity:1; transform:scale(1); }
28801
28940
  @media (max-width:560px) { .theme-grid { grid-template-columns:1fr; } }
28802
- .brand-grid { grid-template-columns:repeat(auto-fit,minmax(min(100%,120px),1fr)); }
28803
- .brand-preview { display:block; width:56px; height:56px; }
28804
- .brand-caption { margin:12px 0 0; color:var(--muted); font-size:12px; line-height:1.5; }
28941
+ .brand-appearance { container:brand-appearance / inline-size; border:1px solid var(--line); border-radius:var(--radius-lg); background:var(--bg); }
28942
+ .brand-controls { display:grid; grid-template-columns:64px minmax(0,1fr); align-items:center; gap:24px; padding:18px 24px; }
28943
+ .brand-preview { display:block; width:64px; height:64px; }
28944
+ .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); }
28945
+ .brand-option-row { display:grid; align-content:center; gap:6px; min-width:0; }
28946
+ .brand-option-label { color:var(--fg-soft); font-size:var(--fs-ui); }
28947
+ .brand-segments { display:flex; gap:2px; padding:3px; border-radius:var(--radius); background:var(--surface-2); }
28948
+ .brand-segment { position:relative; flex:1; min-width:0; text-align:center; }
28949
+ .brand-segment input { position:absolute; inset:0; width:100%; height:100%; opacity:0; margin:0; cursor:pointer; }
28950
+ .brand-segment > span { display:flex; align-items:center; justify-content:center; min-height:32px; padding:4px 12px;
28951
+ border-radius:var(--radius-sm); color:var(--muted); font-size:var(--fs-ui); pointer-events:none;
28952
+ transition:background .14s,color .14s; }
28953
+ .brand-segment:hover > span { color:var(--fg); }
28954
+ .brand-segment input:checked + span { background:var(--bg); color:var(--accent); font-weight:600; }
28955
+ .brand-segment input:focus-visible + span { outline:2px solid var(--accent); outline-offset:2px; }
28956
+ @container brand-appearance (max-width:480px) {
28957
+ .brand-controls { grid-template-columns:48px minmax(0,1fr); gap:16px; padding:16px; }
28958
+ .brand-preview { width:48px; height:48px; }
28959
+ .brand-options { grid-template-columns:minmax(0,1fr); gap:10px; padding-left:16px; }
28960
+ .brand-option-row { gap:4px; }
28961
+ .brand-option-label { font-size:var(--fs-sm); }
28962
+ .brand-segment > span { padding:4px 8px; }
28963
+ }
28964
+ @media (pointer:coarse) { .brand-segment > span { min-height:44px; } }
28805
28965
  .theme-card:disabled { opacity:.55; cursor:not-allowed; }
28806
28966
  .theme-card:disabled:hover { transform:none; border-color:var(--line-strong); }
28807
28967
 
@@ -29549,10 +29709,9 @@ var styles_default = ` :root {
29549
29709
  .proprow-v .x:disabled:hover { background:none; color:var(--muted); }
29550
29710
  .tablewrap { border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
29551
29711
  .tablescroll { overflow-x:auto; }
29552
- /* fixed layout + max-content: each <col> width is authoritative, so the table
29553
- is exactly the sum of its columns and resizing one column never redistributes
29554
- width to its neighbours. min-width:100% fills the container when columns are
29555
- narrow, with the slack absorbed only by the trailing auto (filler) column. */
29712
+ /* The grid supplies a definite width (sum of its columns): max-content alone
29713
+ lets intrinsic long text override fixed layout in some engines. min-width
29714
+ fills the container, with slack absorbed by the trailing filler column. */
29556
29715
  table.grid { border-collapse:collapse; table-layout:fixed; width:max-content; min-width:100%; }
29557
29716
  table.grid th, table.grid td { border-right:1px solid var(--line); border-bottom:1px solid var(--line); padding:0; text-align:left; }
29558
29717
  table.grid th:last-child, table.grid td:last-child { border-right:0; }
@@ -29562,6 +29721,7 @@ var styles_default = ` :root {
29562
29721
  .colhead:hover { background:var(--hover); color:var(--fg-soft); }
29563
29722
  .colhead .ti { color:var(--muted); display:flex; } .colhead .ti svg { width:14px; height:14px; }
29564
29723
  .colhead .nm { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
29724
+ .colhead, .colhead .nm { min-width:0; }
29565
29725
  .colhead .sortmark { width:12px; height:12px; color:var(--accent); flex:none; }
29566
29726
  .colhead .caret { width:12px; height:12px; color:var(--muted); flex:none; opacity:0; transition:opacity .12s; }
29567
29727
  .colhead:hover .caret { opacity:1; }
@@ -29582,9 +29742,9 @@ var styles_default = ` :root {
29582
29742
  table.grid td.filler { border-right:0; border-bottom:0; background:transparent; }
29583
29743
  body.col-resizing { user-select:none; cursor:col-resize; }
29584
29744
  table.grid .selcell { width:38px; min-width:38px; max-width:38px; text-align:center; }
29585
- /* relative: anchors the .celledit overlay editor to the cell */
29745
+ /* relative: anchors preview actions and the selection fill handle */
29586
29746
  td.cell-td { position:relative; vertical-align:top; user-select:none; }
29587
- td.cell-td .celledit, td.cell-td .celledit * { user-select:text; }
29747
+ .celledit, .celledit * { user-select:text; }
29588
29748
  .cell { padding:8px 11px; min-height:37px; cursor:text; word-break:break-word; display:flex; flex-wrap:wrap; gap:3px; align-items:center; }
29589
29749
  /* hover/active tint goes on the <td> (always full row height) not the inner
29590
29750
  .cell (only as tall as its content), so a taller sibling row never leaves a
@@ -29668,18 +29828,36 @@ var styles_default = ` :root {
29668
29828
  .inlineedit { width:100%; border:0; outline:2px solid var(--accent); border-radius:0; padding:7px 9px; background:var(--bg); color:var(--fg); font:inherit; }
29669
29829
  textarea.inlineedit { resize:none; overflow:hidden; line-height:1.45; display:block; }
29670
29830
  .inlineedit.num { text-align:right; font-variant-numeric:tabular-nums; }
29671
- /* Overlay cell editor: floats over the td; the display content underneath
29672
- stays in flow so the row height never changes while editing. Padding on the
29831
+ /* Overlay cell editor: portaled to body to escape the table scrollport.
29832
+ The display stays in flow so the row height never changes. Padding on the
29673
29833
  input matches .cell (8px 11px) so the text doesn't shift on entering edit;
29674
29834
  a multi-line text editor grows downward over the rows below. */
29675
- .celledit { position:absolute; top:0; left:0; width:100%; min-height:100%; z-index:10;
29835
+ .celledit { position:fixed; z-index:60;
29676
29836
  display:flex; align-items:stretch; background:var(--bg); border-radius:0;
29677
29837
  box-shadow:inset 0 0 0 2px var(--accent), var(--shadow-md); }
29678
29838
  .celledit .inlineedit { width:100%; min-width:0; border:0; outline:0; border-radius:inherit;
29679
29839
  padding:8px 11px; background:transparent; }
29680
- .celledit textarea.inlineedit { min-height:37px; }
29840
+ .celledit textarea.inlineedit { min-height:0; max-height:min(320px, 40vh, var(--cell-editor-max-height)); }
29681
29841
  .cell.num { justify-content:flex-end; font-variant-numeric:tabular-nums; }
29682
29842
  .cell .txt { white-space:pre-wrap; }
29843
+ /* Grid text is a preview; full text belongs in the record drawer. Never
29844
+ truncate stored values or let a long token determine the column width. */
29845
+ .cell-preview { width:100%; min-width:0; }
29846
+ .cell .cell-preview-text { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3;
29847
+ overflow:hidden; overflow-wrap:anywhere; line-height:1.55; max-height:4.65em; }
29848
+ table.grid.nowrap .cell .cell-preview-text { display:block; -webkit-line-clamp:unset; max-height:1.55em; }
29849
+ /* Overlay the action so revealing it never changes row height. Opacity keeps
29850
+ it keyboard reachable; focus-within reveals it before activation. */
29851
+ .cell-read-more { position:absolute; top:6px; right:6px; z-index:2; display:flex; align-items:center; justify-content:center;
29852
+ width:24px; height:24px; padding:0; border:1px solid var(--line); border-radius:var(--radius-sm);
29853
+ background:var(--bg); color:var(--fg-soft); cursor:pointer; opacity:0; pointer-events:none; }
29854
+ td.cell-td:is(:hover, .cellsel, :focus-within) .cell-read-more { opacity:1; pointer-events:auto; }
29855
+ .firstcell:has(.cell-read-more) .rowactions { display:none; }
29856
+ .cell-read-more svg { width:14px; height:14px; flex:none; }
29857
+ .cell-read-more:hover { background:var(--surface-2); color:var(--fg); border-color:var(--line-strong); }
29858
+ .cell-read-more:focus-visible, .peek-copy:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
29859
+ textarea.inlineedit { max-height:min(320px, 40vh); overflow-y:auto; overflow-wrap:anywhere; }
29860
+ table.grid .chip { max-width:100%; overflow:hidden; text-overflow:ellipsis; }
29683
29861
  .cell-url { display:inline-flex; align-items:center; gap:4px; max-width:100%; min-width:0; color:var(--accent); text-decoration:none; }
29684
29862
  .cell-url span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
29685
29863
  .cell-url svg { flex:none; width:12px; height:12px; opacity:0; transition:opacity .1s; }
@@ -30121,6 +30299,33 @@ var styles_default = ` :root {
30121
30299
  .peek .proprow .k { cursor:pointer; }
30122
30300
  .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; }
30123
30301
  .proprow .v:hover { background:var(--hover); }
30302
+ .peek h2, .proprow .v { min-width:0; overflow-wrap:anywhere; }
30303
+ .proprow .v .txt { white-space:pre-wrap; overflow-wrap:anywhere; max-width:100%; user-select:text; }
30304
+ .proprow .k { min-width:0; }
30305
+ .proprow .k > svg { flex:none; }
30306
+ .proprow .k > span { min-width:0; overflow-wrap:anywhere; }
30307
+ .proprow-long { flex-direction:column; gap:0; margin-block:18px; scroll-margin-top:8px; }
30308
+ .proprow-long .k { width:100%; }
30309
+ .proprow-long .v { width:100%; line-height:1.7; }
30310
+ .proprow:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:var(--radius-sm); }
30311
+ .peek-copy { display:inline-flex; align-items:center; justify-content:center; flex:none; width:24px; height:24px;
30312
+ margin-inline-start:auto; border-radius:var(--radius-sm); color:var(--muted); cursor:pointer; }
30313
+ .peek-copy:hover { background:var(--hover-2); color:var(--fg); }
30314
+ @media (max-width:560px) {
30315
+ .peek-body { padding:20px 16px; }
30316
+ .peek .proprow { flex-direction:column; gap:0; margin-block:12px; }
30317
+ .peek .proprow .k, .peek .proprow .v { width:100%; }
30318
+ .peek .proprow .v { font-size:16px; }
30319
+ .peek textarea.inlineedit { font-size:16px; }
30320
+ }
30321
+ @media (pointer:coarse) {
30322
+ .cell:has(.cell-read-more) { min-height:44px; }
30323
+ .cell-read-more { width:44px; height:44px; top:0; right:0; }
30324
+ .peek-copy { width:44px; height:44px; }
30325
+ }
30326
+ @media (hover:none), (pointer:coarse) {
30327
+ .cell-read-more { opacity:1; pointer-events:auto; }
30328
+ }
30124
30329
  .peek-divider { height:1px; background:var(--line); margin:22px 0 18px; }
30125
30330
  .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; }
30126
30331
  .peek-hidden-toggle:hover { background:var(--hover); color:var(--fg-soft); }
@@ -30428,14 +30633,22 @@ var styles_default = ` :root {
30428
30633
  .tmpl .tt { font-weight:600; font-size:var(--fs-ui); } .tmpl .td { font-size:var(--fs-xs); color:var(--muted); margin-top:2px; }
30429
30634
 
30430
30635
  /* toast */
30431
- .toasts { position:fixed; bottom:20px; left:20px; z-index:110; display:flex; flex-direction:column; gap:8px; }
30432
- .toast { background:var(--fg); color:var(--bg); padding:10px 14px; border-radius:9px; font-size:13px; box-shadow:var(--shadow-lg);
30636
+ .toasts { position:fixed; bottom:20px; left:20px; max-width:calc(100vw - 40px); z-index:110; display:flex; flex-direction:column; gap:8px; }
30637
+ .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);
30433
30638
  display:flex; align-items:center; gap:8px; animation:slidein .2s ease; }
30434
30639
  @keyframes slidein { from { opacity:0; transform:translateX(-12px); } }
30435
30640
  .toast.error { background:var(--danger); color:var(--danger-fg); }
30436
30641
  .toast.leaving { animation:toast-out .16s ease-in forwards; pointer-events:none; }
30437
30642
  @keyframes toast-out { to { opacity:0; transform:translateX(-8px); } }
30438
- .toast-msg { min-width:0; }
30643
+ .toast-msg { min-width:0; overflow-wrap:anywhere; }
30644
+ .toast > .ico, .toast-act { flex-shrink:0; }
30645
+ .toast.dragging { animation:none; transition:none; cursor:grabbing; }
30646
+ .toast.leaving.swipe-out { animation:none; transition:transform .16s ease-in, opacity .16s ease-in; }
30647
+ .toast-act:focus-visible { outline:2px solid currentColor; outline-offset:2px; }
30648
+ @media (prefers-reduced-motion:reduce) {
30649
+ .toast, .toast.dragging, .toast.leaving, .toast.leaving.swipe-out { animation:none; transition:none; transform:none; }
30650
+ .toast.leaving { opacity:0; }
30651
+ }
30439
30652
  .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; }
30440
30653
  .toast-act:hover { background:color-mix(in srgb, var(--bg) 28%, transparent); }
30441
30654
 
@@ -34401,4 +34614,4 @@ if (scopedConfigHelp) {
34401
34614
  runMain(main, { showUsage: showUsage2 });
34402
34615
  }
34403
34616
 
34404
- //# debugId=06C8CBB4AE61535064756E2164756E21
34617
+ //# debugId=E215016A1551067264756E2164756E21