@north-light/cli 0.3.475 → 0.3.476
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/clients/attach/render/doc-links.js +1 -1
- package/dist/clients/attach/session/doc-html.d.ts +8 -0
- package/dist/clients/attach/session/doc-html.js +66 -0
- package/dist/clients/attach/session/doc-materialize.d.ts +4 -0
- package/dist/clients/attach/session/doc-materialize.js +3 -3
- package/dist/clients/attach/session/document-links.d.ts +3 -0
- package/dist/clients/attach/session/document-links.js +1 -1
- package/dist/clients/attach/viewer.js +616 -551
- package/dist/core/config.js +1 -1
- package/dist/core/user-settings.d.ts +15 -0
- package/dist/core/user-settings.js +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.js +1 -1
- package/package.json +3 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var $=Object.defineProperty;var r=(e,n)=>$(e,"name",{value:n,configurable:!0});import{pathToFileURL as f}from"node:url";const x="crtr-doc-open",l=/\[\[([^[\]\n]+?)\]\]/g,N=/^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+$/,h=r(e=>`\x1B[2m${e}\x1B[22m`,"DIM"),D=r(e=>`\x1B[4m${e}\x1B[24m`,"UNDERLINE");let d;function E(e){d=e}r(E,"setDocLinkContext");function b(e,n){const t=e.indexOf("/");if(t<=0)return{name:e};const o=e.slice(0,t);return o===n?.selfNodeId||n?.nodeName(o)!==void 0||N.test(o)?{nodeId:o,name:e.slice(t+1)}:{name:e}}r(b,"splitDocRef");function m(e,n=d){const{nodeId:t,name:o}=b(e,n);return t===void 0?{name:o}:t===n?.selfNodeId?{name:o}:{name:o,owner:n?.nodeName(t)??t.slice(t.lastIndexOf("-")+1)}}r(m,"docLinkLabel");function w(e,n=d){const t=m(e,n);return t.owner===void 0?t.name:`${t.name} \xB7 ${t.owner}`}r(w,"docLinkText");function O(e){return[...e.matchAll(l)].map(n=>n[1].trim()).filter(n=>n!=="")}r(O,"extractDocRefs");function R(e,n=d){return n===void 0||!e.includes("[[")?e:e.replace(l,(t,o)=>{const i=o.trim();if(i==="")return t;const s=m(i,n),c=n.pathFor(i),a=p(s.name),u=c===void 0?D(a):c.endsWith(".html")?`[${a}](${f(c).href})`:`[${a}](${f(c).href}#${x})`;return s.owner===void 0?u:`${u}${h(` \xB7 ${p(s.owner)}`)}`})}r(R,"rewriteProseDocLinks");function p(e){return e.replace(/([\\[\]*_`<>])/g,"\\$1")}r(p,"escapeLabel");export{x as DOC_OPEN_FRAGMENT,m as docLinkLabel,w as docLinkText,O as extractDocRefs,R as rewriteProseDocLinks,E as setDocLinkContext,b as splitDocRef};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
var n=Object.defineProperty;var o=(e,r)=>n(e,"name",{value:r,configurable:!0});import{Marked as i}from"marked";const l=/\[\[([^[\]\n]+?)\]\]/g;function a(e){return e.replace(/[&<>"']/g,r=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[r])}o(a,"escapeHtml");const p=new i({gfm:!0,renderer:{html({text:e}){return a(e)}}});function s(e){return e.split(/(<pre[\s\S]*?<\/pre>|<code>[\s\S]*?<\/code>)/).map((r,t)=>t%2===1?r:r.replace(l,(c,d)=>`<span class="doc-ref" title="${d.trim()}">${d.trim().split("/").pop()}</span>`)).join("")}o(s,"markDocRefs");function g(e){const r=e.name.split("/").pop()??e.name,t=s(p.parse(e.body,{async:!1}));return`<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>${a(r)}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: light dark; --fg: #1f2328; --muted: #656d76; --bg: #ffffff; --panel: #f6f8fa; --border: #d0d7de; --accent: #0969da; }
|
|
9
|
+
@media (prefers-color-scheme: dark) { :root { --fg: #e6edf3; --muted: #8d96a0; --bg: #0d1117; --panel: #161b22; --border: #30363d; --accent: #4493f8; } }
|
|
10
|
+
* { box-sizing: border-box; }
|
|
11
|
+
body { margin: 0; background: var(--bg); color: var(--fg); font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
|
|
12
|
+
header { position: sticky; top: 0; display: flex; align-items: center; gap: 12px; padding: 10px 24px; background: var(--panel); border-bottom: 1px solid var(--border); }
|
|
13
|
+
header .name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
14
|
+
header .meta { color: var(--muted); font-size: 13px; white-space: nowrap; }
|
|
15
|
+
header .spacer { flex: 1; }
|
|
16
|
+
.toggle { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
|
|
17
|
+
.toggle button { font: inherit; font-size: 13px; padding: 4px 12px; border: 0; background: transparent; color: var(--fg); cursor: pointer; }
|
|
18
|
+
.toggle button[aria-pressed="true"] { background: var(--accent); color: #fff; }
|
|
19
|
+
main { max-width: 860px; margin: 0 auto; padding: 32px 24px 80px; }
|
|
20
|
+
#raw { display: none; white-space: pre-wrap; word-wrap: break-word; font: 14px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; margin: 0; }
|
|
21
|
+
body.raw #raw { display: block; } body.raw #rendered { display: none; }
|
|
22
|
+
#rendered h1, #rendered h2 { border-bottom: 1px solid var(--border); padding-bottom: .3em; }
|
|
23
|
+
#rendered a { color: var(--accent); }
|
|
24
|
+
#rendered code { font: 85% ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--panel); padding: .2em .4em; border-radius: 4px; }
|
|
25
|
+
#rendered pre { background: var(--panel); padding: 14px 16px; border-radius: 6px; overflow: auto; }
|
|
26
|
+
#rendered pre code { background: none; padding: 0; }
|
|
27
|
+
#rendered blockquote { margin: 0; padding: 0 1em; color: var(--muted); border-left: .25em solid var(--border); }
|
|
28
|
+
#rendered table { border-collapse: collapse; display: block; overflow: auto; }
|
|
29
|
+
#rendered th, #rendered td { border: 1px solid var(--border); padding: 6px 13px; }
|
|
30
|
+
#rendered tr:nth-child(2n) { background: var(--panel); }
|
|
31
|
+
#rendered img { max-width: 100%; }
|
|
32
|
+
.doc-ref { color: var(--accent); font-weight: 500; border-bottom: 1px dotted var(--accent); cursor: help; }
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<header>
|
|
37
|
+
<span class="name" title="${a(e.name)}">${a(r)}</span>
|
|
38
|
+
<span class="meta">${a(e.name)} \xB7 revision ${e.revision}</span>
|
|
39
|
+
<span class="spacer"></span>
|
|
40
|
+
<span class="toggle" role="group" aria-label="View">
|
|
41
|
+
<button type="button" data-mode="rendered" aria-pressed="true">Rendered</button>
|
|
42
|
+
<button type="button" data-mode="raw" aria-pressed="false">Raw</button>
|
|
43
|
+
</span>
|
|
44
|
+
</header>
|
|
45
|
+
<main>
|
|
46
|
+
<article id="rendered">
|
|
47
|
+
${t}
|
|
48
|
+
</article>
|
|
49
|
+
<pre id="raw">${a(e.body)}</pre>
|
|
50
|
+
</main>
|
|
51
|
+
<script>
|
|
52
|
+
(function () {
|
|
53
|
+
var KEY = 'crtr-doc-view';
|
|
54
|
+
function set(mode) {
|
|
55
|
+
document.body.classList.toggle('raw', mode === 'raw');
|
|
56
|
+
document.querySelectorAll('.toggle button').forEach(function (b) { b.setAttribute('aria-pressed', String(b.dataset.mode === mode)); });
|
|
57
|
+
try { localStorage.setItem(KEY, mode); } catch (e) {}
|
|
58
|
+
}
|
|
59
|
+
document.querySelectorAll('.toggle button').forEach(function (b) { b.addEventListener('click', function () { set(b.dataset.mode); }); });
|
|
60
|
+
var saved = null; try { saved = localStorage.getItem(KEY); } catch (e) {}
|
|
61
|
+
set(saved === 'raw' ? 'raw' : 'rendered');
|
|
62
|
+
})();
|
|
63
|
+
<\/script>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
66
|
+
`}o(g,"renderDocumentPage");export{g as renderDocumentPage};
|
|
@@ -25,6 +25,10 @@ export declare function docMaterializeRoot(): string;
|
|
|
25
25
|
/** Write the snapshot's body to its stable path (idempotent) and return the path.
|
|
26
26
|
* An existing file at that path is kept: it may hold the person's unsaved edit. */
|
|
27
27
|
export declare function materializeDocument(ref: string, snap: DocumentSnapshot, root?: string): string;
|
|
28
|
+
/** Write the snapshot's browser page beside its Markdown copy (idempotent per
|
|
29
|
+
* revision) and return the page's path. Materializes the Markdown copy and
|
|
30
|
+
* sidecar too, so the page's directory is still recognized as a document. */
|
|
31
|
+
export declare function materializeDocumentPage(ref: string, snap: DocumentSnapshot, root?: string): string;
|
|
28
32
|
/** The document a local path was materialized from, or undefined for a plain file. */
|
|
29
33
|
export declare function documentForPath(path: string, root?: string): MaterializedDoc | undefined;
|
|
30
34
|
/** Save a person's edit of a materialized document. */
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),
|
|
3
|
-
`),{outcome:"saved",revision:
|
|
1
|
+
var v=Object.defineProperty;var o=(t,e)=>v(t,"name",{value:e,configurable:!0});import{createHash as $}from"node:crypto";import{existsSync as d,mkdirSync as S,readFileSync as y,renameSync as _,writeFileSync as p}from"node:fs";import{tmpdir as x}from"node:os";import{basename as A,dirname as g,join as c}from"node:path";import{renderDocumentPage as C}from"./doc-html.js";const z="CRTR_ATTACH_DOC_SAVEBACK",m=".crtr-doc.json";function a(){return c(x(),"crtr-docs")}o(a,"docMaterializeRoot");const f=o(t=>$("sha256").update(t).digest("hex"),"sha");function h(t){return t.replace(/[^A-Za-z0-9._-]/g,"_").slice(0,120)||"doc"}o(h,"safeSegment");function k(t,e,r=a()){const i=c(r,h(e.id),`r${e.revision}`),n=c(i,`${h(A(e.name))}.md`),s=c(i,m);if(d(n)&&d(s))return n;S(i,{recursive:!0});const l={ref:t,id:e.id,name:e.name,revision:e.revision,heads_key:e.heads_key,sha256:f(e.body)};return u(n,e.body),u(s,`${JSON.stringify(l)}
|
|
2
|
+
`),n}o(k,"materializeDocument");function R(t,e,r=a()){const n=k(t,e,r).replace(/\.md$/,".html");return d(n)||u(n,C({name:e.name,revision:e.revision,body:e.body})),n}o(R,"materializeDocumentPage");function u(t,e){const r=`${t}.${process.pid}.tmp`;p(r,e),_(r,t)}o(u,"writeAtomic");function w(t,e=a()){if(t.startsWith(`${e}/`))try{const r=JSON.parse(y(c(g(t),m),"utf8"));return typeof r.id=="string"&&typeof r.heads_key=="string"?r:void 0}catch{return}}o(w,"documentForPath");async function F(t,e,r=a()){const i=w(t,r);if(i===void 0||!d(t))return{outcome:"missing"};const n=y(t,"utf8");if(f(n)===i.sha256)return{outcome:"unchanged"};try{const s=await e(i.id,n,i.heads_key);return s==="stale"?{outcome:"stale",path:t}:(u(c(g(t),m),`${JSON.stringify({...i,sha256:f(n)})}
|
|
3
|
+
`),{outcome:"saved",revision:s.revision})}catch(s){return{outcome:"failed",message:s instanceof Error?s.message:String(s)}}}o(F,"saveDocumentEdit");function J(t,e){switch(e.outcome){case"unchanged":return;case"saved":return`Saved ${t} as revision ${e.revision}`;case"stale":return`${t} changed while you edited it \u2014 not saved; your text is kept at ${e.path}`;case"missing":return`Could not find the edited copy of ${t}`;case"failed":return`Could not save ${t}: ${e.message}`}}o(J,"saveOutcomeNotice");export{z as DOC_SAVEBACK_ENV,a as docMaterializeRoot,w as documentForPath,k as materializeDocument,R as materializeDocumentPage,F as saveDocumentEdit,J as saveOutcomeNotice};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CanvasSource } from '../../../core/canvas/source.js';
|
|
2
|
+
import type { DocLinkOpenMode } from '../../../types.js';
|
|
2
3
|
export interface DocumentLinks {
|
|
3
4
|
/** Whether a clicked local path is a materialized document. */
|
|
4
5
|
isDocumentPath(path: string): boolean;
|
|
@@ -16,6 +17,8 @@ export declare function createDocumentLinks(opts: {
|
|
|
16
17
|
selfNodeId: string;
|
|
17
18
|
remote: boolean;
|
|
18
19
|
canvasSource: CanvasSource;
|
|
20
|
+
/** Where a link points: the browser page or the editable Markdown copy. */
|
|
21
|
+
openMode: DocLinkOpenMode;
|
|
19
22
|
/** Re-render the transcript's Markdown after a name or path lands. */
|
|
20
23
|
refresh: () => void;
|
|
21
24
|
}): DocumentLinks;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var k=Object.defineProperty;var i=(n,o)=>k(n,"name",{value:o,configurable:!0});import{APIError as C}from"../../../api/errors.js";import{cliClient as v}from"../../../commands/api-client.js";import{setDocLinkContext as
|
|
1
|
+
var k=Object.defineProperty;var i=(n,o)=>k(n,"name",{value:o,configurable:!0});import{APIError as C}from"../../../api/errors.js";import{cliClient as v}from"../../../commands/api-client.js";import{setDocLinkContext as g}from"../render/doc-links.js";import{DOC_SAVEBACK_ENV as y,documentForPath as h,materializeDocument as b,materializeDocumentPage as E,saveDocumentEdit as L,saveOutcomeNotice as O}from"./doc-materialize.js";async function w(n){const o=await v().objects.read(n);if(o.state!=="object"||o.type!=="document")return`${n} is not a readable document`;const a=o.data;if(a.needs_merge||a.bodies.length!==1)return`${n} has ${a.heads.length} heads and needs a merge`;const s=o.object.owner?.handle;return{id:o.object.id,name:s===void 0?o.object.name:`${s}/${o.object.name}`,revision:a.revision,heads_key:a.heads.map(d=>d.rev_id).sort().join(","),body:a.bodies[0]}}i(w,"readSnapshot");function M(n){const o=new Map,a=new Map,s=new Set,d=new Set;let m=!1,f=!1,l=!1;const j=i(()=>{m||f||(f=!0,n.canvasSource.listNodes().then(t=>{for(const e of t)o.set(e.node_id,e.name);m=!0,l||n.refresh()}).catch(()=>{m=!0}).finally(()=>{f=!1}))},"loadRoster"),D=i(t=>{n.remote||s.has(t)||d.has(t)||(s.add(t),w(t).then(e=>{if(typeof e=="string"){d.add(t);return}a.set(t,n.openMode==="browser"?E(t,e):b(t,e)),l||n.refresh()}).catch(()=>{d.add(t)}).finally(()=>{s.delete(t)}))},"materialize"),N={selfNodeId:n.selfNodeId,nodeName:i(t=>{const e=o.get(t);return e===void 0&&j(),e},"nodeName"),pathFor:i(t=>{const e=a.get(t);return e===void 0&&D(t),e},"pathFor")};return g(N),{isDocumentPath:i(t=>h(t)!==void 0,"isDocumentPath"),async prepareOpen(t){if(n.remote)return{notice:"Opening a document is unavailable in a remote attach"};const e=h(t);if(e===void 0)return{notice:"Not a document link"};try{const r=await w(e.id);if(typeof r=="string")return{notice:r};const c=b(e.ref,r);return a.set(e.ref,c),{path:c}}catch(r){return{notice:`Could not read ${e.name}: ${r instanceof Error?r.message:String(r)}`}}},async consumeSaveBack(t){const e=process.env[y];if(e===void 0||e==="")return;delete process.env[y];const r=h(e),c=await L(e,async(S,_,$)=>{try{return{revision:(await v().docs.edit(S,{body:_,base:$,rationale:"edited by the person in the attach viewer"})).revision}}catch(u){if(u instanceof C&&u.code==="stale_revision")return"stale";throw u}}),p=O(r?.name??e,c);p!==void 0&&t(p)},dispose(){l=!0,g(void 0)}}}i(M,"createDocumentLinks");export{M as createDocumentLinks};
|