@heycys/gallery 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/THIRD-PARTY-NOTICES.md +1 -1
  2. package/browser/editor/components/icon/page.mjs +6 -4
  3. package/browser/editor/components/icons.mjs +2 -2
  4. package/browser/editor/page/clipboard/import/read.mjs +20 -2
  5. package/browser/shared/document-copy.mjs +43 -0
  6. package/browser/shared/drafts.mjs +10 -3
  7. package/browser/shared/emoji/icon-value.mjs +2 -2
  8. package/browser/shared/extensions.mjs +83 -7
  9. package/browser/shared/i18n/en.mjs +10 -0
  10. package/browser/shared/outline-shape.mjs +6 -0
  11. package/browser/shared/outline.mjs +135 -10
  12. package/browser/shared/workspace-columns.mjs +11 -7
  13. package/dist/{chunk-QE5RG76S.mjs → chunk-2GKGEVMR.mjs} +11 -7
  14. package/dist/{chunk-HYMEEXKN.mjs → chunk-2RKIHJI2.mjs} +1 -1
  15. package/dist/{chunk-742D2WWJ.mjs → chunk-36FIRJIF.mjs} +2 -2
  16. package/dist/{chunk-EAIFITAV.mjs → chunk-3A5I2GJK.mjs} +1 -1
  17. package/dist/{chunk-7JDX4E65.mjs → chunk-3OFXMN6N.mjs} +1 -1
  18. package/dist/chunk-46ACXOU5.mjs +2 -0
  19. package/dist/{chunk-PUOQ65NC.mjs → chunk-4AMTKWDO.mjs} +1 -1
  20. package/dist/{chunk-HPOI4ZX7.mjs → chunk-4DNHHJSL.mjs} +1 -1
  21. package/dist/chunk-5DPABPBI.mjs +2 -0
  22. package/dist/{chunk-ALZ6ZENZ.mjs → chunk-5IIH335D.mjs} +1 -1
  23. package/dist/{chunk-EP25HDAJ.mjs → chunk-5L26JUYA.mjs} +1 -1
  24. package/dist/{chunk-FDMAXMQA.mjs → chunk-5MEXZ2UF.mjs} +1 -1
  25. package/dist/{chunk-ZHEHPMBK.mjs → chunk-6MPKCH3P.mjs} +1 -1
  26. package/dist/{chunk-C564NU2H.mjs → chunk-74RPP2M2.mjs} +1 -1
  27. package/dist/{chunk-YNAWBAYN.mjs → chunk-7M7UPWU6.mjs} +1 -1
  28. package/dist/{chunk-YROX2O22.mjs → chunk-7N2LPG33.mjs} +1 -1
  29. package/dist/{chunk-VS7JIGJW.mjs → chunk-7XDYK3UC.mjs} +1 -1
  30. package/dist/chunk-AEMVZYZQ.mjs +166 -0
  31. package/dist/{chunk-TR7TWGHB.mjs → chunk-AFGUR7SY.mjs} +1 -1
  32. package/dist/{chunk-K5XATEOO.mjs → chunk-AMDGVDNV.mjs} +1 -1
  33. package/dist/{chunk-JA7G53PC.mjs → chunk-ASAXY2DJ.mjs} +1 -1
  34. package/dist/{chunk-W7665ZEC.mjs → chunk-BYSF5UMM.mjs} +2 -2
  35. package/dist/{chunk-U3SSPL4K.mjs → chunk-CCSGFCLZ.mjs} +1 -1
  36. package/dist/{chunk-JLTZF4LA.mjs → chunk-CG3UHDKH.mjs} +1 -1
  37. package/dist/{chunk-YLPSMDIS.mjs → chunk-CPS4SRPF.mjs} +2 -2
  38. package/dist/{chunk-AHN3NFKJ.mjs → chunk-CZ5KJSGK.mjs} +1 -1
  39. package/dist/chunk-DAMVDXR3.mjs +2 -0
  40. package/dist/{chunk-WQDQWCX5.mjs → chunk-DH6GMDNN.mjs} +1 -1
  41. package/dist/{chunk-U5XL7D6A.mjs → chunk-DHB7UZ7F.mjs} +1 -1
  42. package/dist/{chunk-OEXWRJME.mjs → chunk-DKR7LFIY.mjs} +1 -1
  43. package/dist/chunk-DLFZJJEE.mjs +2 -0
  44. package/dist/{chunk-DJISHPUL.mjs → chunk-FCK6NXON.mjs} +1 -1
  45. package/dist/{chunk-IV64FFI4.mjs → chunk-FX42XU6J.mjs} +1 -1
  46. package/dist/{chunk-FETSREUN.mjs → chunk-FXQBADMN.mjs} +1 -1
  47. package/dist/{chunk-BFJGHSQN.mjs → chunk-G4YP7GVC.mjs} +1 -1
  48. package/dist/{chunk-V6XZ4VIR.mjs → chunk-GWOYMENX.mjs} +1 -1
  49. package/dist/chunk-HATA2W7X.mjs +2 -0
  50. package/dist/{chunk-YI43RG4P.mjs → chunk-HBAUKKQH.mjs} +1 -1
  51. package/dist/{chunk-MAFIGVXY.mjs → chunk-HM4E43VM.mjs} +1 -1
  52. package/dist/{chunk-OFHXGWOX.mjs → chunk-JI6T3A4Y.mjs} +1 -1
  53. package/dist/{chunk-5XA2ZBAE.mjs → chunk-JJNRIFND.mjs} +2 -2
  54. package/dist/chunk-JL6R4FG4.mjs +4 -0
  55. package/dist/{chunk-UMHUSOPJ.mjs → chunk-JXLSHVJ2.mjs} +1 -1
  56. package/dist/{chunk-VFX2KNHP.mjs → chunk-KFZBYDIS.mjs} +1 -1
  57. package/dist/{chunk-ZTHICSSF.mjs → chunk-KH24LWAT.mjs} +1 -1
  58. package/dist/{chunk-GHOYKMQ3.mjs → chunk-KUILYI7H.mjs} +1 -1
  59. package/dist/{chunk-5K44HLTZ.mjs → chunk-L7W624FD.mjs} +1 -1
  60. package/dist/{chunk-J5PUIV3Y.mjs → chunk-LE44MD3O.mjs} +1 -1
  61. package/dist/{chunk-4K5KC6V2.mjs → chunk-LV24RY2E.mjs} +1 -1
  62. package/dist/{chunk-43B75J5T.mjs → chunk-MKTX5PGV.mjs} +1 -1
  63. package/dist/{chunk-LKX55H76.mjs → chunk-NAGMRVW4.mjs} +1 -1
  64. package/dist/{chunk-DAZL73VK.mjs → chunk-NFABH3QW.mjs} +2 -2
  65. package/dist/{chunk-U7NZI6N4.mjs → chunk-NIW7F7LU.mjs} +2 -2
  66. package/dist/{chunk-PAREWQGB.mjs → chunk-NWRZXELO.mjs} +1 -1
  67. package/dist/{chunk-ZWWC4KLF.mjs → chunk-OU3S466R.mjs} +1 -1
  68. package/dist/{chunk-MN7BZL4R.mjs → chunk-PAFSFVYB.mjs} +1 -1
  69. package/dist/chunk-QCEATQC4.mjs +2 -0
  70. package/dist/{chunk-Y34NR5JL.mjs → chunk-QJ3TFJLQ.mjs} +1 -1
  71. package/dist/{chunk-IA4PSIMD.mjs → chunk-QK3VISCA.mjs} +1 -1
  72. package/dist/{chunk-7OG26PGF.mjs → chunk-QXVNUASA.mjs} +1 -1
  73. package/dist/{chunk-GRAXKKG4.mjs → chunk-QZ35UAB4.mjs} +1 -1
  74. package/dist/{chunk-AR5D57ZD.mjs → chunk-S2Y7VI4H.mjs} +2 -2
  75. package/dist/{chunk-HXG7EOPJ.mjs → chunk-SAHS56QM.mjs} +1 -1
  76. package/dist/{chunk-3MBRL2PM.mjs → chunk-SOD23RGL.mjs} +1 -1
  77. package/dist/chunk-TDWJKOLN.mjs +2 -0
  78. package/dist/{chunk-ITOH5EGO.mjs → chunk-TOE6DPLH.mjs} +1 -1
  79. package/dist/chunk-UGO5N3EI.mjs +15 -0
  80. package/dist/{chunk-4OKWZY5E.mjs → chunk-V5JRSGZF.mjs} +1 -1
  81. package/dist/{chunk-BZ5RAF7L.mjs → chunk-VA3KAQZD.mjs} +1 -1
  82. package/dist/{chunk-HDWTZ763.mjs → chunk-VG5JEIUQ.mjs} +1 -1
  83. package/dist/{chunk-POTA2FV4.mjs → chunk-VGTMLQWZ.mjs} +1 -1
  84. package/dist/{chunk-UYVCQK3H.mjs → chunk-VKSRM7EY.mjs} +1 -1
  85. package/dist/{chunk-ZDLPUOAG.mjs → chunk-VNUWREXU.mjs} +1 -1
  86. package/dist/{chunk-AGDILNWB.mjs → chunk-VPVOAJ7X.mjs} +1 -1
  87. package/dist/{chunk-CMA72MAG.mjs → chunk-WCWYNFDF.mjs} +1 -1
  88. package/dist/{chunk-ZQS6H473.mjs → chunk-WTEGUPFG.mjs} +1 -1
  89. package/dist/{chunk-R2ZDLJYT.mjs → chunk-XKOHV4H6.mjs} +1 -1
  90. package/dist/{chunk-MXUBKDRO.mjs → chunk-XMKHCUI6.mjs} +1 -1
  91. package/dist/{chunk-67N2QJYT.mjs → chunk-XQRUG4G3.mjs} +1 -1
  92. package/dist/{chunk-PGDE24N5.mjs → chunk-YBB5LWZK.mjs} +1 -1
  93. package/dist/{chunk-6N3PX4XX.mjs → chunk-YFJU3O7G.mjs} +1 -1
  94. package/dist/chunk-YPPJVDQI.mjs +2 -0
  95. package/dist/chunk-ZANMKAWS.mjs +2 -0
  96. package/dist/{chunk-6QBHU4DF.mjs → chunk-ZCP7BTOZ.mjs} +1 -1
  97. package/dist/{chunk-ERLAIBOA.mjs → chunk-ZEY2RLBT.mjs} +2 -2
  98. package/dist/{chunk-SQ6Y2ZMA.mjs → chunk-ZO7SFB2E.mjs} +2 -2
  99. package/dist/cli.mjs +1 -1
  100. package/dist/server.mjs +1 -1
  101. package/dist/worker.mjs +1 -1
  102. package/kit/math.js +1 -1
  103. package/kit/report.css +3 -3
  104. package/package.json +1 -1
  105. package/release.json +5 -5
  106. package/skill/SKILL.md +8 -7
  107. package/skill/references/extensions.md +11 -7
  108. package/web/assets/index-BHC5ZOMh.js +15 -0
  109. package/web/assets/index-xP0uw3a0.css +1 -0
  110. package/web/index.html +2 -2
  111. package/dist/chunk-6KBYRA2C.mjs +0 -2
  112. package/dist/chunk-F6HVDZR4.mjs +0 -2
  113. package/dist/chunk-J43Z6LSS.mjs +0 -2
  114. package/dist/chunk-JZT7I2OJ.mjs +0 -2
  115. package/dist/chunk-L2SUIS5S.mjs +0 -2
  116. package/dist/chunk-L7UP52JO.mjs +0 -2
  117. package/dist/chunk-LM4XFQHZ.mjs +0 -2
  118. package/dist/chunk-MTYYHM2G.mjs +0 -15
  119. package/dist/chunk-NAMN3RAF.mjs +0 -2
  120. package/dist/chunk-ORID3AE7.mjs +0 -166
  121. package/dist/chunk-SWUTTZCN.mjs +0 -2
  122. package/dist/chunk-UJQJKNGK.mjs +0 -2
  123. package/web/assets/index-BM_K-Auu.css +0 -1
  124. package/web/assets/index-DRslOBiv.js +0 -15
  125. /package/dist/{chunk-YEY3RSFQ.mjs → chunk-5MMN3N5J.mjs} +0 -0
  126. /package/dist/{chunk-UH4SIXTR.mjs → chunk-74UW7QSL.mjs} +0 -0
  127. /package/dist/{chunk-W7XHE4CT.mjs → chunk-EKGYYOSP.mjs} +0 -0
  128. /package/dist/{chunk-QAIHRH3I.mjs → chunk-KNZJT4ET.mjs} +0 -0
  129. /package/dist/{chunk-A55HN5OK.mjs → chunk-N3DSBH5S.mjs} +0 -0
  130. /package/dist/{chunk-QR6A54KJ.mjs → chunk-ZW2A6GBW.mjs} +0 -0
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-GHOYKMQ3.mjs";import"./chunk-QE5RG76S.mjs";import"./chunk-5L7RYZCL.mjs";export{e as main,d as notOurs,b as readCommandLine,c as unwritable,a as versionOf};
2
+ import{a,b,c,d,e}from"./chunk-KUILYI7H.mjs";import"./chunk-2GKGEVMR.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-ORID3AE7.mjs";import"./chunk-DR73Y2UJ.mjs";import"./chunk-JLTZF4LA.mjs";import"./chunk-PUOQ65NC.mjs";import"./chunk-7OG26PGF.mjs";import"./chunk-DJISHPUL.mjs";import"./chunk-ZWWC4KLF.mjs";import"./chunk-AHN3NFKJ.mjs";import"./chunk-MTYYHM2G.mjs";import"./chunk-ALZ6ZENZ.mjs";import"./chunk-IA4PSIMD.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-ITOH5EGO.mjs";import"./chunk-ZQS6H473.mjs";import"./chunk-UYVCQK3H.mjs";import"./chunk-J5PUIV3Y.mjs";import"./chunk-MXUBKDRO.mjs";import"./chunk-HXG7EOPJ.mjs";import"./chunk-ZTHICSSF.mjs";import"./chunk-6N3PX4XX.mjs";import"./chunk-PAREWQGB.mjs";import"./chunk-BFJGHSQN.mjs";import"./chunk-U5XL7D6A.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-R2ZDLJYT.mjs";import"./chunk-VFX2KNHP.mjs";import"./chunk-VS7JIGJW.mjs";import"./chunk-HDWTZ763.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)}
2
+ import{a as o}from"./chunk-AEMVZYZQ.mjs";import"./chunk-DR73Y2UJ.mjs";import"./chunk-CG3UHDKH.mjs";import"./chunk-4AMTKWDO.mjs";import"./chunk-QXVNUASA.mjs";import"./chunk-FCK6NXON.mjs";import"./chunk-OU3S466R.mjs";import"./chunk-CZ5KJSGK.mjs";import"./chunk-UGO5N3EI.mjs";import"./chunk-5IIH335D.mjs";import"./chunk-QK3VISCA.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-TOE6DPLH.mjs";import"./chunk-WTEGUPFG.mjs";import"./chunk-VKSRM7EY.mjs";import"./chunk-LE44MD3O.mjs";import"./chunk-XMKHCUI6.mjs";import"./chunk-SAHS56QM.mjs";import"./chunk-KH24LWAT.mjs";import"./chunk-YFJU3O7G.mjs";import"./chunk-NWRZXELO.mjs";import"./chunk-G4YP7GVC.mjs";import"./chunk-DHB7UZ7F.mjs";import"./chunk-2RKIHJI2.mjs";import"./chunk-JXLSHVJ2.mjs";import"./chunk-XKOHV4H6.mjs";import"./chunk-KFZBYDIS.mjs";import"./chunk-7XDYK3UC.mjs";import"./chunk-VG5JEIUQ.mjs";import"./chunk-N3DSBH5S.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-DAZL73VK.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-ITOH5EGO.mjs";import"./chunk-ZQS6H473.mjs";import"./chunk-BFJGHSQN.mjs";import"./chunk-U5XL7D6A.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-R2ZDLJYT.mjs";import"./chunk-VFX2KNHP.mjs";import{c as F}from"./chunk-VS7JIGJW.mjs";import{g as R,i as M}from"./chunk-HDWTZ763.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?`![${at(n.getAttribute("alt")??"")}](${u(i,"image",D(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`
2
+ import{a as P,b as W,c as B,e as H}from"./chunk-NFABH3QW.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-F4ZA6JRZ.mjs";import"./chunk-7M2BQQGZ.mjs";import"./chunk-TOE6DPLH.mjs";import"./chunk-WTEGUPFG.mjs";import"./chunk-G4YP7GVC.mjs";import"./chunk-DHB7UZ7F.mjs";import"./chunk-2RKIHJI2.mjs";import"./chunk-JXLSHVJ2.mjs";import"./chunk-XKOHV4H6.mjs";import"./chunk-KFZBYDIS.mjs";import{c as F}from"./chunk-7XDYK3UC.mjs";import{g as R,i as M}from"./chunk-VG5JEIUQ.mjs";import"./chunk-N3DSBH5S.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?`![${at(n.getAttribute("alt")??"")}](${u(i,"image",D(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/report.css CHANGED
@@ -1193,10 +1193,10 @@ span.doc-link-mention-icon {
1193
1193
  }
