@heycys/gallery 0.2.1 → 0.2.3
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/THIRD-PARTY-NOTICES.md +1 -1
- package/browser/editor/page/annotate/index.mjs +3 -2
- package/browser/editor/page/board/index.mjs +2 -1
- package/browser/editor/page/board/settings.mjs +2 -1
- package/browser/editor/page/core.mjs +3 -2
- package/browser/editor/page/custom-page.mjs +4 -3
- package/browser/editor/page/editor.mjs +13 -7
- package/browser/editor/page/emoji-picker/support.mjs +3 -2
- package/browser/editor/page/links/index.mjs +1 -12
- package/browser/editor/page/links/pages.mjs +3 -2
- package/browser/editor/page/outline/folds.mjs +3 -0
- package/browser/editor/page/post.mjs +2 -1
- package/browser/editor/page/present-links.mjs +4 -3
- package/browser/editor/page/present-shell.mjs +3 -58
- package/browser/editor/page/session.mjs +3 -37
- package/browser/editor/page/storage.mjs +5 -5
- package/browser/shared/component-catalog.mjs +2 -2
- package/browser/shared/drafts.mjs +26 -51
- package/browser/shared/emoji/icon-value.mjs +2 -1
- package/browser/shared/extension-manifest.mjs +90 -0
- package/browser/shared/extensions.mjs +26 -16
- package/browser/shared/http.mjs +1 -1
- package/browser/shared/i18n/en.mjs +46 -15
- package/browser/shared/media.mjs +2 -1
- package/browser/shared/outline.mjs +11 -51
- package/browser/shared/page-address.mjs +8 -15
- package/browser/shared/paths.mjs +2 -1
- package/browser/shared/preferences.mjs +3 -1
- package/browser/shared/preview-server.mjs +51 -63
- package/browser/shared/service-location.mjs +18 -0
- package/browser/shared/session.mjs +3 -6
- package/browser/shared/workspace-address.mjs +22 -0
- package/browser/shared/workspace-columns.mjs +1 -2
- package/browser/shared/workspace-registry.mjs +6 -40
- package/browser/shared/workspace.mjs +3 -6
- package/browser/shared/writing-preferences.mjs +6 -4
- package/dist/{chunk-5MEXZ2UF.mjs → chunk-225ALKYI.mjs} +1 -1
- package/dist/{chunk-7N2LPG33.mjs → chunk-2HS7ZBPU.mjs} +1 -1
- package/dist/chunk-3F57LEN5.mjs +2 -0
- package/dist/{chunk-MKTX5PGV.mjs → chunk-43B75J5T.mjs} +1 -1
- package/dist/{chunk-VKSRM7EY.mjs → chunk-4B2TB5K2.mjs} +1 -1
- package/dist/{chunk-LV24RY2E.mjs → chunk-4K5KC6V2.mjs} +1 -1
- package/dist/chunk-5GK7HS62.mjs +2 -0
- package/dist/chunk-6GV3CBB2.mjs +2 -0
- package/dist/{chunk-TOE6DPLH.mjs → chunk-6NLXUKAY.mjs} +1 -1
- package/dist/{chunk-ZCP7BTOZ.mjs → chunk-6QBHU4DF.mjs} +1 -1
- package/dist/{chunk-QXVNUASA.mjs → chunk-6QWQXHAV.mjs} +1 -1
- package/dist/chunk-6XD6U3QA.mjs +5 -0
- package/dist/{chunk-DKR7LFIY.mjs → chunk-6Y3HYOBM.mjs} +1 -1
- package/dist/{chunk-36FIRJIF.mjs → chunk-742D2WWJ.mjs} +2 -2
- package/dist/{chunk-7M7UPWU6.mjs → chunk-7EFHNQ5B.mjs} +1 -1
- package/dist/{chunk-2GKGEVMR.mjs → chunk-7UZUQQ7Q.mjs} +25 -24
- package/dist/{chunk-VPVOAJ7X.mjs → chunk-AGDILNWB.mjs} +1 -1
- package/dist/{chunk-5IIH335D.mjs → chunk-ALZ6ZENZ.mjs} +1 -1
- package/dist/{chunk-G4YP7GVC.mjs → chunk-BFJGHSQN.mjs} +1 -1
- package/dist/{chunk-AMDGVDNV.mjs → chunk-BQNTTFPL.mjs} +1 -1
- package/dist/chunk-BX5AKINW.mjs +15 -0
- package/dist/{chunk-PAFSFVYB.mjs → chunk-BYNPEUDS.mjs} +1 -1
- package/dist/{chunk-74RPP2M2.mjs → chunk-C564NU2H.mjs} +1 -1
- package/dist/{chunk-DH6GMDNN.mjs → chunk-CRDG6EF2.mjs} +1 -1
- package/dist/{chunk-7XDYK3UC.mjs → chunk-E7656LR7.mjs} +1 -1
- package/dist/{chunk-KFZBYDIS.mjs → chunk-ECZBLQKK.mjs} +1 -1
- package/dist/{chunk-ZEY2RLBT.mjs → chunk-ERLAIBOA.mjs} +2 -2
- package/dist/{chunk-YFJU3O7G.mjs → chunk-FJLM7YE7.mjs} +1 -1
- package/dist/{chunk-AFGUR7SY.mjs → chunk-FNCCVGKX.mjs} +1 -1
- package/dist/{chunk-VNUWREXU.mjs → chunk-FOJ3K5Z3.mjs} +1 -1
- package/dist/{chunk-WCWYNFDF.mjs → chunk-FQ72LZHB.mjs} +1 -1
- package/dist/chunk-G7N7YFVD.mjs +2 -0
- package/dist/chunk-GIRXJBGC.mjs +2 -0
- package/dist/{chunk-VA3KAQZD.mjs → chunk-GKOBB6DH.mjs} +1 -1
- package/dist/chunk-GMJD7ID3.mjs +2 -0
- package/dist/chunk-GQJMVSSN.mjs +2 -0
- package/dist/{chunk-QZ35UAB4.mjs → chunk-GRAXKKG4.mjs} +1 -1
- package/dist/{chunk-SAHS56QM.mjs → chunk-HX4DLE3J.mjs} +1 -1
- package/dist/{chunk-2RKIHJI2.mjs → chunk-HYMEEXKN.mjs} +1 -1
- package/dist/{chunk-QK3VISCA.mjs → chunk-IA4PSIMD.mjs} +1 -1
- package/dist/chunk-IS7N3N6Q.mjs +168 -0
- package/dist/{chunk-CZ5KJSGK.mjs → chunk-JJNHKGTD.mjs} +1 -1
- package/dist/chunk-JPG2LBQM.mjs +11 -0
- package/dist/chunk-JYK5WJPM.mjs +2 -0
- package/dist/{chunk-LE44MD3O.mjs → chunk-KLQP46Y3.mjs} +1 -1
- package/dist/chunk-L55NOXBE.mjs +4 -0
- package/dist/{chunk-FXQBADMN.mjs → chunk-LCB6MARS.mjs} +1 -1
- package/dist/{chunk-SOD23RGL.mjs → chunk-LJLVCU6W.mjs} +8 -8
- package/dist/{chunk-NAGMRVW4.mjs → chunk-LKX55H76.mjs} +1 -1
- package/dist/chunk-LMUZQK5O.mjs +2 -0
- package/dist/{chunk-CG3UHDKH.mjs → chunk-M4NRDYI5.mjs} +1 -1
- package/dist/{chunk-HM4E43VM.mjs → chunk-MAFIGVXY.mjs} +1 -1
- package/dist/chunk-NLDF35P5.mjs +2 -0
- package/dist/{chunk-3OFXMN6N.mjs → chunk-NMF3IPYA.mjs} +1 -1
- package/dist/chunk-ONKC4BCC.mjs +2 -0
- package/dist/{chunk-CPS4SRPF.mjs → chunk-OU6ZZVBX.mjs} +2 -2
- package/dist/{chunk-NWRZXELO.mjs → chunk-PAREWQGB.mjs} +1 -1
- package/dist/{chunk-BYSF5UMM.mjs → chunk-PKVK6WFC.mjs} +2 -2
- package/dist/{chunk-4AMTKWDO.mjs → chunk-PUOQ65NC.mjs} +1 -1
- package/dist/{chunk-L7W624FD.mjs → chunk-QJAVCTY7.mjs} +1 -1
- package/dist/{chunk-6MPKCH3P.mjs → chunk-QNECWMGR.mjs} +1 -1
- package/dist/{chunk-HBAUKKQH.mjs → chunk-QP7BOHE4.mjs} +1 -1
- package/dist/chunk-QVGOPHOF.mjs +2 -0
- package/dist/{chunk-NFABH3QW.mjs → chunk-RRKJMHNX.mjs} +2 -2
- package/dist/chunk-S3SX3COT.mjs +20 -0
- package/dist/chunk-SQ3FQCCN.mjs +8 -0
- package/dist/{chunk-ZO7SFB2E.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
- package/dist/{chunk-3A5I2GJK.mjs → chunk-T4Y2ZSVX.mjs} +1 -1
- package/dist/{chunk-VG5JEIUQ.mjs → chunk-TKWEX4FD.mjs} +2 -2
- package/dist/{chunk-CCSGFCLZ.mjs → chunk-U3SSPL4K.mjs} +1 -1
- package/dist/{chunk-DHB7UZ7F.mjs → chunk-U5XL7D6A.mjs} +1 -1
- package/dist/{chunk-JXLSHVJ2.mjs → chunk-UMHUSOPJ.mjs} +1 -1
- package/dist/chunk-UTF7GBQU.mjs +4 -0
- package/dist/chunk-UTMAY3WK.mjs +2 -0
- package/dist/{chunk-S2Y7VI4H.mjs → chunk-V4ZHRALW.mjs} +2 -2
- package/dist/{chunk-GWOYMENX.mjs → chunk-V6XZ4VIR.mjs} +1 -1
- package/dist/{chunk-WTEGUPFG.mjs → chunk-VRPVVMRB.mjs} +1 -1
- package/dist/{chunk-XKOHV4H6.mjs → chunk-W5NPH5UA.mjs} +1 -1
- package/dist/{chunk-VGTMLQWZ.mjs → chunk-WD4U6XXT.mjs} +1 -1
- package/dist/{chunk-4DNHHJSL.mjs → chunk-XBNFXWAW.mjs} +1 -1
- package/dist/chunk-XZV72SQ4.mjs +2 -0
- package/dist/{chunk-QJ3TFJLQ.mjs → chunk-Y34NR5JL.mjs} +1 -1
- package/dist/{chunk-YBB5LWZK.mjs → chunk-YFJB534W.mjs} +1 -1
- package/dist/{chunk-V5JRSGZF.mjs → chunk-YN37ZDLW.mjs} +1 -1
- package/dist/chunk-Z6OKLRHC.mjs +2 -0
- package/dist/chunk-ZLS2FRS6.mjs +3 -0
- package/dist/{chunk-JI6T3A4Y.mjs → chunk-ZMHUDYLV.mjs} +1 -1
- package/dist/{chunk-KH24LWAT.mjs → chunk-ZTHICSSF.mjs} +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/server.mjs +1 -1
- package/dist/worker.mjs +1 -1
- package/kit/math.js +1 -1
- package/kit/runtime.js +13 -2
- package/kit/state.js +2 -0
- package/kit/ui.js +5 -1
- package/package.json +1 -1
- package/release.json +7 -7
- package/skill/SKILL.md +13 -13
- package/skill/references/building-blocks.md +1 -1
- package/skill/references/extensions.md +45 -9
- package/web/assets/index-D8P8uhDW.js +15 -0
- package/web/assets/{index-xP0uw3a0.css → index-FLb9jCfl.css} +1 -1
- package/web/index.html +2 -2
- package/dist/chunk-46ACXOU5.mjs +0 -2
- package/dist/chunk-5DPABPBI.mjs +0 -2
- package/dist/chunk-5L26JUYA.mjs +0 -2
- package/dist/chunk-AEMVZYZQ.mjs +0 -166
- package/dist/chunk-ASAXY2DJ.mjs +0 -2
- package/dist/chunk-DAMVDXR3.mjs +0 -2
- package/dist/chunk-DLFZJJEE.mjs +0 -2
- package/dist/chunk-EKNHM3P4.mjs +0 -2
- package/dist/chunk-FCK6NXON.mjs +0 -3
- package/dist/chunk-FX42XU6J.mjs +0 -2
- package/dist/chunk-HATA2W7X.mjs +0 -2
- package/dist/chunk-JJNRIFND.mjs +0 -8
- package/dist/chunk-JL6R4FG4.mjs +0 -4
- package/dist/chunk-KUILYI7H.mjs +0 -4
- package/dist/chunk-NIW7F7LU.mjs +0 -4
- package/dist/chunk-OU3S466R.mjs +0 -20
- package/dist/chunk-QCEATQC4.mjs +0 -2
- package/dist/chunk-TDWJKOLN.mjs +0 -2
- package/dist/chunk-UGO5N3EI.mjs +0 -15
- package/dist/chunk-XMKHCUI6.mjs +0 -15
- package/dist/chunk-XQRUG4G3.mjs +0 -2
- package/dist/chunk-YPPJVDQI.mjs +0 -2
- package/dist/chunk-ZANMKAWS.mjs +0 -2
- package/web/assets/index-BHC5ZOMh.js +0 -15
- /package/dist/{chunk-N3DSBH5S.mjs → chunk-A55HN5OK.mjs} +0 -0
- /package/dist/{chunk-KNZJT4ET.mjs → chunk-QAIHRH3I.mjs} +0 -0
- /package/dist/{chunk-ZW2A6GBW.mjs → chunk-QR6A54KJ.mjs} +0 -0
- /package/dist/{chunk-74UW7QSL.mjs → chunk-UH4SIXTR.mjs} +0 -0
- /package/dist/{chunk-EKGYYOSP.mjs → chunk-W7XHE4CT.mjs} +0 -0
- /package/dist/{chunk-5MMN3N5J.mjs → chunk-YEY3RSFQ.mjs} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{a,b,c,d,e,f,g}from"./chunk-5GK7HS62.mjs";import"./chunk-S3SX3COT.mjs";import"./chunk-IA4PSIMD.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-6NLXUKAY.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-BX5AKINW.mjs";import"./chunk-HX4DLE3J.mjs";import"./chunk-ZTHICSSF.mjs";import"./chunk-FJLM7YE7.mjs";import"./chunk-PAREWQGB.mjs";import"./chunk-BFJGHSQN.mjs";import"./chunk-U5XL7D6A.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-W5NPH5UA.mjs";import"./chunk-ECZBLQKK.mjs";import"./chunk-E7656LR7.mjs";import"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{a as handInReports,e as idLines,f as inStepLines,d as noteNew,b as previewSettled,g as rereadLine,c as roundBegins};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{b as D}from"./chunk-VRPVVMRB.mjs";import{g as I}from"./chunk-HX4DLE3J.mjs";import{a as $}from"./chunk-HYMEEXKN.mjs";import{e as E,j as F}from"./chunk-ECZBLQKK.mjs";import d from"node:fs/promises";import x from"node:path";import{isInstance as T}from"../browser/shared/components/index.mjs";import{ATTRIBUTE as _}from"../browser/shared/components/installed.mjs";import{exportSourceOf as z}from"../browser/shared/export-wrap.mjs";import{sha256 as b}from"../browser/shared/files.mjs";import{attr as w,hasClass as k}from"../browser/shared/html-tree.mjs";import{reportsDirectory as j}from"../browser/shared/paths.mjs";import{documentDirectory as G,documentFiles as H,layoutOf as B}from"../browser/shared/workspace.mjs";function K(e,t=null){return e.tagName==="figure"&&k(e,"doc-figure")||e.tagName==="div"&&(k(e,"doc-code")||k(e,"doc-diff"))||t===R||T(e)||k(e,"doc-flow")||k(e,"doc-sheet-shell")||w(e,"data-doc-sheets")!==void 0?0:e.tagName==="figure"||k(e,"doc-columns")?1:k(e,"doc-table-wrap")?2:3}function Q(e){let t=new Map,s=new Map;for(let a of E(e).units)a.blockId&&!s.has(a.blockId)&&s.set(a.blockId,a.kind);let l=new Map,n=new Map,c=new Map,i=a=>!!a.tagName&&w(a,"data-block-id")!==void 0,u=(a,f)=>{if(a.nodeName==="#text")return a.value;if(!a.tagName||a.tagName==="template")return"";let r=w(a,"data-doc-chart-data")?.trim();r&&l.get(f).push(r);let o=w(a,"data-doc-chart-source")?.trim();o&&!n.has(o)&&n.set(o,f);let h=a.attrs.map(({name:g,value:y})=>`${g}="${y}"`).sort().join(" ");return`<${a.tagName} ${h}>${(a.childNodes??[]).map(g=>i(g)?"":u(g,f)).join("")}</${a.tagName}>`},p=(a,f)=>{for(let r of a.childNodes??[]){if(!r.tagName||r.tagName==="template")continue;let o=w(r,"data-block-id"),h=f;if(o!==void 0&&!t.has(o)){let g=[r.tagName,...(w(r,"class")??"").split(/\s+/).filter(Boolean).slice(0,1)].join("."),y=c.get(f)??0;c.set(f,y+1),l.set(o,[]);let M=K(r,s.get(o)),N=r.sourceCodeLocation,U=N?(N.endOffset??N.startTag?.endOffset??N.startOffset)-N.startOffset:0,S=u(r,o).replace(L," ");t.set(o,{hash:b(S).slice(0,16),bare:b(S.replace(`data-block-id="${o}"`,"")).slice(0,16),rank:M,card:M<3,whole:s.get(o)===R,figure:s.get(o)===Y,tag:g,parent:f,at:y,size:U,package:T(r)?w(r,_)??null:null}),h=o}p(r,h)}};p($(e),null);let m=new Map([...t].map(([a,{hash:f,bare:r}])=>[a,{hash:f,bare:r}]));for(let[a,f]of l){let r=f.map(o=>m.get(n.get(o))).filter(Boolean);r.length&&(t.get(a).hash=b([m.get(a).hash,...r.map(({hash:o})=>o)].join("|")).slice(0,16),t.get(a).bare=b([m.get(a).bare,...r.map(({bare:o})=>o)].join("|")).slice(0,16))}return t}function V(e){let t=[],s=l=>{for(let n of l.childNodes??[])if(!(!n.tagName||n.tagName==="template"||w(n,"data-block-id")!==void 0))if(n.tagName==="script"||n.tagName==="style"){let c=n.attrs.map(({name:i,value:u})=>`${i}="${u}"`).sort().join(" ");t.push(`<${n.tagName} ${c}>${(n.childNodes??[]).map(i=>i.value??"").join("")}`.replace(L," "))}else s(n)};return s($(e)),t.length?b(t.join("|")).slice(0,16):""}var X=e=>Object.fromEntries(I(e).map(({id:t,stamp:s})=>[t,s])),R="block",Y="figure",L=/[ \t\n\f\r]+/g,Z=["export","preview"];function tt(e){if(e==null)return null;try{let{blocks:t,places:s,moved:l,unseen:n,from:c,time:i,idless:u,page:p,packages:m}=JSON.parse(e)??{};if(!t||typeof t!="object"||Array.isArray(t))return null;let a=s&&typeof s=="object"&&!Array.isArray(s)?s:{},f=r=>Array.isArray(r)?r.filter(o=>typeof o=="string"):[];return{blocks:Object.fromEntries(Object.entries(t).filter(([,r])=>typeof r=="string")),places:Object.fromEntries(Object.entries(a).filter(([,r])=>Array.isArray(r)&&r.length===2&&(r[0]===null||typeof r[0]=="string")&&Number.isInteger(r[1]))),moved:f(l),unseen:f(n),...Z.includes(c)?{from:c,time:Number.isFinite(i)?i:null}:{},...f(u).length?{idless:f(u)}:{},...typeof p=="string"?{page:p}:{},...m&&typeof m=="object"&&!Array.isArray(m)?{packages:Object.fromEntries(Object.entries(m).filter(([,r])=>typeof r=="string"))}:{}}}catch{return null}}var A="ge-idless-";function et(e,{from:t,time:s}){let l=0,{text:n}=F(D(e).text,()=>`${A}${l++}`),c=X(n),i={blocks:{},places:{},moved:[],unseen:[],from:t,time:s,page:V(n),...Object.keys(c).length?{packages:c}:{}},u=[];for(let[p,{hash:m,bare:a,card:f,parent:r,at:o}]of Q(n)){if(p.startsWith(A)){u.push(a);continue}i.blocks[p]=m,r?.startsWith(A)||(i.places[p]=[r,o]),f&&i.unseen.push(p)}return u.length?{...i,idless:u}:i}function bt(e,t){if(!t?.idless?.length)return t;let s=new Map;for(let c of t.idless)s.set(c,(s.get(c)??0)+1);let l={...t,blocks:{...t.blocks},unseen:[...t.unseen]};for(let[c,{hash:i,bare:u,card:p}]of e)Object.hasOwn(t.blocks,c)||!s.get(u)||(s.set(u,s.get(u)-1),l.blocks[c]=i,p&&l.unseen.push(c));let n=[...s].flatMap(([c,i])=>Array(i).fill(c));return n.length?l.idless=n:delete l.idless,l}var st=/<[^>]*\sdata-doc-placeholder\b/,O=(e,t)=>x.join(B(t).qa,e),P=(e,t)=>x.join(O(e,t),"blocks.json"),W=(e,t)=>x.join(O(e,t),"exported.html");async function q(e,t=j){return tt(await d.readFile(P(e,t),"utf8").catch(()=>null))!==null}async function C(e,t,s,l=j){let n=B(l).state,c=async o=>d.readFile(o).then(h=>({bytes:h,file:o}),()=>null),i=await c(W(e,l)),u=[i];if(t){let o=t.slice(0,16),h=x.join(n,"history",e),g=await d.readdir(h).catch(()=>[]);u.push(...await Promise.all(g.filter(y=>y.endsWith(`-${o}.html`)).map(y=>c(x.join(h,y))))),u.push(await c(x.join(n,"snapshots",`${o}.html`)))}let p=i?(await d.stat(i.file).catch(()=>null))?.mtimeMs??null:null,a=!(i&&t&&s!==null&&p!==null&&p>s&&b(i.bytes)!==t)&&t&&u.find(o=>o&&b(o.bytes)===t),f=a||i;if(!f)return null;let r=a&&s!==null?s:f===i?p??s:(await d.stat(f.file).catch(()=>null))?.mtimeMs??s;return{text:f.bytes.toString("utf8"),time:r??null}}async function v(e,t){let s=B(t).exports,l=x.join(s,`${e}.html`),[n,c]=await Promise.all([d.readFile(l,"utf8").catch(()=>null),d.stat(l).catch(()=>null)]);return n===null||!c?null:{said:z(n),time:c.mtimeMs}}async function J(e,t,s,l){if(st.test(t))return!1;let n=et(t,s);return!Object.keys(n.blocks).length&&!n.idless?!1:(await d.mkdir(O(e,l),{recursive:!0}),await d.writeFile(P(e,l),`${JSON.stringify(n)}
|
|
3
|
+
`),!0)}async function kt(e,t=j){let s=new Map(await Promise.all(e.map(async n=>[n,await v(n,t).catch(()=>null)])));return{keep:async(n,c=null)=>{for(let i of n)try{if(!await q(i,t)){let m=s.get(i),a=await C(i,m?.said??null,m?.time??null,t);a&&await J(i,a.text,{from:"export",time:a.time},t)}let[u,p]=await Promise.all([d.readFile(H(G(i,t)).html),c?{said:c.get(i)??null}:v(i,t)]);p?.said&&b(u)===p.said&&(await d.mkdir(O(i,t),{recursive:!0}),await d.writeFile(W(i,t),u))}catch{}}}}async function xt(e,t,s=j){try{if(await q(e,s))return!1;let l=await v(e,s),n=await C(e,l?.said??null,l?.time??null,s);return await J(e,n?.text??t,n?{from:"export",time:n.time}:{from:"preview",time:Date.now()},s)}catch{return!1}}export{K as a,Q as b,V as c,X as d,Z as e,tt as f,et as g,bt as h,q as i,C as j,kt as k,xt as l};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
-
import{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}from"./chunk-
|
|
2
|
+
import{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}from"./chunk-NMF3IPYA.mjs";import"./chunk-LJLVCU6W.mjs";import"./chunk-XBNFXWAW.mjs";import"./chunk-342HXE3Q.mjs";import"./chunk-T4Y2ZSVX.mjs";import"./chunk-5GK7HS62.mjs";import"./chunk-SQ3FQCCN.mjs";import"./chunk-JEUK3ZO7.mjs";import"./chunk-M4NRDYI5.mjs";import"./chunk-PUOQ65NC.mjs";import"./chunk-ZLS2FRS6.mjs";import"./chunk-S3SX3COT.mjs";import"./chunk-IA4PSIMD.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-6NLXUKAY.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-4OZWXZE5.mjs";import"./chunk-BX5AKINW.mjs";import"./chunk-HX4DLE3J.mjs";import"./chunk-ZTHICSSF.mjs";import"./chunk-FJLM7YE7.mjs";import"./chunk-PAREWQGB.mjs";import"./chunk-BFJGHSQN.mjs";import"./chunk-U5XL7D6A.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-W5NPH5UA.mjs";import"./chunk-ECZBLQKK.mjs";import"./chunk-E7656LR7.mjs";import"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{b as BEGIN,e as GESTURES,M as INSIDE_ALONE,N as OPEN_IT,a as READY,c as SAVING,L as TRY_LINES,m as asShown,o as changesOf,k as chosenInstance,z as cutOffSaid,D as documentHost,y as dragPath,x as drawn,s as dropScratch,t as dropStale,v as fallbackSaid,g as gestureOf,i as instancesIn,d as keysOf,B as listen,P as madeSaid,A as nothingSaid,n as partsOf,j as placeSaid,r as scratchCopy,f as stepOf,E as stepsCalled,h as stepsOf,K as stoppedTrial,C as takenAgain,u as takenBy,q as toldBy,J as trial,O as trialSaid,Q as tryCommand,w as untilTaken,l as withoutIds,F as wordsDiffer,G as wordsIn,p as writtenLines,I as writtenOtherwise,H as writtenWords};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
-
import{b as h,e as x}from"./chunk-
|
|
2
|
+
import{b as h,e as x}from"./chunk-A55HN5OK.mjs";x();import{BODY as C}from"../browser/shared/components/index.mjs";import{attr as u,elementsOf as f,walk as w}from"../browser/shared/html-tree.mjs";import{decoded as R,pageAddress as T}from"../browser/shared/page-address.mjs";import{CHART_SOURCE as A,cellWords as N,tableRows as E}from"../browser/shared/table-chart.mjs";var p=a=>a.nodeName==="#text"?a.value:(a.childNodes??[]).map(p).join(""),n=a=>f(a).filter(l=>l.tagName==="td"||l.tagName==="th"),M=a=>["rowspan","colspan"].some(l=>{let o=Number.parseInt(u(a,l)??"1",10);return Number.isFinite(o)&&o!==1});function b(a){let l=f(a),o=t=>f(t).filter(m=>m.tagName==="tr"),e=l.find(t=>t.tagName==="thead"),s=e?o(e)[0]??null:null,r=l.filter(t=>t.tagName==="tbody").flatMap(o);return!s&&r.length>0&&n(r[0]).length>0&&n(r[0]).every(t=>t.tagName==="th")&&([s,...r]=r),{groups:l,rowsOf:o,header:s,rows:r}}function S(a){let{groups:l,rowsOf:o,header:e,rows:s}=b(a),r=t=>N(p(t));return{names:e?n(e).map(r):null,types:e?n(e).map(t=>u(t,"data-sort-type")??null):null,rows:s.map(t=>n(t).map(m=>({words:r(m),sort:u(m,"data-sort-value")?.trim()??null}))),foot:l.filter(t=>t.tagName==="tfoot").flatMap(o).map(t=>n(t)[0]?r(n(t)[0]):""),merged:[e,...s].some(t=>t&&n(t).some(M))}}function W(a,l){let o=[];return w(a,e=>{if(e.tagName!=="a")return;let s=(u(e,"href")??"").trim();if(!s)return;let r=s.startsWith("#")&&s.length>1?l?{slug:l,block:R(s.slice(1))}:null:T(s);r&&o.push({slug:r.slug,title:N(p(e)),block:r.block??null})}),o}function I(a,l,o=null){let e=null;if(w(h(String(a)),i=>{e!==null||u(i,A)!==l||(e=i.tagName==="table"?i:f(i).find(c=>c.tagName==="table")??!1)},{...C,inTemplates:!1}),!e)return{error:"missing"};let s=S(e),r=E(s);if(r.error)return{error:"merged"};let{header:t,rows:m}=b(e),k=t?m:m.slice(1),O=s.names?s.rows:s.rows.slice(1),d=r.data.columns;return{columns:[...d],rows:r.data.map((i,c)=>{let g={};return d.forEach((v,y)=>{g[v]=O[c][y]?.words??""}),{values:{...i},links:W(k[c],o),words:g}})}}export{S as a,I as b};
|
package/dist/cli.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
-
import{a,b,c,d,e}from"./chunk-
|
|
2
|
+
import{a,b,c,d,e}from"./chunk-L55NOXBE.mjs";import"./chunk-7UZUQQ7Q.mjs";import"./chunk-5L7RYZCL.mjs";export{e as main,d as notOurs,b as readCommandLine,c as unwritable,a as versionOf};
|
package/dist/server.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
-
import{a as o}from"./chunk-
|
|
2
|
+
import{a as o}from"./chunk-IS7N3N6Q.mjs";import"./chunk-6GV3CBB2.mjs";import"./chunk-DR73Y2UJ.mjs";import"./chunk-M4NRDYI5.mjs";import"./chunk-PUOQ65NC.mjs";import"./chunk-6QWQXHAV.mjs";import"./chunk-ZLS2FRS6.mjs";import"./chunk-S3SX3COT.mjs";import"./chunk-JJNHKGTD.mjs";import"./chunk-JPG2LBQM.mjs";import"./chunk-ALZ6ZENZ.mjs";import"./chunk-IA4PSIMD.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-6NLXUKAY.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-4B2TB5K2.mjs";import"./chunk-KLQP46Y3.mjs";import"./chunk-BX5AKINW.mjs";import"./chunk-HX4DLE3J.mjs";import"./chunk-ZTHICSSF.mjs";import"./chunk-FJLM7YE7.mjs";import"./chunk-PAREWQGB.mjs";import"./chunk-BFJGHSQN.mjs";import"./chunk-U5XL7D6A.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-W5NPH5UA.mjs";import"./chunk-ECZBLQKK.mjs";import"./chunk-E7656LR7.mjs";import"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";var t=process.argv.indexOf("--port"),r=t===-1?Number.NaN:Number(process.argv[t+1]);(!Number.isInteger(r)||r<=0||r>=65536)&&(console.error("Usage: node dist/server.mjs --port <n> (GALLERY_WORKSPACE names the workspace). The gallery command starts it: gallery open."),process.exit(2));try{let{port:e}=await o({port:r});console.log(`[gallery] ${new Date().toISOString()} listening on http://127.0.0.1:${e}/ (pid ${process.pid})`)}catch(e){console.error(`[gallery] could not start on port ${r}: ${e.message}`),process.exit(1)}
|
package/dist/worker.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
-
import{a as P,b as W,c as B,e as H}from"./chunk-
|
|
2
|
+
import{a as P,b as W,c as B,e as H}from"./chunk-RRKJMHNX.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-6NLXUKAY.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-BFJGHSQN.mjs";import"./chunk-U5XL7D6A.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-W5NPH5UA.mjs";import"./chunk-ECZBLQKK.mjs";import{c as F}from"./chunk-E7656LR7.mjs";import{g as R,i as M}from"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";import{parentPort as N}from"node:worker_threads";import{PAGE as U}from"../browser/editor/components/kit.mjs";import{APART as T,clipboardOf as X,tagKind as j,unitClipboard as _,writtenApart as Y}from"../browser/editor/components/registry.mjs";import{WRITERS as h,heldToWords as Z,unitOpening as tt,wordsIn as et}from"../browser/editor/page/clipboard/own/writers.mjs";import{escapeAttribute as v}from"../browser/shared/escape.mjs";import{referencedFile as rt}from"../browser/shared/media.mjs";var nt=/^\s*\\\(([\s\S]*)\\\)\s*$/,ot=" ",st=["script","style","template","noscript",T,U.header,`${U.header} > h1`].join(", ");function it(r,u){let f=globalThis.document.createElement("template");f.innerHTML=r??"";let d=g=>[...g.childNodes].map(n=>{if(n.nodeType===3)return n.data.replace(/ /g," ");if(n.nodeType!==1)return"";if(n.matches(".doc-math-inline")){let i=nt.exec(n.textContent)?.[1];return i!==void 0?i.trim()?`$${i.trim()}$`:"":n.textContent}if(n.localName==="img"){let i=n.getAttribute("src");return i?`)})`:""}if(n.matches("a.doc-link-mention")){let i=(n.querySelector(".doc-link-mention-title")??n).textContent.replace(/\s+/g," ").trim();return n.getAttribute("href")?`[${i||n.getAttribute("href")}](${n.getAttribute("href")})`:i}let l=d(n);if(n.localName==="br")return`
|
|
3
3
|
`;if(!l.trim())return l;let[c,m,p]=[l.match(/^\s*/)[0],l.trim(),l.match(/\s*$/)[0]];switch(n.localName){case"strong":case"b":return`${c}**${m}**${p}`;case"em":case"i":return`${c}*${m}*${p}`;case"s":case"del":case"strike":return`${c}~~${m}~~${p}`;case"code":return`${c}\`${m}\`${p}`;case"a":{let i=n.getAttribute("href")??"";return!i||i.startsWith("#")?l:rt(i)?`${c}[${m}](${u(i,"file",n.getAttribute("download")||D(i))})${p}`:m===i?`${c}${i}${p}`:`${c}[${m}](${i})${p}`}default:return n.classList.contains("doc-tab")?` ${l}`:l}}).join("");return Z(d(f.content).replace(/[ \t]+\n/g,`
|
|
4
4
|
`).trim())}function b(r){if(!r.querySelector(T))return r;let u=r.cloneNode(!0);for(let f of u.querySelectorAll(T))f.remove();return u}var ct=r=>(r?.textContent??"").replace(/\s+/g," ").trim(),D=r=>r.replace(/[?#].*$/,"").split("/").pop(),at=r=>r.replace(/[\\[\]]/g,"\\$&"),ut=/(\d+)/g;async function K(r,{title:u,media:f=g=>g,chart:d=()=>null}){let{UNIT:g,unitsOf:n,serializeInner:l,nestedBlocks:c,isHost:m}=await P(),p=e=>it(e,f),i=e=>{let t=b(e),o=[...t.querySelectorAll("img")];if(o.length===0)return l(t);let s=t.cloneNode(!0);return s.querySelectorAll("img").forEach((a,A)=>a.replaceWith(`\uE000${A}\uE001`)),l(s).replace(ut,(a,A)=>`<img src="${v(o[A].getAttribute("src")??"")}" alt="${v(o[A].getAttribute("alt")??"")}">`)},z=e=>p(i(e)),G=(e,{tables:t=!0}={})=>{let o=b(e);return[...o.querySelectorAll("img[src]")].filter(s=>!s.closest("a.doc-link-mention")&&(t||!o.contains(s.closest("table")))).map(s=>p(s.outerHTML))},x=e=>(j(e)&&!e.matches(g)&&e.setAttribute("data-ge-unit-kind",j(e)),e);function w(e){let t=[],o=null;for(let s of e.childNodes){let a=s.nodeType===1;(a?s.matches(st):s.nodeType!==3)||(a&&(s.matches(g)||F.has(s.localName))?(t.push(x(s)),o=null):(a||o||s.data.trim())&&(o||t.push(o=x(r.createElement("p"))),o.append(s.cloneNode(!0))))}return t}let y=e=>h[_(e).kind].list,C=e=>e.map(t=>t&&`${ot}${t}`);function k(e){let t=[],o=null;for(let s of e){let a=E(s);a.length!==0&&(o!==null&&!(y(o)&&y(s))&&t.push(""),t.push(...a),o=s)}return t}function S(e){let t=[];for(let o of e){let s=E(o);if(s.length===0)continue;let a=y(o);!a&&t.length>0&&t.at(-1)!==""&&t.push(""),!a&&t.length===0&&t.push(""),t.push(...C(s)),a||t.push("")}for(;t.at(-1)==="";)t.pop();return t}function J(e,t=null){let s=n(e).length>0?[]:h.words.exported(e,$,{without:t});return s.length>0?["",...C(s)]:S(w(e).filter(a=>a!==t))}function E(e){let t=_(e);return h[t.kind].exported(e,$,t)}function V(e,t){let o=/^en(?:-|$)/i.test(e.ownerDocument.documentElement.getAttribute("lang")??"");return t?o?"Chart":"\u56FE\u8868":o?"Figure":"\u56FE"}let $={isHost:m,nestedBlocks:c,serializeInner:l,media:f,chart:d,inline:p,own:z,withPictures:i,picturesIn:G,lines:k,held:S,heldIn:J,untitled:V,wordsIn:(e,t,o)=>et(b(e),t,o),plain:ct,unitsOf:w,words:e=>h.words.exported(e,$),packageOf:e=>R().packages.get(e),opening:(e,t)=>tt(e,t,$),writtenAs(e,t){let o=X(e);return h[o.kind].exported(t,$,o)}},I=r.querySelector(".doc-header"),O=I?k(w(I)):[],q=r.querySelector("main"),L=k(q?w(q):[]),Q=Y(r).flatMap(({element:e,clipboard:t})=>["",...h[t.kind].exported(e,$,t)]);return[`# ${u||"Untitled"}`,...O.length?["",...O]:[],...L.length?["",...L]:[],...Q].join(`
|
|
5
5
|
`)}import{saying as lt}from"../browser/shared/i18n/index.mjs";import{referencedFile as pt}from"../browser/shared/media.mjs";var ft=r=>`\0F${r}\0`,mt=r=>`\0C${r}\0`,dt={async markdown({source:r,title:u,pictures:f}){let{document:d}=await W(r),g=[],n=[...d.querySelectorAll("main figure.doc-figure")];return{text:await K(d,{title:u,media:(c,m,p)=>{let i=pt(c);return i?ft(g.push({src:i,kind:m,name:p})-1):c},chart:c=>f&&n.includes(c)?mt(n.indexOf(c)):null}),files:g,charts:n.length,charted:n.map(c=>!!c.querySelector("[data-doc-chart]"))}},import:({uploads:r,scratch:u})=>B(r,u),fill:r=>H(r)};N.on("message",async({id:r,type:u,payload:f})=>{try{M(),N.postMessage({id:r,result:await dt[u](f)})}catch(d){N.postMessage({id:r,error:{...lt(d),stack:d.stack}})}});
|
package/kit/math.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/render/kit/page-katex.js
|
|
4
4
|
var page_katex_default = globalThis.katex;
|
|
5
5
|
|
|
6
|
-
// node_modules/katex/contrib/mhchem/mhchem.js
|
|
6
|
+
// ../../../node_modules/katex/contrib/mhchem/mhchem.js
|
|
7
7
|
page_katex_default.__defineMacro("\\ce", function(context) {
|
|
8
8
|
return chemParse(context.consumeArgs(1)[0], "ce");
|
|
9
9
|
});
|
package/kit/runtime.js
CHANGED
|
@@ -1392,6 +1392,17 @@ ${indent}${list2 ? "]" : "}"}`;
|
|
|
1392
1392
|
};
|
|
1393
1393
|
}
|
|
1394
1394
|
|
|
1395
|
+
// src/shared/workspace-address.mjs
|
|
1396
|
+
var PROJECT = /^(\/~(ws-[0-9a-f]{6}))(?=\/|$)/;
|
|
1397
|
+
var projectOfPath = (pathname) => PROJECT.exec(pathname)?.[2] ?? null;
|
|
1398
|
+
var localPath = (pathname) => pathname.replace(PROJECT, "") || "/";
|
|
1399
|
+
var currentProjectBase = () => PROJECT.exec(globalThis.location?.pathname ?? "")?.[1] ?? "";
|
|
1400
|
+
function workspaceAddress(address, base = currentProjectBase()) {
|
|
1401
|
+
if (!base || typeof address !== "string" || projectOfPath(address)) return address;
|
|
1402
|
+
if (/^\/__gallery\/(?:edit\/(?:v\/[a-f0-9]{16}\/)?src\/|components\/)/.test(address)) return address;
|
|
1403
|
+
return /^\/(?:__gallery\/|generated\/|pages\/)/.test(address) ? base + address : address;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1395
1406
|
// src/shared/page-links.mjs
|
|
1396
1407
|
var DOC_SLUG = /^doc-([a-z][a-z0-9]{7})$/;
|
|
1397
1408
|
function hashId(slug) {
|
|
@@ -1454,7 +1465,7 @@ ${indent}${list2 ? "]" : "}"}`;
|
|
|
1454
1465
|
if (inFile) return known(inFile[1], decoded(inFile[2] ?? ""));
|
|
1455
1466
|
const url = ownAddress(address);
|
|
1456
1467
|
if (!url) return null;
|
|
1457
|
-
const path = PAGE3.exec(url.pathname) ?? OWN_FILE.exec(url.pathname);
|
|
1468
|
+
const path = PAGE3.exec(localPath(url.pathname)) ?? OWN_FILE.exec(localPath(url.pathname));
|
|
1458
1469
|
if (path) return known(path[1], decoded(url.hash.slice(1)));
|
|
1459
1470
|
return url.pathname === "/" && url.hash.length > 1 ? hashAddress(decoded(url.hash.slice(1)), links) : null;
|
|
1460
1471
|
}
|
|
@@ -1957,7 +1968,7 @@ ${indent}${list2 ? "]" : "}"}`;
|
|
|
1957
1968
|
const detail = page ? { page } : { slug, block };
|
|
1958
1969
|
const asked = new CustomEvent(OPEN_EVENT, { cancelable: true, detail });
|
|
1959
1970
|
if (!globalThis.dispatchEvent(asked)) return;
|
|
1960
|
-
globalThis.location.assign(page ? `/pages/${page}` : `/generated/${slug}.html${block ? `#${encodeURIComponent(block)}` : ""}`);
|
|
1971
|
+
globalThis.location.assign(workspaceAddress(page ? `/pages/${page}` : `/generated/${slug}.html${block ? `#${encodeURIComponent(block)}` : ""}`));
|
|
1961
1972
|
}
|
|
1962
1973
|
};
|
|
1963
1974
|
if (place !== "export") {
|
package/kit/state.js
CHANGED
package/kit/ui.js
CHANGED
|
@@ -133,6 +133,10 @@ ${source}
|
|
|
133
133
|
var GENERATORS = [GENERATOR, "HTML Report Studio"];
|
|
134
134
|
var OWN_METAS = new RegExp(`<head\\b[^>]*>\\s*<meta name="generator" content="(?:${GENERATORS.join("|")})">\\s*<meta http-equiv="Content-Security-Policy" content="[^"]*">\\s*<meta name="${SOURCE_META}" content="sha256:([0-9a-f]{64})">(?:\\s*<meta name="${PACKAGES_META}" content="([^"]*)">)?`, "i");
|
|
135
135
|
|
|
136
|
+
// src/shared/workspace-address.mjs
|
|
137
|
+
var PROJECT = /^(\/~(ws-[0-9a-f]{6}))(?=\/|$)/;
|
|
138
|
+
var localPath = (pathname) => pathname.replace(PROJECT, "") || "/";
|
|
139
|
+
|
|
136
140
|
// src/shared/page-links.mjs
|
|
137
141
|
var DOC_SLUG = /^doc-([a-z][a-z0-9]{7})$/;
|
|
138
142
|
function hashId(slug) {
|
|
@@ -196,7 +200,7 @@ ${source}
|
|
|
196
200
|
if (inFile) return known(inFile[1], decoded(inFile[2] ?? ""));
|
|
197
201
|
const url = ownAddress(address);
|
|
198
202
|
if (!url) return null;
|
|
199
|
-
const path = PAGE.exec(url.pathname) ?? OWN_FILE.exec(url.pathname);
|
|
203
|
+
const path = PAGE.exec(localPath(url.pathname)) ?? OWN_FILE.exec(localPath(url.pathname));
|
|
200
204
|
if (path) return known(path[1], decoded(url.hash.slice(1)));
|
|
201
205
|
return url.pathname === "/" && url.hash.length > 1 ? hashAddress(decoded(url.hash.slice(1)), links) : null;
|
|
202
206
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heycys/gallery",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Gallery: documents written as HTML by an AI agent, shown and edited in the browser, delivered as one offline file. The gallery command and its local server.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gallery",
|
package/release.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heycys/gallery",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"built": "2026-11-
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"built": "2026-11-11T06:57:17.901Z",
|
|
5
5
|
"stamps": {
|
|
6
|
-
"editor": "
|
|
6
|
+
"editor": "df4e5d28db0fe187",
|
|
7
7
|
"kit": {
|
|
8
8
|
"styles": "fd4d4ec8d57ef8d1",
|
|
9
9
|
"katex-styles": "81d0a0b4dcb41846",
|
|
10
|
-
"runtime": "
|
|
10
|
+
"runtime": "5b8464b256e0e7ed",
|
|
11
11
|
"code": "f893c8b464096161",
|
|
12
|
-
"state": "
|
|
12
|
+
"state": "81377b50855ccd0f",
|
|
13
13
|
"sheets": "fda7b7b754e01019",
|
|
14
14
|
"tokens": "a218d5d5f9008530",
|
|
15
|
-
"ui": "
|
|
15
|
+
"ui": "1666564002ba1acc",
|
|
16
16
|
"dialogs": "42866e832540bf9f",
|
|
17
17
|
"katex": "1b0a973a02ab84f0",
|
|
18
18
|
"katex-auto-render": "63e7162ddec186d6",
|
|
19
|
-
"math": "
|
|
19
|
+
"math": "af1582414dca108c",
|
|
20
20
|
"d3": "9f5a21b6070f8353",
|
|
21
21
|
"charts": "22bde4e5900579fe",
|
|
22
22
|
"tables": "f776dc063389a916",
|
package/skill/SKILL.md
CHANGED
|
@@ -9,16 +9,16 @@ Create a self-contained HTML document whose claims, numbers, and visual encoding
|
|
|
9
9
|
|
|
10
10
|
## Before anything: the gallery command
|
|
11
11
|
|
|
12
|
-
This skill holds the instructions; the tools are the npm package `@heycys/gallery`, the `gallery` command. This skill is Gallery 0.2.
|
|
12
|
+
This skill holds the instructions; the tools are the npm package `@heycys/gallery`, the `gallery` command. This skill is Gallery 0.2.3's and needs `@heycys/gallery` ^0.2.3: a version npm's range `^0.2.3` takes, 0.2.3 or a later 0.2.x.
|
|
13
13
|
|
|
14
14
|
1. Run `gallery --version`. Its first line is the version: when it is such a version, go on to step 3. A line after it may say that something is newer; that never stops the task: say it once (the handoff will do) and go on, and update nothing without the user's yes, unless they already said in this conversation to update.
|
|
15
15
|
- `技能:<folder> …`: the Gallery skill there (this one, maybe) is older than the gallery. Tell the user so and offer to run the command the line gives (`gallery skill install`, with `--project` for a project's skill; run by `npx`, the line gives `npx -y @heycys/gallery@^<version> skill install`); after their yes, run it and tell them a new Agent session reads the new skill. Finish this task with the skill as it is.
|
|
16
16
|
- `新版本:…`: npm has a newer Gallery. Tell the user once in the conversation and offer to update; after their yes, run the line's `npm i -g …`, then `gallery skill install`, and tell them a new Agent session reads the new skill. Where you run Gallery by `npx` (no global install), the line gives `npx -y @heycys/gallery@^<its version> skill install` instead: run that after their yes, and tell them the next commands can take the newer range: `npx -y @heycys/gallery@^<its version> …`.
|
|
17
|
-
2. When `gallery` is not found, or prints a version older than `^0.2.
|
|
18
|
-
- When it prints a version newer than `^0.2.
|
|
17
|
+
2. When `gallery` is not found, or prints a version older than `^0.2.3` takes, install it with `npm i -g @heycys/gallery@^0.2.3`, then run `gallery --version` again. When the user has already said to install it (in this request or earlier in the conversation), run it without asking again; otherwise ask the user before you install anything: say that Gallery's command is missing (or which version it is) and that you would run that command, and run it once they agree.
|
|
18
|
+
- When it prints a version newer than `^0.2.3` takes, install nothing (that would take Gallery back to an older version): this skill is older than their Gallery. Tell the user and offer to run `gallery skill install` (with `--project` when the skill is the project's, in its `.claude/skills/`; a `技能:` line gives it); after their yes, run it and ask them to start a new Agent session, which reads the new skill.
|
|
19
19
|
- No Node, or `node --version` older than 22.13: tell the user to install the current LTS from https://nodejs.org (or with their system's package manager), then start again.
|
|
20
|
-
- npm says `EACCES` (macOS and Linux, where npm's global folder belongs to the system): never use `sudo`. Tell the user, and offer either of these: give npm a folder of their own (`npm config set prefix ~/.npm-global`, add `~/.npm-global/bin` to `PATH` in their shell profile, open a new terminal, install again); or run every command as `npx -y @heycys/gallery@^0.2.
|
|
21
|
-
- Use `npx -y @heycys/gallery@^0.2.
|
|
20
|
+
- npm says `EACCES` (macOS and Linux, where npm's global folder belongs to the system): never use `sudo`. Tell the user, and offer either of these: give npm a folder of their own (`npm config set prefix ~/.npm-global`, add `~/.npm-global/bin` to `PATH` in their shell profile, open a new terminal, install again); or run every command as `npx -y @heycys/gallery@^0.2.3 <command> …` instead of `gallery <command> …`.
|
|
21
|
+
- Use `npx -y @heycys/gallery@^0.2.3 <command> …` likewise wherever a global install is not allowed (a sandbox, CI). It is slower: a few seconds more per command.
|
|
22
22
|
- On Windows, `gallery` works in PowerShell right after the install, in the same terminal. Only when PowerShell says running scripts is disabled (`gallery.ps1`): run `gallery.cmd` with the same arguments, or ask the user to run `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` once.
|
|
23
23
|
- After an upgrade, run `gallery open --no-browser` once: the server of the version before stops by itself, and this starts the new one. Tell the user that `gallery skill install` brings this skill to the new version too (an Agent session started after it reads it).
|
|
24
24
|
3. The documents live in a workspace, a folder: `--workspace <dir>` (every command takes it), else `GALLERY_WORKSPACE`, else the nearest folder up from the current one holding `gallery.json`, else `Gallery` in the user's Documents folder, `~/Documents/Gallery` (made on first use; on Windows the Documents folder Explorer shows, OneDrive's when OneDrive backs it up). `gallery new`, `gallery import`, `gallery list` and `gallery open` print it as `工作区:<path>`. When a command prints `Cannot write the workspace <path> (…)` (a sandbox that allows writes only in the project, or a system that has not let the terminal into Documents), ask the user to allow Gallery to write there, or pass `--workspace <a folder in the project>` to every command.
|
|
@@ -27,7 +27,7 @@ This skill holds the instructions; the tools are the npm package `@heycys/galler
|
|
|
27
27
|
4. The browser service: `gallery new` and `gallery import` start the workspace's local server (or use the one running) and open the user's browser at the document, `gallery open [<slug>]` starts or reuses it and opens the user's browser (each takes `--no-browser`: the address without a tab), and each prints the address on its `交给用户` line: give it to the user all the same (the browser opened may not be Chrome or Edge, or not in front of them). Editing in the browser and `gallery qa` need Chrome or Edge. In a cloud sandbox the user cannot reach (`127.0.0.1` there is not their computer), skip the address: hand over the exported file instead. `gallery stop` stops the server (`Stopped the server on port …`, or `No Gallery server runs for <workspace>.`).
|
|
28
28
|
5. Every command but `gallery serve` returns by itself, the server's included (it runs on after the command; `gallery serve` runs it in the foreground, for the user's own terminal: you start it with `gallery open --no-browser`): run each in the foreground, with no `timeout` wrapper, background job or `Start-Process`. When you ask the user to allow commands, `gallery *` covers them all (`gallery.cmd *` in PowerShell). In Windows PowerShell 5.1, give a command no `2>&1` (it makes its warnings errors) and read files with the file tool or `Get-Content -Encoding UTF8`. Resolve `references/` and `sdk/` from the folder holding this `SKILL.md`.
|
|
29
29
|
|
|
30
|
-
**写作偏好** (`<
|
|
30
|
+
**写作偏好** (`<home>/preferences.md`) holds the user's rules for writing a document: what to write and in what order, the opening summary, titles, fidelity to the materials, evidence and data. Read it before you write and follow it. When instructions differ, the user's request comes first, then 写作偏好, then this skill. Only the user changes 写作偏好: never edit it unless asked, and never write `"instructions": true` in a document's `doc.json` (it marks the page holding 写作偏好). Where 写作偏好 says `report.json`, a document made by Gallery 0.1 or later has `doc.json`; an older one keeps `report.json`.
|
|
31
31
|
|
|
32
32
|
A document has one writer at a time: never let two Agents, or an Agent and a script, write the same `doc.html` at once. The user may edit it in the browser meanwhile; Gallery merges their edits with yours.
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ A document is the folder `<workspace>/<slug>/`: `doc.html`, the page you write,
|
|
|
35
35
|
|
|
36
36
|
## Workflow
|
|
37
37
|
|
|
38
|
-
1. Read 写作偏好 before anything else (for a document: a custom page has none, [Custom pages](#custom-pages-自定义页)). When `<
|
|
38
|
+
1. Read 写作偏好 before anything else (for a document: a custom page has none, [Custom pages](#custom-pages-自定义页)). When `<home>/preferences.md` is not there yet, `gallery new` (step 2) makes it from Gallery's defaults and prints its path (`gallery import` and `gallery list` print it too; `gallery open` does not): read it then, before you write. When `gallery new` prints `写作偏好:空`, the user emptied it: write from the request, this skill and the materials, and leave the file as it is.
|
|
39
39
|
2. Create the document as soon as you know its title: `gallery new <slug> "<title>" "<description>"` (a lowercase kebab-case slug).
|
|
40
40
|
- Its place in the sidebar's tree: [Organizing documents](#organizing-documents) (`--under` or `--after` where the user says).
|
|
41
41
|
- It prints where it wrote, the workspace, the path of 写作偏好 and the next steps, starts the browser service (or uses the one running), and ends with its `交给用户` line: the address where the user watches and edits the document (step 3). The line before it, the page alone, is yours.
|
|
@@ -108,7 +108,7 @@ Paths are absolute, as this system writes them: use them as they are.
|
|
|
108
108
|
|
|
109
109
|
When the user names a document by its title or topic, find it with `gallery list <words>` (words of its title or topic; those whose title, description or slug holds them come first, then those whose text does, with `正文:` and the first place; `gallery list` alone lists every document, the one edited last first): it prints each document's slug, title, description, place in the tree, address, last edit and file. A search of the `doc.html` files themselves (grep) is fine too. An address the user gives (`http://127.0.0.1:<port>/#<title words>-<page id>`, the workbench's address bar or 复制块链接, `/<block id>` after it for a block) names its document by the id at its end, and the words before it may be out of date: `gallery list <address>` prints that document, or says it is in the trash, and every command that takes a slug takes the address, or the link after its `#`, as well. The id comes from the folder's name, so an address from another workspace can name this workspace's folder of that name: before you change anything, compare the title `gallery list <address>` prints, and its `工作区:` line, with what the user means. A path the user gives (the workbench's 复制本地路径 copies one) is that document's `doc.html`; its folder's name is its slug, the folder holding it the workspace (pass it as `--workspace` when it is not the one the commands find).
|
|
110
110
|
|
|
111
|
-
Before you change anything, run `gallery open <slug> --no-browser` and give the user the address on its `交给用户` line, even when they say the page is open: it reuses the running server without opening another tab, and it keeps the document as it stands so that the first `gallery qa` pictures what you change. It prints no `写作偏好` line: read `<
|
|
111
|
+
Before you change anything, run `gallery open <slug> --no-browser` and give the user the address on its `交给用户` line, even when they say the page is open: it reuses the running server without opening another tab, and it keeps the document as it stands so that the first `gallery qa` pictures what you change. It prints no `写作偏好` line: read `<home>/preferences.md` before you change anything.
|
|
112
112
|
|
|
113
113
|
A long `doc.html` may not fit one read: map it first with a search of its headings and ids (`<h2`, `<h3`, `data-block-id`, with line numbers), then read the part you change by its lines. Right before each change, re-read the part you change and replace only it; never rewrite the whole file from an earlier read or regenerate it with a script. A script may read the file at that moment, replace several exact spans, each found once in it, and write it once (its read is the re-read); it stops without writing when a span is missing or repeated. Write it for Node (`node`), which Gallery runs on and so is always there: `python` may be missing, or a stub that runs nothing and says nothing (Windows' app alias). Keep the start tag of each block you change: its `data-block-id`, `style` and classes may be the user's edits.
|
|
114
114
|
|
|
@@ -132,8 +132,8 @@ The workbench's sidebar shows the documents as a tree: a document may sit under
|
|
|
132
132
|
- A new document goes where the user says: `gallery new <slug> "<title>" "<description>" --under <document>` (last under it; `--first`, first) or `--after <document>` (right after it, at its level). When they name no place, run `gallery list --tree` first when the workspace has documents, and put it under the document whose topic it belongs to (a week's notes under their project); at the top level when none fits. Say in the handoff where it went.
|
|
133
133
|
- Move documents only when the user asks you to organize them: `gallery move <document> --under <document>` (or `--top`), `--after <document>` or `--first` for its place there, each with the documents under it. No file changes, and links between documents still work. Name every move in the handoff.
|
|
134
134
|
- The sidebar lists custom pages (自定义页) too, among 新建 and 搜索 in the order the user drags them to: pages, not documents ([Custom pages](#custom-pages-自定义页)). A page's slug and a document's are one set: `gallery new` refuses a page's, `gallery page new` a document's.
|
|
135
|
-
-
|
|
136
|
-
-
|
|
135
|
+
- One Gallery service per user data directory serves all registered document folders. Their documents, manual links, order and hierarchy share the default workspace’s tree; files stay with their owner. Reorder within a folder; crossing folders confirms a copy, including descendants. Extensions, pages, blocks and writing preferences belong only to the default workspace. For extension tasks, run `gallery extension list` and read the listed AGENT.md first.
|
|
136
|
+
- Web links: `gallery link add <url> "<name>"` (`--column`, `--under`, `--after`, `--first` choose the place); `gallery link set` edits, `gallery link remove` removes. `gallery list --tree` shows them. Links have no check/export/search; Gallery’s own server URLs are refused (link the document). Report their place (`位置:`) in the handoff.
|
|
137
137
|
- Change a document's title, page icon or description with `gallery set <document> --title "…" --icon <emoji, or none> --description "…"` (any of them): it writes the header of `doc.html` and `doc.json` in step, and the open page and the sidebar show it. Never edit `doc.json`'s title or icon, or the header, by hand for that. Re-read `doc.html` after it before you edit it.
|
|
138
138
|
|
|
139
139
|
## Custom pages (自定义页)
|
|
@@ -154,7 +154,7 @@ A custom page is the user's own page in the workbench's sidebar, yours to design
|
|
|
154
154
|
|
|
155
155
|
## Output contract
|
|
156
156
|
|
|
157
|
-
- `<workspace>/<slug>/doc.html` and `doc.json` are the editable sources; what Gallery keeps of its own is under `<workspace>/.gallery/`, never yours to edit; `<
|
|
157
|
+
- `<workspace>/<slug>/doc.html` and `doc.json` are the editable sources; what Gallery keeps of its own is under `<workspace>/.gallery/`, never yours to edit; `<home>/components/` holds custom blocks (自定义块: their files keep the word component, `component.json` and `data-doc-component`), no document, and is no slug a new document can take; `<home>/pages/` holds the custom pages ([Custom pages](#custom-pages-自定义页)), each with its own files in its `assets/`; `<workspace>/exports/<slug>.html` is the offline, single-file deliverable. Re-export after the last source edit. In the handoff the clickable HTML is the export or a verified byte-identical copy of it, never a source or a page of the local server.
|
|
158
158
|
- Everything document-specific (data, visuals, styles, scripts) stays in `doc.html` and its `assets/`. Gallery's shared kit (its blocks, styles and `sdk/` types) is not yours to change; the workspace's custom blocks are. A document uses the kit's blocks and whole blocks. A document's own script cannot write the file: a control that must keep what a reader enters (a ledger, a checklist, a form) is a custom block's data part (`gallery block new <id> --data`), though the user does not say to use it again. Otherwise make a custom block only when the user clearly wants a block to use again, never one per document: run `gallery block list` first (Gallery's own blocks among what it lists, each added as it ships by `gallery block add <name>`), then read [references/building-blocks.md](references/building-blocks.md) (a block with a script: [references/block-script.md](references/block-script.md); one that reads the workspace, a table or the web: [references/pages-and-data.md](references/pages-and-data.md)). `gallery component …` is another name for `gallery block …`. When the user wants a document that uses it too, create that document first, even when they say to make the block first: Workflow steps 1–3 (`gallery new`, the address sent), then its skeleton with the heading of the section the instance goes in. Then make the block, and write the instance into its section last. An instance of a block `gallery block list` gives under 文档里可用 is written as `gallery block show <id>` prints.
|
|
159
159
|
|
|
160
160
|
## Handoff
|
|
@@ -173,12 +173,12 @@ Before you hand over, do the review 写作偏好 asks for before handing in (a r
|
|
|
173
173
|
|
|
174
174
|
| Need | Read |
|
|
175
175
|
| --- | --- |
|
|
176
|
-
| The user's rules for writing: what to write, the opening summary, titles, fidelity, evidence and data | 写作偏好, `<
|
|
176
|
+
| The user's rules for writing: what to write, the opening summary, titles, fidelity, evidence and data | 写作偏好, `<home>/preferences.md` |
|
|
177
177
|
| How a block is written | [references/example-report.html](references/example-report.html) |
|
|
178
178
|
| The rules behind the blocks: the page, what plain HTML becomes, figures and charts you draw yourself | [references/writing-html.md](references/writing-html.md) |
|
|
179
179
|
| A custom block (a block to use again), when the user asks for one: the block, its flow, files, manifest and instance; sharing a block pack | [references/building-blocks.md](references/building-blocks.md) |
|
|
180
180
|
| A block's script (`kit.js`) and how it writes back to the file | [references/block-script.md](references/block-script.md) |
|
|
181
181
|
| A card of a custom page (size, settings), and a block that reads the workspace's data, a table of its document or the web | [references/pages-and-data.md](references/pages-and-data.md) |
|
|
182
|
-
|
|
|
182
|
+
| Sidebar columns or workbench/backend extensions (`gallery extension new/install/list/check/remove`), when requested | [references/extensions.md](references/extensions.md) |
|
|
183
183
|
| Exact API types | `sdk/*.d.ts`, from [`sdk/index.d.ts`](sdk/index.d.ts) |
|
|
184
184
|
| What each command does and prints | `gallery help`, `gallery <command> --help` |
|
|
@@ -41,7 +41,7 @@ One block, one manifest and one `context` stand in either place; the two differ
|
|
|
41
41
|
When the user wants a document that uses the block too, the document comes first: `gallery new`, its address sent, its skeleton written (SKILL.md, Workflow steps 1–4). Then make the block as below, write the instance in its section at step 5, and try it there at step 6.
|
|
42
42
|
|
|
43
43
|
1. `gallery block list` first: what the workspace has now, in two groups by where a block may stand (文档里可用, in documents; 自定义页可用, on custom pages), each with its version, whether the menus can insert it, and what is there and cannot be used, with why; then Gallery's own blocks it has not added yet. When one does what the user asks, use it: `gallery block show <id>` prints its parts, how to write it and its example (`gallery block show gallery/<name>` does so for one of Gallery's own the workspace has not added yet, and ends with the line that adds it; `show` and `check` take its bare name too, `gallery block show heatmap`, as `add` does, a block of the workspace's own by that name staying `local/`'s). When one of Gallery's own does, `gallery block add <name>` puts it in as it ships, and it needs no check and no try (Gallery's own blocks, below).
|
|
44
|
-
2. `gallery block new <id>`: a new block in `<
|
|
44
|
+
2. `gallery block new <id>`: a new block in `<home>/components/<scope>/<name>/`, its `id` being `<scope>/<name>`, the scope `local` when you name none: the default pack (默认块包, first in 设置 › 组件列表), where a single block goes, with no pack of its own to make. It writes a block that passes the check, for you to replace. Add `--script` only when the block must do something when a reader uses it: a static block runs no code, is the one to start with, and is all most blocks need. Add `--flow` when it holds blocks of the document's own under its markup (a card around any content): a slot, `body` (Slots, below). Add `--data` for a block that keeps what a reader enters in the file, the usual reason to make one (a list of lines, a field that adds one, a button that takes one away): a data part `items` (JSON on one line, as `context.editJSON` writes it), a `kit.js` that reads it with `context.readJSON` and writes it with `context.editJSON`, a field (`DocKit.input`) and buttons hidden where nothing is written, and an `example()` that adds a line, which works on `template.html`'s empty instance as on `example.html`'s: rename the part and its fields for what the block keeps, and keep that shape. Add `--page` for a custom page's card (its places `["page"]`, a `card` of two sizes). `--data` and `--page` go together or alone, and either goes with `--script`; neither goes with `--flow` (a page has no slot, and `--data` is a part of its own), nor with `--recipe <name>`, which starts from one of Gallery's own (below). Each scaffold passes `gallery block check`, and `gallery block try` on its field (`--type .component-field "words" --then Enter`), as it is.
|
|
45
45
|
3. Write its files (below): the manifest, `template.html`, `example.html`, its style and, with `--script`, `kit.js`, then what a writer needs to know.
|
|
46
46
|
4. `gallery block check <id>`, until it prints `OK`. It says each problem at its file and line; one with no line is of that file, or of the page, as a whole. Then open `overview-light.png` and `overview-dark.png` in the folder it prints, each the pictures of 360, 736 and 1024px wide of a theme side by side, `example.html` above the line and `template.html` below it, then at full size each width where they show something to fix, and `edit-1024-light.png` and `edit-360-light.png`, as the editing page draws it, `context.canEdit()` true: what the block draws only where a reader can edit (a handle, a button), which an export leaves out. A block that draws nothing more there gives the export's pictures again, and the check says so. It has run them and looked at nothing for you: a `Not opened yet: …` line right before the verdict lists every picture of the run in one place, as `gallery qa`'s does: count by it. In the handoff name the pictures you opened. A block with a data part whose export (`1024-light.png`, where `context.canEdit()` is false) still shows an enabled button, field, select or textarea gets a note: a control that writes is drawn hidden there, which you see by comparing that picture with `edit-1024-light.png`.
|
|
47
47
|
5. Use it where the user asked: in a document, write the element as `example.html` does (when the user wants no document, in one of its own made with `gallery new`, named in the handoff: step 6 tries the block there). The user finds it in "/" under its name. `gallery export <slug>` exports it as any block, and `gallery qa <slug>` pictures it among the cards. Around an instance, the document's words say what the block is and where its data comes from. A value that a reader's gesture changes (a count by column, a status, a score, a total) goes in the document's words only with when it held ("截至 11 月 5 日,…"), as an opening summary may need it; otherwise point to the block, which shows it as it is now. How to use the block on the editing page (drag a card, press a dot) goes in the handoff and in writing.md, not in the document: its export and PDF show the block read-only. Then `gallery block check <id> --documents`: it runs each instance the workspace's documents hold as step 4 runs the example, a section for each document, each problem at its line of the document's file (`board/doc.html:15`), the pictures in `documents/<slug>/` of the folder it prints, so that an instance filled otherwise than the example (a long title, an empty table) is tried as the reader's page runs it. It writes no document.
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
# Extensions:
|
|
1
|
+
# Extensions: sidebar columns, workbench scripts and backends
|
|
2
2
|
|
|
3
3
|
An extension gives the sidebar a column of its own, beside the workspace's own column and those of the other workspaces: a list of links, nested if you like, that the user opens in the content area. Make one only when the user asks for it ("put my usual links in the sidebar", "an entry for my team's wiki"), or when a tool you are building must show entries of its own. A single web page the user wants in the sidebar is not an extension: `gallery link add <url> "<name>"` is.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Only when the user explicitly wants a project-specific extension, add `--local` (and `--workspace <path>` to select the project if needed). The folder may hold whatever else the extension needs (a small server, its code); Gallery reads nothing else there.
|
|
8
|
-
|
|
9
|
-
Every workbench shows the default workspace's extensions together with its project's own extensions. A project `sidebar.json` with the same `<id>` takes precedence in that project; an empty project folder does not hide the public column. Existing project files are not moved. Public titles and root order are shared: editing them from any workbench writes the same public file, and other open workbenches refresh even if the default workspace has no running server. The column's position among other columns and its folded state remain each workbench's own.
|
|
5
|
+
All extensions live in the default workspace’s `extensions/<id>/`. `sidebar.json` supplies an optional column; `extension.json` independently supplies optional workbench scripts, styles, a backend and Agent instructions. Project-local extension folders are not read. `gallery extension new <id> "<title>"` creates an empty sidebar.json and prints its path. Read and edit that file; the command never overwrites it. Column order and hierarchy are stored in the shared tree.
|
|
10
6
|
|
|
11
7
|
- `<id>` is the folder's name: lowercase letters, digits and dashes (`yuque`, `team-wiki`), 48 characters at most, never `ws-` followed by six hex digits.
|
|
12
|
-
- Run `gallery extension list` first: it lists the visible extensions and their actual file paths, so you change one rather than make a second. `gallery extension check <id>`
|
|
8
|
+
- Run `gallery extension list` first: it lists the visible extensions and their actual file paths, so you change one rather than make a second. `gallery extension check <id>` reads that same default-workspace source. Read the reported path rather than constructing a path under the current project.
|
|
13
9
|
|
|
14
10
|
## The file
|
|
15
11
|
|
|
@@ -49,15 +45,55 @@ The file is at most 1 MB. Fields it does not know are ignored.
|
|
|
49
45
|
|
|
50
46
|
## What the user sees
|
|
51
47
|
|
|
52
|
-
- The column sits among the sidebar's columns. The user can fold it and drag its heading to another place. They can reorder its top-level entries by dragging before or after another root; Gallery writes the new `entries` order here, keeping each whole subtree. They cannot change nesting, drag entries into or out of it, rename entries
|
|
48
|
+
- The column sits among the sidebar's columns. The user can fold it and drag its heading to another place. They can reorder its top-level entries by dragging before or after another root; Gallery writes the new `entries` order here, keeping each whole subtree. They can delete a top-level entry and its whole subtree, or delete the column, after confirmation. Root groups without a URL have this menu too; nested entries do not offer deletion. Deleting a column removes only its sidebar.json, keeping the extension’s other files. Columns update in every open Gallery tab. These operations do not delete the original web documents and cannot be undone. They cannot change nesting, drag entries into or out of it, or rename entries. Hovering the column name shows this file’s path.
|
|
53
49
|
- A click on an entry with a `url` shows that page in the content area, the way a web link is shown, filling the content area without a top bar. The right-click menu has 在浏览器中打开, 复制网址 and 在新标签页打开. A site that does not let other pages frame it gets a card with 在浏览器中打开 instead. Pages on this computer under another port (your own server) work; Gallery's own address does not.
|
|
54
50
|
- The address bar holds `#~<id>/<entry id>`, so an entry can be opened again from a link.
|
|
55
51
|
- A file that does not read shows the column with one row, `条目读不出来:<why>`, and no entries.
|
|
56
52
|
|
|
57
53
|
## Changing it
|
|
58
54
|
|
|
59
|
-
Read the latest file before changing it: the user may have renamed its column
|
|
55
|
+
Read the latest file before changing it: the user may have renamed its column, reordered its roots or deleted entries in Gallery. Keep those changes unless asked to replace them; if sidebar.json has been deleted, do not recreate it unless the user asks. Write the file again: Gallery reads it again within a second of every change, the sidebar updates without a reload, and an entry the user has open stays open while its `id` is still in the file. When it is gone from the file, the user is told (这个条目已被扩展移除) and taken to the home page. Write a temporary file in the same folder and rename it over `sidebar.json`, so that no one reads half a file.
|
|
60
56
|
|
|
61
57
|
Check the file before you say it is done: `gallery extension check <id>` reads it as the sidebar does and prints what the sidebar will show, the column and each entry with its id, title and address. It exits 1 with the reason when the file would show `条目读不出来`; `note:` lines are only things it left out (an icon that is neither an emoji nor an icon of the picker). Read its output against what the user asked for: the entries in the order they asked, no entry missing.
|
|
62
58
|
|
|
63
59
|
Do not write `.gallery/outline.json` for an extension: where its column sits is the user's to drag, and Gallery keeps it there.
|
|
60
|
+
|
|
61
|
+
## Installing and enabling executable extensions
|
|
62
|
+
|
|
63
|
+
Run `gallery extension install <folder | .zip>` to validate and install a complete extension in the default workspace. A ZIP may contain files at its root or inside one enclosing folder. The optional manifest `id` names the destination; otherwise the source folder/ZIP basename does. Replacement stages all files first and stops the old backend before swapping folders. Replacement requires fresh confirmation. `gallery extension remove <id>` stops its backend and removes its folder. These commands use the running Gallery service when available.
|
|
64
|
+
|
|
65
|
+
`gallery extension list` prints capabilities and the declared Agent instruction path. When the user’s task relates to an extension, read that AGENT.md before acting. Installing does not enable executable code. The user enables it in Settings > Extensions and confirms its permissions; reopen Gallery to load/unload workbench code. Backends start lazily on the first API request, stop when disabled/removed or Gallery exits, and restart after crashes with exponential backoff.
|
|
66
|
+
|
|
67
|
+
Example `extension.json` (all capability fields optional):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"id": "yuque-publish",
|
|
72
|
+
"workbench": { "script": "workbench.js", "style": "workbench.css" },
|
|
73
|
+
"server": { "command": ["node", "server.mjs"] },
|
|
74
|
+
"agent": "AGENT.md"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Workbench files are served at `/__gallery/extensions/<id>/files/<relative-path>`. Paths cannot leave the extension directory or traverse symlinks. Scripts load once as modules, only in the workbench. The API is available before scripts load:
|
|
79
|
+
|
|
80
|
+
- `window.gallery.slot('topbar-end')`: a stable container before the favorite button (also available on custom pages). No topbar is added to online webpages.
|
|
81
|
+
- `window.gallery.slot('sidebar-footer')`: a stable container above Settings.
|
|
82
|
+
- `window.gallery.onRoute(callback)`: immediately reports the current route, then changes; returns an unsubscribe function. Value: `{kind, ws, slug, link, title}`. `slug` is the owner-local folder name, `ws` the owner’s registry ID; `link` is the workbench URL.
|
|
83
|
+
- `await window.gallery.token()`: token for authenticated API requests.
|
|
84
|
+
|
|
85
|
+
Use Gallery’s CSS variables: `--texPri`, `--texSec`, `--bacPri`, `--sidBac`, `--bacIntTra`, `--btnPriBg`, `--btnPriTex` (see the workbench theme for the full set). Do not select internal DOM class names. Slots retain their elements as pages change.
|
|
86
|
+
|
|
87
|
+
Backend commands run without a shell, with cwd set to the extension folder and the service’s environment and system permissions. `node` uses Gallery’s Node executable. Listen on `GALLERY_EXTENSION_HOST` (127.0.0.1) and `GALLERY_EXTENSION_PORT` (also `PORT`); `GALLERY_EXTENSION_ID` identifies the extension. Logs are `<data>/logs/extensions/<id>.log`. This internal port is managed by Gallery; users always use the one public Gallery address.
|
|
88
|
+
|
|
89
|
+
Gallery proxies `/__gallery/extensions/<id>/api/*` to the backend’s `/*`. Every request requires the Gallery token and same-origin browser context. Browser example:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
const response = await fetch('/__gallery/extensions/yuque-publish/api/publish', {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: { 'Content-Type': 'application/json', 'X-Gallery-Token': await window.gallery.token() },
|
|
95
|
+
body: JSON.stringify({ link: location.href })
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The browser supplies Origin for POST. For GET/HEAD, Gallery also accepts the same-origin Referer when Origin is absent; do not attempt to set these browser-controlled headers manually. Command-line clients pass both Origin and token. No CORS configuration is needed.
|