1194
1194
 
1195
1195
  /* A page block: the address is a document's. An empty icon is a page, as a page with none has in the
1196
- workbench's tree (Lucide's file-text), and the pointer lights it up as Notion's page mention, a whole to click on. */
1196
+ workbench's tree (Lucide's plain file outline), and the pointer lights it up as Notion's page mention, a whole to click on. */
1197
1197
  .doc-link-mention[href]:not([href^="http" i]) > span.doc-link-mention-icon {
1198
- -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z'/%3E%3Cpath d='M14 2v5a1 1 0 0 0 1 1h5'/%3E%3Cpath d='M10 9H8M16 13H8M16 17H8'/%3E%3C/svg%3E");
1199
- mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z'/%3E%3Cpath d='M14 2v5a1 1 0 0 0 1 1h5'/%3E%3Cpath d='M10 9H8M16 13H8M16 17H8'/%3E%3C/svg%3E");
1198
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z'/%3E%3Cpath d='M14 2v5a1 1 0 0 0 1 1h5'/%3E%3C/svg%3E");
1199
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z'/%3E%3Cpath d='M14 2v5a1 1 0 0 0 1 1h5'/%3E%3C/svg%3E");
1200
1200
  }
1201
1201
  .doc-link-mention[href]:not([href^="http" i]):hover {
1202
1202
  border-radius: 1px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heycys/gallery",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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,11 +1,11 @@
1
1
  {
2
2
  "name": "@heycys/gallery",
3
- "version": "0.2.0",
4
- "built": "2026-11-10T06:00:12.267Z",
3
+ "version": "0.2.1",
4
+ "built": "2026-11-10T18:08:08.621Z",
5
5
  "stamps": {
6
- "editor": "730eb4fe396a3d82",
6
+ "editor": "6391411681dcaf3d",
7
7
  "kit": {
8
- "styles": "90714ca047271da8",
8
+ "styles": "fd4d4ec8d57ef8d1",
9
9
  "katex-styles": "81d0a0b4dcb41846",
10
10
  "runtime": "ccf037942f0fee28",
11
11
  "code": "f893c8b464096161",
@@ -16,7 +16,7 @@
16
16
  "dialogs": "42866e832540bf9f",
17
17
  "katex": "1b0a973a02ab84f0",
18
18
  "katex-auto-render": "63e7162ddec186d6",
19
- "math": "af1582414dca108c",
19
+ "math": "cf768d1151047082",
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.0's and needs `@heycys/gallery` ^0.2.0: a version npm's range `^0.2.0` takes, 0.2.0 or a later 0.2.x.
12
+ This skill holds the instructions; the tools are the npm package `@heycys/gallery`, the `gallery` command. This skill is Gallery 0.2.1's and needs `@heycys/gallery` ^0.2.1: a version npm's range `^0.2.1` takes, 0.2.1 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.0` takes, install it with `npm i -g @heycys/gallery@^0.2.0`, 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.0` 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.
17
+ 2. When `gallery` is not found, or prints a version older than `^0.2.1` takes, install it with `npm i -g @heycys/gallery@^0.2.1`, 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.1` 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.0 <command> …` instead of `gallery <command> …`.
21
- - Use `npx -y @heycys/gallery@^0.2.0 <command> …` likewise wherever a global install is not allowed (a sandbox, CI). It is slower: a few seconds more per command.
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.1 <command> …` instead of `gallery <command> …`.
21
+ - Use `npx -y @heycys/gallery@^0.2.1 <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.
@@ -132,7 +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
- - The sidebar is in columns, one for each workspace of this machine (the default one's is 默认文档), and holds web links beside the documents. A document made or opened by `gallery new` or `gallery open` in another workspace joins the default workspace's sidebar too, as its line `默认工作区(…)的侧边栏:已加进…` says (display only: edit it in its own workspace). To put a web page in the sidebar, use `gallery link add <url> "<name>"` (`--column "<column>"`, `--under`, `--after` or `--first` for its place; `gallery link set` and `gallery link remove` change and take it out): a link is no document, so nothing checks, exports or searches it, and an address of this Gallery's own server is refused (link the document). `gallery list` and `gallery list --tree` show links and columns as the sidebar does. Say in the handoff where a link went (`位置:`).
135
+ - The sidebar shows every registered workspace’s documents and manually added web links, with one shared order and hierarchy in all workbenches. Files stay in their owning workspace; the current project’s column is displayed first. Use the existing `gallery list --tree` and `gallery move` commands for ordering within a workspace. Cross-workspace drag or 移动到 requires confirmation and creates a copy, including descendants; originals remain. Do not move a cross-workspace reference by editing the outline file. Explicit project-only extension columns remain local; public extension columns come from the default workspace. `gallery new` and `gallery open` still report the document’s place in the default workspace’s sidebar; `GALLERY_NO_DEFAULT_TREE=1` suppresses this join/announcement, not registered-workspace visibility.
136
+ - To put a web page in the sidebar, use `gallery link add <url> "<name>"` (`--column "<column>"`, `--under`, `--after` or `--first` for its place; `gallery link set` and `gallery link remove` change and take it out). A link is no document, so nothing checks, exports or searches it; an address of this Gallery's own server is refused (link the document). `gallery list` and `gallery list --tree` show links and columns as the sidebar does. Say in the handoff where a link went (`位置:`).
136
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.
137
138
 
138
139
  ## Custom pages (自定义页)
@@ -178,6 +179,6 @@ Before you hand over, do the review 写作偏好 asks for before handing in (a r
178
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) |
179
180
  | A block's script (`kit.js`) and how it writes back to the file | [references/block-script.md](references/block-script.md) |
180
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) |
181
- | A sidebar column of links an extension provides (`extensions/<id>/sidebar.json`), when the user asks for one | [references/extensions.md](references/extensions.md) |
182
+ | A public sidebar column of links (`gallery extension new`, default workspace’s `extensions/<id>/sidebar.json`; `--local` only for project-specific columns), when the user asks for one | [references/extensions.md](references/extensions.md) |
182
183
  | Exact API types | `sdk/*.d.ts`, from [`sdk/index.d.ts`](sdk/index.d.ts) |
183
184
  | What each command does and prints | `gallery help`, `gallery <command> --help` |
@@ -2,10 +2,14 @@
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
- An extension is a folder in the workspace, `extensions/<id>/`, and the one file Gallery reads in it is `sidebar.json`. The folder may hold whatever else the extension needs (a small server, its code); Gallery reads nothing else there.
5
+ An extension is a folder `extensions/<id>/` in a workspace, and the one file Gallery reads in it is `sidebar.json`. **Create public link columns in the default workspace**, shared by all projects. Use `gallery extension new <id> "<title>"`: it creates an empty file there and prints its absolute path; read that file and fill its `entries`. It never overwrites an existing file. Do not assume the current project is the default workspace.
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.
6
10
 
7
11
  - `<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.
8
- - Run `gallery extension list` first: it lists the extensions that are there, so you change one rather than make a second.
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>` resolves that same public/project source. Read the reported path rather than constructing a path under the current project.
9
13
 
10
14
  ## The file
11
15
 
@@ -33,8 +37,8 @@ An extension is a folder in the workspace, `extensions/<id>/`, and the one file
33
37
 
34
38
  | Field | Rule |
35
39
  | --- | --- |
36
- | `title` | The column's name. Required, 100 characters at most. The user cannot rename it. |
37
- | `icon` | Optional, for the column and for any entry. An emoji, or an icon of Gallery's icon picker (`/icons/<name>_<color>.svg`). Never a picture from the web: nothing is loaded from a web address. An entry with no icon shows the column's. |
40
+ | `title` | The column's name. Required, 100 characters at most. The user can rename it from the column menu; Gallery writes the new title here. |
41
+ | `icon` | Optional, for the column and for any entry. An emoji, or an icon of Gallery's icon picker (`/icons/<name>_<color>.svg`). Never a picture from the web: nothing is loaded from a web address. An entry with no icon shows the column's. With neither specified, a parent with children shows a folder, a leaf shows the ordinary document icon, whether it has a URL or is an empty group. |
38
42
  | `entries` | Required, a list (it may be empty). Each entry is an object. Up to 8 levels deep, 2000 entries in all. |
39
43
  | `id` (entry) | Required, 1 to 128 characters of `A-Z a-z 0-9 . _ : -`, used once in the file, at any level. Keep it the same for as long as the entry is the same entry: the address bar, the open page and the user's place in the sidebar go by it. |
40
44
  | `title` (entry) | Required, 200 characters at most. |
@@ -45,14 +49,14 @@ The file is at most 1 MB. Fields it does not know are ignored.
45
49
 
46
50
  ## What the user sees
47
51
 
48
- - The column sits among the sidebar's columns. The user can fold it and drag its heading to another place. They cannot drag entries into or out of it, rename them or remove them: the entries are yours.
49
- - A click on an entry with a `url` shows that page in the content area, the way a web link is shown, with 在浏览器中打开 in the 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.
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 or remove them. Hovering the column name shows this file’s path.
53
+ - 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.
50
54
  - The address bar holds `#~<id>/<entry id>`, so an entry can be opened again from a link.
51
55
  - A file that does not read shows the column with one row, `条目读不出来:<why>`, and no entries.
52
56
 
53
57
  ## Changing it
54
58
 
55
- 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.
59
+ Read the latest file before changing it: the user may have renamed its column or reordered its roots in Gallery. Keep those changes unless asked to replace them. 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.
56
60
 
57
61
  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.
58
62