diffing 0.18.1 → 0.18.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/dist/{cli-agent-Cf6SAnH4.mjs → cli-agent-DV5ZhMX1.mjs} +209 -25
- package/dist/{cli-gh-2NEumKQB.mjs → cli-gh-0hY7pvwJ.mjs} +2 -2
- package/dist/cli.mjs +13 -12
- package/dist/client/assets/{angular-html-BV3gPi7L.js → angular-html-DgLkj6cM.js} +1 -1
- package/dist/client/assets/{angular-ts-DuuSfKJK.js → angular-ts-S7IdfqOh.js} +1 -1
- package/dist/client/assets/{apl-BMc69egk.js → apl-dC851k0e.js} +1 -1
- package/dist/client/assets/{arc-CEC1cz1S.js → arc-v8u_6qG_.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BmJ5OHgd.js → architectureDiagram-3BPJPVTR-CtfNNxPZ.js} +1 -1
- package/dist/client/assets/{astro-Bg_CYwmB.js → astro-DgnNzfoZ.js} +1 -1
- package/dist/client/assets/{blade-Mca0XoRC.js → blade-By7nZeyV.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM--XrThRLW.js → blockDiagram-GPEHLZMM-cfmYosKW.js} +1 -1
- package/dist/client/assets/{c-B2Uqi6oI.js → c-C6I1CPq_.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-W8w5oiRi.js → c4Diagram-AAUBKEIU-L52oIoFq.js} +1 -1
- package/dist/client/assets/channel-B8Y5aSHX.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-Bpr3MqKf.js → chunk-2J33WTMH-Dpy-98nI.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-DdMV_3DM.js → chunk-3OPIFGDE-DsFmltuQ.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-BkfeLLsz.js → chunk-4BX2VUAB-B1PXeD30.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-pCwXbxR0.js → chunk-55IACEB6-BkfaPY5R.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-BGFzCgvs.js → chunk-5ZQYHXKU-kHj4q0HC.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-DU7oVWfJ.js → chunk-727SXJPM-DYndNEF7.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DaVYG1qm.js → chunk-AQP2D5EJ-CGgu3cJd.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-DAvnzgjY.js → chunk-BSJP7CBP-B85q9YnN.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-D6ZDvMsi.js → chunk-CSCIHK7Q-D4izdgab.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BFDCYCqF.js → chunk-FMBD7UC4-CwH6kGrM.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-BmD6XY52.js → chunk-KSCS5N6A-D6WNG6Qv.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-Dl9iUIBf.js → chunk-L5ZTLDWV-BFzgANjL.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-rOOJDxdB.js → chunk-LZXEDZCA-CXVdeXQA.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-DBDu-JeD.js → chunk-ND2GUHAM-nez7hw5f.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-CRyk4F3R.js → chunk-NZK2D7GU-DQxrGm1r.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-DcKB07iP.js → chunk-O5CBEL6O-BAYDbzO8.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-DPZNzASS.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-DOKX6dPU.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-WuzWMSrp.js → chunk-XPW4576I-DdtioiQK.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CkitIGos.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BD7zcbFa.js +1 -0
- package/dist/client/assets/{cobol-oQ1i-YtO.js → cobol-DVRUz845.js} +1 -1
- package/dist/client/assets/{coffee-s9XlmHbB.js → coffee-ywp-ztcj.js} +1 -1
- package/dist/client/assets/{cose-bilkent-S5V4N54A-CymUm7fn.js → cose-bilkent-S5V4N54A-YAV-XlD-.js} +1 -1
- package/dist/client/assets/{cpp-D60LxTox.js → cpp-lGJg5yP8.js} +1 -1
- package/dist/client/assets/{crystal-CHrCczDb.js → crystal-C255DQNq.js} +1 -1
- package/dist/client/assets/{css-1PQO32Hw.js → css-D0fueLBG.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-DOHZc0tO.js → dagre-BM42HDAG-C-nRRJ_h.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-D8O2cleS.js → diagram-2AECGRRQ-Cy08G2GT.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CO-Ej4UD.js → diagram-5GNKFQAL-B5KxlMfb.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-DCUr4wzS.js → diagram-KO2AKTUF-BtlklsuV.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-D9HnA0Qg.js → diagram-LMA3HP47-DwJhvsVf.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BxAitO2n.js → diagram-OG6HWLK6-e6mkflzE.js} +1 -1
- package/dist/client/assets/{dist-N-ROBnkL.js → dist-8nkFH_K6.js} +1 -1
- package/dist/client/assets/{edge-Ck54Fn7y.js → edge-Cy4Xn6qQ.js} +1 -1
- package/dist/client/assets/{edit-B8RwtrDs.js → edit-C3wvBNC7.js} +1 -1
- package/dist/client/assets/{elixir-xXxdfsfU.js → elixir-CHz66mgQ.js} +1 -1
- package/dist/client/assets/{elm-g0vLRWbF.js → elm-CuSKJX5P.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-1ZMg7g98.js → erDiagram-TEJ5UH35-U7lAAznz.js} +1 -1
- package/dist/client/assets/{erb-CGFnxOST.js → erb-BbtQ1hep.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-D5u9lhJ-.js → flowDiagram-I6XJVG4X-jIylSBs9.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BbVKEzs8.js → ganttDiagram-6RSMTGT7-DW3d8a36.js} +1 -1
- package/dist/client/assets/{git-rebase-B3s6bE5_.js → git-rebase-R-XkmJUY.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-sAottiiz.js → gitGraphDiagram-PVQCEYII-BHzkJu8s.js} +1 -1
- package/dist/client/assets/{glimmer-js-C2Xff18_.js → glimmer-js-C_yhese4.js} +1 -1
- package/dist/client/assets/{glimmer-ts-CRBS8ftd.js → glimmer-ts-DKD-Bn0M.js} +1 -1
- package/dist/client/assets/{glsl-aOY_t6dG.js → glsl-CJScdeHB.js} +1 -1
- package/dist/client/assets/{graphql-Cucpdpc6.js → graphql-BUP7lnbX.js} +1 -1
- package/dist/client/assets/{hack-Bw0KkVza.js → hack-Uu2tSM5_.js} +1 -1
- package/dist/client/assets/{haml-DnyaPoa2.js → haml-BKKDeeb3.js} +1 -1
- package/dist/client/assets/{handlebars-AedEof-m.js → handlebars-DXZJVvuj.js} +1 -1
- package/dist/client/assets/{html-Bu5h_Kgf.js → html-BNpfcyGi.js} +1 -1
- package/dist/client/assets/{html-derivative-CNiVLld1.js → html-derivative-D2Zj9RHU.js} +1 -1
- package/dist/client/assets/{http-B9-oOYPw.js → http-BDjsRgLN.js} +1 -1
- package/dist/client/assets/{hurl-Bo6NUnnd.js → hurl-D_YxHbnH.js} +1 -1
- package/dist/client/assets/{index-BkwV-g6J.js → index-CpN9wq1L.js} +142 -142
- package/dist/client/assets/{index-C73yr1tm.css → index-EoTRSuBL.css} +1 -1
- package/dist/client/assets/{infoDiagram-5YYISTIA-CfVELF4R.js → infoDiagram-5YYISTIA-oGpzG0Af.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DztjIGfb.js → ishikawaDiagram-YF4QCWOH-066RGSaQ.js} +1 -1
- package/dist/client/assets/{java-JJ3dl_jz.js → java-DeOKsdfJ.js} +1 -1
- package/dist/client/assets/{javascript-QgUuQNTw.js → javascript-oM5JhL2b.js} +1 -1
- package/dist/client/assets/{jinja-GbeEm2-u.js → jinja-CpW87uSV.js} +1 -1
- package/dist/client/assets/{jison-BXZbHsO3.js → jison-Du6rTe9r.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-Lg8CkxeI.js → journeyDiagram-JHISSGLW-BB4Aymt4.js} +1 -1
- package/dist/client/assets/{json-D02YvQSa.js → json-Cl5vEJGM.js} +1 -1
- package/dist/client/assets/{jsx-eZde6NZS.js → jsx-D3lCkT_f.js} +1 -1
- package/dist/client/assets/{julia-CfcuUD-A.js → julia-P1bWdzXQ.js} +1 -1
- package/dist/client/assets/{just-hQr8WcQp.js → just-BQHIj4wn.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-BWIeZIf6.js → kanban-definition-UN3LZRKU-CveGMVSw.js} +1 -1
- package/dist/client/assets/{latex-BxffZwv_.js → latex-CzeBoguz.js} +1 -1
- package/dist/client/assets/{line-Cy1AFxf0.js → line-CjLRf0wl.js} +1 -1
- package/dist/client/assets/{linear-Kk5IbGHr.js → linear-GmrB0HMZ.js} +1 -1
- package/dist/client/assets/{liquid-BvKnpUyR.js → liquid-BNh96Tca.js} +1 -1
- package/dist/client/assets/{lua-Bcds2vY9.js → lua-CNQoqqQP.js} +1 -1
- package/dist/client/assets/{marko-CZJQLjYc.js → marko-C_xevFIb.js} +1 -1
- package/dist/client/assets/{mdc-BRigtixg.js → mdc-XPBTv6ak.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-Dh2uTVxt.js → mermaid-parser.core-HsXe3oD9.js} +1 -1
- package/dist/client/assets/{mermaid.core-CXIGVQ74.js → mermaid.core-BukRjtV2.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DQNQJuAB.js → mindmap-definition-RKZ34NQL-VTmEFYO4.js} +1 -1
- package/dist/client/assets/{nginx-BI3-LI9U.js → nginx-C6cZlyey.js} +1 -1
- package/dist/client/assets/{nim-PEd8ZOME.js → nim-Cd9xainG.js} +1 -1
- package/dist/client/assets/{perl-BPTGdP4p.js → perl-DUfKG3XX.js} +1 -1
- package/dist/client/assets/{php-C8BKL0yL.js → php-DqMFPxyM.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-Dz7gE0t5.js → pieDiagram-4H26LBE5-Bwlobw-Z.js} +1 -1
- package/dist/client/assets/{pug-H5z9rtbz.js → pug-GUix1iYH.js} +1 -1
- package/dist/client/assets/{qml-EzSpo2aM.js → qml-CqF34de7.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-pTnmYdEJ.js → quadrantDiagram-W4KKPZXB-CMujQAS2.js} +1 -1
- package/dist/client/assets/{r-VMeUwMIt.js → r-BxeiO8R8.js} +1 -1
- package/dist/client/assets/{razor-Ne05zcaj.js → razor-Cu8elnMU.js} +1 -1
- package/dist/client/assets/{regexp-i5_jUZAj.js → regexp-DxNPsA-x.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-GVw3NOxE.js → requirementDiagram-4Y6WPE33-CrrnxwFz.js} +1 -1
- package/dist/client/assets/{review-export-B4TOUDUE.js → review-export-Bd6rUYIi.js} +1 -1
- package/dist/client/assets/{rst-BZWuei_6.js → rst-2Xzw7kAb.js} +1 -1
- package/dist/client/assets/{ruby-CApICFTD.js → ruby-tZt1Nxb8.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CwWzwmqj.js → sankeyDiagram-5OEKKPKP-ihur8vgn.js} +1 -1
- package/dist/client/assets/{sas-BzEPX6GL.js → sas-DjpUVtXs.js} +1 -1
- package/dist/client/assets/{scss-B6Ld2NRm.js → scss-CZffX3V1.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Bz_Q76Kt.js → sequenceDiagram-3UESZ5HK-Bg7fvZIu.js} +1 -1
- package/dist/client/assets/{shellscript-KmoqGVVu.js → shellscript-B7bmiOyR.js} +1 -1
- package/dist/client/assets/{shellsession-CsSh_KTo.js → shellsession-CS7D1mSi.js} +1 -1
- package/dist/client/assets/{soy-CLgOu1If.js → soy-B9Nii6ks.js} +1 -1
- package/dist/client/assets/{sql-BdCh56kU.js → sql-CzQPwj1z.js} +1 -1
- package/dist/client/assets/{src-D7QQkGEc.js → src-DNsvZq9f.js} +1 -1
- package/dist/client/assets/{stata-C3fo8EYS.js → stata-BjQgQlmT.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-D0pZtzLu.js → stateDiagram-AJRCARHV-DsgTVCaj.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CwRm1eh7.js +1 -0
- package/dist/client/assets/{surrealql-qzihsJw_.js → surrealql-D6DnLnn1.js} +1 -1
- package/dist/client/assets/{svelte-B4tbn_KI.js → svelte-DRgP3jXp.js} +1 -1
- package/dist/client/assets/{templ-Ddev3BvY.js → templ-Cwgx1vj-.js} +1 -1
- package/dist/client/assets/{tex-DJLtgNQ6.js → tex-RzUmM9ei.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CWTWgYi0.js → timeline-definition-PNZ67QCA-rG2Mo1Zq.js} +1 -1
- package/dist/client/assets/{ts-tags-qqxigRtr.js → ts-tags-gdlLe8LD.js} +1 -1
- package/dist/client/assets/{tsx-Pkf9BaPv.js → tsx-C3y6O1l0.js} +1 -1
- package/dist/client/assets/{twig-BiScb10p.js → twig-DLMzUdS6.js} +1 -1
- package/dist/client/assets/{typescript-Bhy3UXig.js → typescript-T8PJwqaO.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DwB89v9I.js → vennDiagram-CIIHVFJN-B-eDjTJG.js} +1 -1
- package/dist/client/assets/{vue-CANMWBRv.js → vue-BMIaruOV.js} +1 -1
- package/dist/client/assets/{vue-html-DtS6f62r.js → vue-html-D4e4SuHW.js} +1 -1
- package/dist/client/assets/{vue-vine-BpQ9kwSB.js → vue-vine-DU2nprLA.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DNwwPk4s.js → wardleyDiagram-YWT4CUSO-BkcHoefB.js} +1 -1
- package/dist/client/assets/{xml-CD5eIXsm.js → xml-DD7circ2.js} +1 -1
- package/dist/client/assets/{xsl-DGNZQAUu.js → xsl-Bu-TQ7Ag.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-L7M4YSvw.js → xychartDiagram-2RQKCTM6-BGa1-yRO.js} +1 -1
- package/dist/client/assets/{yaml-Cm8mwIB7.js → yaml-C12ZIXNt.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/completions-C0ERX2wC.mjs +2 -0
- package/dist/design-system-types-QR9xS7o3.mjs +2 -0
- package/dist/design-system-types-uiP9W2uj.mjs +75 -0
- package/dist/{doctor-BxTaLqAF.mjs → doctor-Dg61wsBc.mjs} +6 -6
- package/dist/{doctor-BdDB6p27.mjs → doctor-Y8l1LEDM.mjs} +1 -1
- package/dist/{github-CToLsvPu.mjs → github-BtABlwOZ.mjs} +3 -3
- package/dist/{github--rpPZ3vK.mjs → github-BuBcy-co.mjs} +1 -1
- package/dist/{github-attachments-DEvAeo5P.mjs → github-attachments-Bwr25ItV.mjs} +1 -1
- package/dist/{mcp-DoNBucix.mjs → mcp-45VPdWIr.mjs} +136 -17
- package/dist/{mockup-format-D4Ln0WxJ.mjs → mockup-lint-BRjhxXSw.mjs} +160 -7
- package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
- package/dist/native/tui-darwin-x64/diffing-tui +0 -0
- package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
- package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
- package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
- package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
- package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
- package/dist/{review-export-DoST2oPt.mjs → review-export-BXCeRNjk.mjs} +1 -1
- package/dist/{server-BRQdb64n.mjs → server-CH-UPGFR.mjs} +1145 -127
- package/dist/{server-lock-CdaUNZyX.mjs → server-lock-ACw2cKDI.mjs} +22 -5
- package/dist/{session-conflict-BVgyyerQ.mjs → session-conflict-oUPEv3YT.mjs} +3 -3
- package/dist/{session-manager-CUqF8hkP.mjs → session-manager-C9KjVkzq.mjs} +2 -2
- package/dist/{session-url-B9FOnF3F.mjs → session-url-CyBpcLAH.mjs} +1 -1
- package/dist/{settings-DkPDB_hS.mjs → settings-DIa-3C8o.mjs} +1 -1
- package/dist/{setup-BwWZ9Fzq.mjs → setup-B6bOeWQl.mjs} +6 -6
- package/dist/{setup-first-run-i1o7DBki.mjs → setup-first-run-CAjtqBal.mjs} +2 -2
- package/dist/{setup-first-run-DyT0PlOZ.mjs → setup-first-run-DPXLAMqa.mjs} +1 -1
- package/extensions/pi/index.ts +128 -60
- package/extensions/pi/skill-heal.ts +73 -0
- package/extensions/pi/tsconfig.json +2 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-C9URWRe4.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-BBNO2-yv.js +0 -1
- package/dist/client/assets/chunk-WU5MYG2G-HP2Om5f9.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BW_ZPHe7.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOd628kk.js +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-P0vTqsyH.js +0 -1
- package/dist/completions-CYa5usLb.mjs +0 -2
- /package/dist/{apply-suggestion-BsR_8FXW.mjs → apply-suggestion-tLsWREyb.mjs} +0 -0
- /package/dist/{comment-format-D4mCqr7T.mjs → comment-format-DS1wOWj_.mjs} +0 -0
- /package/dist/{completions-2aHVTmiJ.mjs → completions-ClqvbDYF.mjs} +0 -0
- /package/dist/{diff-fingerprint-BL5jNHnP.mjs → diff-fingerprint-PnC8Fs1-.mjs} +0 -0
- /package/dist/{find-tui-binary-DGspwyvd.mjs → find-tui-binary-DvADkBkP.mjs} +0 -0
- /package/dist/{handoff-DcShCxWJ.mjs → handoff-B85WLpcN.mjs} +0 -0
- /package/dist/{terminal-BroplOnw.mjs → terminal-Ce7OL1A-.mjs} +0 -0
- /package/dist/{tui-search-bridge-fFKqX4Fi.mjs → tui-search-bridge-N33QmB0q.mjs} +0 -0
- /package/dist/{update-check-ISaw61qS.mjs → update-check-D0yDl7sZ.mjs} +0 -0
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { C as listRepoFiles, D as toSafeRelativePath, E as isSafePath, S as isGitRepo, T as revertHunk, _ as getShowDiff, a as getCommitSeriesSummary, b as getUntrackedFilePathsAsync, d as getMergeStatus, f as getProjectStorageDir, h as getRepoRoot, l as getGitDiffAsync, m as getRepoMetadataAsync, o as getCustomGitDiffAsync, s as getFileContent, u as getHunkHistory, x as gitAddFile, y as getTabSizeForFilesAsync } from "./git-BGj31GX-.mjs";
|
|
2
2
|
import { a as searchFiles, i as searchContent, o as searchSymbols, r as searchAll, s as trackSelection, t as getSearchStatus } from "./search-3R2E5xxr.mjs";
|
|
3
|
-
import { i as loadSettings, s as saveSettings } from "./settings-
|
|
4
|
-
import { a as
|
|
5
|
-
import { t as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { c as writeJsonAtomically, i as loadSettings, s as saveSettings } from "./settings-DIa-3C8o.mjs";
|
|
4
|
+
import { a as formatPlanReview, c as MOCKUP_SCREEN_ID_RE, d as isMockupViewport, f as normalizeMockupViewport, i as extractPlanLines, l as commentViewport, n as lintMockupScreens, o as sectionTitleForLine, p as normalizeSubmitScreens, r as formatMockupReview, s as MOCKUP_MAX_SCREEN_BYTES, u as defaultScreenLabel } from "./mockup-lint-BRjhxXSw.mjs";
|
|
5
|
+
import { a as slugifyDesignId, i as emptyTokens, o as snapshotRevision, r as cloneTokens, s as tokensToCss, t as DEFAULT_DESIGN_SYSTEM_ID } from "./design-system-types-uiP9W2uj.mjs";
|
|
6
|
+
import { t as formatComments } from "./comment-format-DS1wOWj_.mjs";
|
|
7
|
+
import { g as createServerAuthMiddleware, h as buildSessionTokenSetCookieValue, v as injectSessionTokenIntoHtml } from "./server-lock-ACw2cKDI.mjs";
|
|
8
|
+
import { D as setPrReviewThreadResolved, E as resolvedFromSession, O as submitReview, i as deletePrReviewComment, k as updatePrReviewComment, l as fetchExistingCommentsViaGh, p as fetchPrFileContentViaGh, t as buildPrSession, u as fetchExistingReviewsViaGh, w as refreshPrSession } from "./github-BtABlwOZ.mjs";
|
|
9
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { dirname, extname, join, resolve } from "node:path";
|
|
12
|
+
import { dirname, extname, join, relative, resolve } from "node:path";
|
|
12
13
|
import { existsSync, mkdirSync, readFileSync, watch } from "node:fs";
|
|
13
|
-
import { parseArgs } from "node:util";
|
|
14
|
-
import { mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
15
|
-
import { homedir } from "node:os";
|
|
14
|
+
import { parseArgs, promisify } from "node:util";
|
|
15
|
+
import { mkdir, mkdtemp, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
16
|
+
import { homedir, tmpdir } from "node:os";
|
|
16
17
|
import { Hono } from "hono";
|
|
17
18
|
import { streamSSE } from "hono/streaming";
|
|
18
19
|
import { serve } from "@hono/node-server";
|
|
@@ -1446,7 +1447,7 @@ function isReviewCommentSide(value) {
|
|
|
1446
1447
|
function planSourceFilePath(storageDir, planId) {
|
|
1447
1448
|
return join(storageDir, "plan-sources", `${planId}.md`);
|
|
1448
1449
|
}
|
|
1449
|
-
function newId$
|
|
1450
|
+
function newId$2() {
|
|
1450
1451
|
return crypto.randomUUID();
|
|
1451
1452
|
}
|
|
1452
1453
|
/**
|
|
@@ -1512,7 +1513,7 @@ function applyUpsert$1(plans, input, now) {
|
|
|
1512
1513
|
}
|
|
1513
1514
|
}
|
|
1514
1515
|
const plan = {
|
|
1515
|
-
id: input.id || newId$
|
|
1516
|
+
id: input.id || newId$2(),
|
|
1516
1517
|
title: input.title,
|
|
1517
1518
|
body: input.body,
|
|
1518
1519
|
source: input.source,
|
|
@@ -1744,20 +1745,174 @@ var FilePlanStore = class {
|
|
|
1744
1745
|
}
|
|
1745
1746
|
};
|
|
1746
1747
|
//#endregion
|
|
1748
|
+
//#region src/lib/mockup-region.ts
|
|
1749
|
+
/**
|
|
1750
|
+
* Replace the inner HTML of the first `[data-diffing="<region>"]` element.
|
|
1751
|
+
* Used by revise_mockup op=replace-region. Not a general HTML rewriter —
|
|
1752
|
+
* good enough for agent-authored mockup fragments.
|
|
1753
|
+
*/
|
|
1754
|
+
const VOID_TAGS = new Set([
|
|
1755
|
+
"area",
|
|
1756
|
+
"base",
|
|
1757
|
+
"br",
|
|
1758
|
+
"col",
|
|
1759
|
+
"embed",
|
|
1760
|
+
"hr",
|
|
1761
|
+
"img",
|
|
1762
|
+
"input",
|
|
1763
|
+
"link",
|
|
1764
|
+
"meta",
|
|
1765
|
+
"param",
|
|
1766
|
+
"source",
|
|
1767
|
+
"track",
|
|
1768
|
+
"wbr"
|
|
1769
|
+
]);
|
|
1770
|
+
function scanOpenTag(html, at) {
|
|
1771
|
+
if (html[at] !== "<" || html[at + 1] === "/" || html[at + 1] === "!") return null;
|
|
1772
|
+
const nameMatch = /^<([A-Za-z][\w:-]*)/.exec(html.slice(at));
|
|
1773
|
+
if (!nameMatch) return null;
|
|
1774
|
+
let quote = null;
|
|
1775
|
+
for (let i = at + 1; i < html.length; i++) {
|
|
1776
|
+
const ch = html[i];
|
|
1777
|
+
if (quote) {
|
|
1778
|
+
if (ch === quote) quote = null;
|
|
1779
|
+
continue;
|
|
1780
|
+
}
|
|
1781
|
+
if (ch === "\"" || ch === "'") {
|
|
1782
|
+
quote = ch;
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
if (ch === ">") {
|
|
1786
|
+
const selfClosing = html[i - 1] === "/" || VOID_TAGS.has(nameMatch[1].toLowerCase());
|
|
1787
|
+
return {
|
|
1788
|
+
start: at,
|
|
1789
|
+
end: i + 1,
|
|
1790
|
+
name: nameMatch[1],
|
|
1791
|
+
selfClosing
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
return null;
|
|
1796
|
+
}
|
|
1797
|
+
function findOpenTagContaining(html, attrIndex) {
|
|
1798
|
+
let i = attrIndex;
|
|
1799
|
+
while (i >= 0 && html[i] !== "<") i--;
|
|
1800
|
+
if (i < 0) return null;
|
|
1801
|
+
const tag = scanOpenTag(html, i);
|
|
1802
|
+
if (!tag || attrIndex >= tag.end) return null;
|
|
1803
|
+
return tag;
|
|
1804
|
+
}
|
|
1805
|
+
function findMatchingClose(html, afterOpen, tagName) {
|
|
1806
|
+
const lower = tagName.toLowerCase();
|
|
1807
|
+
let depth = 1;
|
|
1808
|
+
let i = afterOpen;
|
|
1809
|
+
while (i < html.length) {
|
|
1810
|
+
if (html[i] !== "<") {
|
|
1811
|
+
i++;
|
|
1812
|
+
continue;
|
|
1813
|
+
}
|
|
1814
|
+
if (html.startsWith("<!--", i)) {
|
|
1815
|
+
const end = html.indexOf("-->", i + 4);
|
|
1816
|
+
i = end === -1 ? html.length : end + 3;
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
const close = /^<\/([A-Za-z][\w:-]*)\s*>/.exec(html.slice(i));
|
|
1820
|
+
if (close) {
|
|
1821
|
+
if (close[1].toLowerCase() === lower) {
|
|
1822
|
+
depth--;
|
|
1823
|
+
if (depth === 0) return i;
|
|
1824
|
+
}
|
|
1825
|
+
i += close[0].length;
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1828
|
+
const open = scanOpenTag(html, i);
|
|
1829
|
+
if (open) {
|
|
1830
|
+
if (!open.selfClosing && open.name.toLowerCase() === lower) depth++;
|
|
1831
|
+
i = open.end;
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
i++;
|
|
1835
|
+
}
|
|
1836
|
+
return null;
|
|
1837
|
+
}
|
|
1838
|
+
const ATTR_RE = /data-diffing\s*=\s*(["'])([^"']*)\1/gi;
|
|
1839
|
+
/** Count how many `data-diffing="<region>"` attributes appear. */
|
|
1840
|
+
function countDataDiffingRegion(html, region) {
|
|
1841
|
+
ATTR_RE.lastIndex = 0;
|
|
1842
|
+
let count = 0;
|
|
1843
|
+
let match;
|
|
1844
|
+
while (match = ATTR_RE.exec(html)) if (match[2] === region) count++;
|
|
1845
|
+
return count;
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Replace inner HTML of the first element whose `data-diffing` equals `region`.
|
|
1849
|
+
* `occurrences` is how many matching attributes existed before the replace.
|
|
1850
|
+
*/
|
|
1851
|
+
function replaceDataDiffingRegion(html, region, replacement) {
|
|
1852
|
+
if (!region) return {
|
|
1853
|
+
ok: false,
|
|
1854
|
+
error: "region is required"
|
|
1855
|
+
};
|
|
1856
|
+
const occurrences = countDataDiffingRegion(html, region);
|
|
1857
|
+
if (occurrences === 0) return {
|
|
1858
|
+
ok: false,
|
|
1859
|
+
error: `Region "${region}" not found`
|
|
1860
|
+
};
|
|
1861
|
+
ATTR_RE.lastIndex = 0;
|
|
1862
|
+
let match;
|
|
1863
|
+
while (match = ATTR_RE.exec(html)) {
|
|
1864
|
+
if (match[2] !== region) continue;
|
|
1865
|
+
const tag = findOpenTagContaining(html, match.index);
|
|
1866
|
+
if (!tag) return {
|
|
1867
|
+
ok: false,
|
|
1868
|
+
error: `Region "${region}" is not on an element`
|
|
1869
|
+
};
|
|
1870
|
+
if (tag.selfClosing) return {
|
|
1871
|
+
ok: false,
|
|
1872
|
+
error: `Region "${region}" is on a void/self-closing <${tag.name}> — give it a wrapper with inner HTML`
|
|
1873
|
+
};
|
|
1874
|
+
const closeStart = findMatchingClose(html, tag.end, tag.name);
|
|
1875
|
+
if (closeStart === null) return {
|
|
1876
|
+
ok: false,
|
|
1877
|
+
error: `Region "${region}" has no matching </${tag.name}>`
|
|
1878
|
+
};
|
|
1879
|
+
return {
|
|
1880
|
+
ok: true,
|
|
1881
|
+
html: html.slice(0, tag.end) + replacement + html.slice(closeStart),
|
|
1882
|
+
occurrences
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
return {
|
|
1886
|
+
ok: false,
|
|
1887
|
+
error: `Region "${region}" not found`
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
//#endregion
|
|
1747
1891
|
//#region src/lib/mockups.ts
|
|
1748
1892
|
function mockupSourceDir(storageDir, mockupId) {
|
|
1749
1893
|
return join(storageDir, "mockup-sources", mockupId);
|
|
1750
1894
|
}
|
|
1751
|
-
function newId() {
|
|
1895
|
+
function newId$1() {
|
|
1752
1896
|
return crypto.randomUUID();
|
|
1753
1897
|
}
|
|
1754
1898
|
function snapshotScreens(screens) {
|
|
1755
1899
|
return screens.map((s) => ({
|
|
1756
1900
|
id: s.id,
|
|
1757
1901
|
label: s.label,
|
|
1758
|
-
html: s.html
|
|
1902
|
+
html: s.html,
|
|
1903
|
+
stateOf: s.stateOf,
|
|
1904
|
+
flow: s.flow
|
|
1759
1905
|
}));
|
|
1760
1906
|
}
|
|
1907
|
+
function snapshotBinding(input) {
|
|
1908
|
+
return {
|
|
1909
|
+
designSystemId: input.designSystemId,
|
|
1910
|
+
designRevision: input.designRevision,
|
|
1911
|
+
mode: input.mode,
|
|
1912
|
+
planId: input.planId,
|
|
1913
|
+
flows: input.flows
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1761
1916
|
function backfillMockup(mockup) {
|
|
1762
1917
|
const currentVersion = mockup.version ?? 1;
|
|
1763
1918
|
const have = Array.isArray(mockup.versions) ? mockup.versions.length : 0;
|
|
@@ -1793,6 +1948,11 @@ function applyUpsert(mockups, input, now) {
|
|
|
1793
1948
|
existing.screens = snapshotScreens(input.screens);
|
|
1794
1949
|
if (input.source !== void 0) existing.source = input.source;
|
|
1795
1950
|
if (input.model !== void 0) existing.model = input.model;
|
|
1951
|
+
if (input.designSystemId !== void 0) existing.designSystemId = input.designSystemId;
|
|
1952
|
+
if (input.designRevision !== void 0) existing.designRevision = input.designRevision;
|
|
1953
|
+
if (input.mode !== void 0) existing.mode = input.mode;
|
|
1954
|
+
if (input.planId !== void 0) existing.planId = input.planId;
|
|
1955
|
+
if (input.flows !== void 0) existing.flows = input.flows;
|
|
1796
1956
|
existing.version += 1;
|
|
1797
1957
|
existing.updatedAt = now;
|
|
1798
1958
|
if (!existing.versions) existing.versions = [];
|
|
@@ -1802,7 +1962,8 @@ function applyUpsert(mockups, input, now) {
|
|
|
1802
1962
|
screens: snapshotScreens(existing.screens),
|
|
1803
1963
|
source: existing.source,
|
|
1804
1964
|
model: existing.model,
|
|
1805
|
-
createdAt: now
|
|
1965
|
+
createdAt: now,
|
|
1966
|
+
...snapshotBinding(existing)
|
|
1806
1967
|
});
|
|
1807
1968
|
existing.decision = "pending";
|
|
1808
1969
|
existing.decisionComment = void 0;
|
|
@@ -1811,7 +1972,7 @@ function applyUpsert(mockups, input, now) {
|
|
|
1811
1972
|
}
|
|
1812
1973
|
}
|
|
1813
1974
|
const mockup = {
|
|
1814
|
-
id: input.id || newId(),
|
|
1975
|
+
id: input.id || newId$1(),
|
|
1815
1976
|
title: input.title,
|
|
1816
1977
|
screens: snapshotScreens(input.screens),
|
|
1817
1978
|
source: input.source,
|
|
@@ -1821,13 +1982,15 @@ function applyUpsert(mockups, input, now) {
|
|
|
1821
1982
|
version: 1,
|
|
1822
1983
|
decision: "pending",
|
|
1823
1984
|
comments: [],
|
|
1985
|
+
...snapshotBinding(input),
|
|
1824
1986
|
versions: [{
|
|
1825
1987
|
version: 1,
|
|
1826
1988
|
title: input.title,
|
|
1827
1989
|
screens: snapshotScreens(input.screens),
|
|
1828
1990
|
source: input.source,
|
|
1829
1991
|
model: input.model,
|
|
1830
|
-
createdAt: now
|
|
1992
|
+
createdAt: now,
|
|
1993
|
+
...snapshotBinding(input)
|
|
1831
1994
|
}]
|
|
1832
1995
|
};
|
|
1833
1996
|
mockups.push(mockup);
|
|
@@ -1851,6 +2014,7 @@ function syncCurrentVersion(mockup) {
|
|
|
1851
2014
|
last.screens = snapshotScreens(mockup.screens);
|
|
1852
2015
|
last.source = mockup.source;
|
|
1853
2016
|
last.model = mockup.model;
|
|
2017
|
+
Object.assign(last, snapshotBinding(mockup));
|
|
1854
2018
|
}
|
|
1855
2019
|
function versionMismatch(expected, current) {
|
|
1856
2020
|
if (expected === void 0) return void 0;
|
|
@@ -1869,7 +2033,8 @@ function bumpMockupVersion(mockup, now) {
|
|
|
1869
2033
|
screens: snapshotScreens(mockup.screens),
|
|
1870
2034
|
source: mockup.source,
|
|
1871
2035
|
model: mockup.model,
|
|
1872
|
-
createdAt: now
|
|
2036
|
+
createdAt: now,
|
|
2037
|
+
...snapshotBinding(mockup)
|
|
1873
2038
|
});
|
|
1874
2039
|
}
|
|
1875
2040
|
function applyScreenUpsert(mockup, screen, now) {
|
|
@@ -1878,10 +2043,14 @@ function applyScreenUpsert(mockup, screen, now) {
|
|
|
1878
2043
|
if (existing) {
|
|
1879
2044
|
existing.html = html;
|
|
1880
2045
|
if (screen.label?.trim()) existing.label = screen.label.trim();
|
|
2046
|
+
if (screen.stateOf !== void 0) existing.stateOf = screen.stateOf;
|
|
2047
|
+
if (screen.flow !== void 0) existing.flow = screen.flow;
|
|
1881
2048
|
} else mockup.screens.push({
|
|
1882
2049
|
id: screen.id,
|
|
1883
2050
|
label: screen.label?.trim() || defaultScreenLabel(screen.id),
|
|
1884
|
-
html
|
|
2051
|
+
html,
|
|
2052
|
+
stateOf: screen.stateOf,
|
|
2053
|
+
flow: screen.flow
|
|
1885
2054
|
});
|
|
1886
2055
|
bumpMockupVersion(mockup, now);
|
|
1887
2056
|
}
|
|
@@ -1892,6 +2061,12 @@ function applyScreenPatch(screen, patch) {
|
|
|
1892
2061
|
screen.html = screen.html.slice(0, idx) + patch.replacement + screen.html.slice(idx + patch.expectedText.length);
|
|
1893
2062
|
return count;
|
|
1894
2063
|
}
|
|
2064
|
+
function applyScreenRegion(screen, patch) {
|
|
2065
|
+
const result = replaceDataDiffingRegion(screen.html, patch.region, patch.replacement);
|
|
2066
|
+
if (!result.ok) return { error: result.error };
|
|
2067
|
+
screen.html = result.html;
|
|
2068
|
+
return { occurrences: result.occurrences };
|
|
2069
|
+
}
|
|
1895
2070
|
/** Validate every op against the mockup before applying any (all-or-nothing). */
|
|
1896
2071
|
function validateThreadBatch(mockup, ops) {
|
|
1897
2072
|
const comments = mockup.comments ?? [];
|
|
@@ -2318,6 +2493,36 @@ var FileMockupStore = class {
|
|
|
2318
2493
|
occurrences
|
|
2319
2494
|
};
|
|
2320
2495
|
}
|
|
2496
|
+
async replaceRegion(id, screenId, patch, opts) {
|
|
2497
|
+
const mockups = await this.getAll();
|
|
2498
|
+
const mockup = mockups.find((m) => m.id === id);
|
|
2499
|
+
if (!mockup) return {
|
|
2500
|
+
mockup: null,
|
|
2501
|
+
error: "Mockup not found"
|
|
2502
|
+
};
|
|
2503
|
+
const vm = versionMismatch(opts?.expectedVersion, mockup.version);
|
|
2504
|
+
if (vm) return {
|
|
2505
|
+
mockup: null,
|
|
2506
|
+
versionMismatch: vm
|
|
2507
|
+
};
|
|
2508
|
+
const screen = mockup.screens.find((s) => s.id === screenId);
|
|
2509
|
+
if (!screen) return {
|
|
2510
|
+
mockup: null,
|
|
2511
|
+
error: `Screen ${screenId} not found`
|
|
2512
|
+
};
|
|
2513
|
+
const applied = applyScreenRegion(screen, patch);
|
|
2514
|
+
if ("error" in applied) return {
|
|
2515
|
+
mockup: null,
|
|
2516
|
+
error: applied.error
|
|
2517
|
+
};
|
|
2518
|
+
bumpMockupVersion(mockup, Date.now());
|
|
2519
|
+
await this.writeSourceMirror(mockup);
|
|
2520
|
+
await this.save(mockups);
|
|
2521
|
+
return {
|
|
2522
|
+
mockup,
|
|
2523
|
+
occurrences: applied.occurrences
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2321
2526
|
async applyThreadBatch(mockupId, ops) {
|
|
2322
2527
|
const mockups = await this.getAll();
|
|
2323
2528
|
const mockup = mockups.find((m) => m.id === mockupId);
|
|
@@ -2594,7 +2799,7 @@ function buildMockupProbeScript(opts = {}) {
|
|
|
2594
2799
|
function payloadFor(el, ev, kindOverride) {
|
|
2595
2800
|
// Nearest named [data-diffing] ancestor only — sections never nest deeper.
|
|
2596
2801
|
const section = el && el.closest ? el.closest('[data-diffing]') : null;
|
|
2597
|
-
const kind = kindOverride || (tool === 'pin' ? 'point' : tool === 'section' && section ? 'section' : 'block');
|
|
2802
|
+
const kind = kindOverride || ((tool === 'pin' || tool === 'point') ? 'point' : tool === 'section' && section ? 'section' : 'block');
|
|
2598
2803
|
const hitEl = deepestBlock(el);
|
|
2599
2804
|
const outlineEl = kind === 'section' && section ? section : hitEl;
|
|
2600
2805
|
const pt = ev ? pointPct(ev) : { x: 0, y: 0 };
|
|
@@ -2705,7 +2910,7 @@ function buildMockupProbeScript(opts = {}) {
|
|
|
2705
2910
|
|
|
2706
2911
|
shield.addEventListener('mousemove', (ev) => {
|
|
2707
2912
|
const target = targetAt(ev);
|
|
2708
|
-
if (tool === 'pin') {
|
|
2913
|
+
if (tool === 'pin' || tool === 'point') {
|
|
2709
2914
|
post('hover', Object.assign(payloadFor(target, ev, 'point'), { kind: 'point' }));
|
|
2710
2915
|
return;
|
|
2711
2916
|
}
|
|
@@ -2734,7 +2939,7 @@ function buildMockupProbeScript(opts = {}) {
|
|
|
2734
2939
|
const dy = ev.clientY - drag.y;
|
|
2735
2940
|
const dist = Math.hypot(dx, dy);
|
|
2736
2941
|
drag = null;
|
|
2737
|
-
if (tool === 'pin') {
|
|
2942
|
+
if (tool === 'pin' || tool === 'point') {
|
|
2738
2943
|
post('click', payloadFor(target, ev, 'point'));
|
|
2739
2944
|
return;
|
|
2740
2945
|
}
|
|
@@ -2754,11 +2959,18 @@ function buildMockupProbeScript(opts = {}) {
|
|
|
2754
2959
|
ev.preventDefault();
|
|
2755
2960
|
ev.stopPropagation();
|
|
2756
2961
|
}, true);
|
|
2757
|
-
post('ready', { sections: sections() });
|
|
2962
|
+
post('ready', { sections: sections(), height: docHeight() });
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
function docHeight() {
|
|
2966
|
+
return Math.max(
|
|
2967
|
+
document.documentElement.scrollHeight,
|
|
2968
|
+
document.body ? document.body.scrollHeight : 0,
|
|
2969
|
+
);
|
|
2758
2970
|
}
|
|
2759
2971
|
|
|
2760
2972
|
function announce() {
|
|
2761
|
-
post('ready', { sections: sections() });
|
|
2973
|
+
post('ready', { sections: sections(), height: docHeight() });
|
|
2762
2974
|
}
|
|
2763
2975
|
|
|
2764
2976
|
const boot = PASSIVE ? announce : installShield;
|
|
@@ -2767,6 +2979,14 @@ function buildMockupProbeScript(opts = {}) {
|
|
|
2767
2979
|
} else {
|
|
2768
2980
|
boot();
|
|
2769
2981
|
}
|
|
2982
|
+
// Keep the parent's frame height in sync after late-loading media and on
|
|
2983
|
+
// any reflow (the frame is sized to the full document so nothing is cropped).
|
|
2984
|
+
window.addEventListener('load', function () {
|
|
2985
|
+
post('height', { height: docHeight() });
|
|
2986
|
+
});
|
|
2987
|
+
window.addEventListener('resize', function () {
|
|
2988
|
+
post('height', { height: docHeight() });
|
|
2989
|
+
});
|
|
2770
2990
|
})();`;
|
|
2771
2991
|
}
|
|
2772
2992
|
buildMockupProbeScript();
|
|
@@ -2786,98 +3006,641 @@ function injectMockupProbe(html, opts = {}) {
|
|
|
2786
3006
|
return script + html;
|
|
2787
3007
|
}
|
|
2788
3008
|
//#endregion
|
|
2789
|
-
//#region src/lib/
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
{
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
}
|
|
2799
|
-
{
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
{
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
re: /role\s*=\s*["']menu(?:bar)?["']/i
|
|
2834
|
-
},
|
|
2835
|
-
{
|
|
2836
|
-
label: "dropdown",
|
|
2837
|
-
re: /data-(?:bs-)?toggle\s*=\s*["']dropdown["']/i
|
|
2838
|
-
},
|
|
2839
|
-
{
|
|
2840
|
-
label: "dropdown",
|
|
2841
|
-
re: /class\s*=\s*["'][^"']*\bdropdown\b/i
|
|
2842
|
-
},
|
|
2843
|
-
{
|
|
2844
|
-
label: "toggle",
|
|
2845
|
-
re: /role\s*=\s*["']switch["']/i
|
|
2846
|
-
},
|
|
2847
|
-
{
|
|
2848
|
-
label: "toggle",
|
|
2849
|
-
re: /class\s*=\s*["'][^"']*\b(?:toggle|switch)\b/i
|
|
3009
|
+
//#region src/lib/design-system.ts
|
|
3010
|
+
function newId() {
|
|
3011
|
+
return crypto.randomUUID();
|
|
3012
|
+
}
|
|
3013
|
+
function backfill(system) {
|
|
3014
|
+
if (!system.tokens) system.tokens = emptyTokens();
|
|
3015
|
+
if (!system.tokens.raw) system.tokens.raw = {};
|
|
3016
|
+
if (!system.tokens.color) system.tokens.color = {};
|
|
3017
|
+
if (!system.tokens.font) system.tokens.font = {};
|
|
3018
|
+
if (!system.tokens.radius) system.tokens.radius = {};
|
|
3019
|
+
if (!system.tokens.shadow) system.tokens.shadow = {};
|
|
3020
|
+
if (!system.tokens.space) system.tokens.space = {
|
|
3021
|
+
unit: 4,
|
|
3022
|
+
scale: [
|
|
3023
|
+
4,
|
|
3024
|
+
8,
|
|
3025
|
+
12,
|
|
3026
|
+
16,
|
|
3027
|
+
24,
|
|
3028
|
+
32,
|
|
3029
|
+
48
|
|
3030
|
+
]
|
|
3031
|
+
};
|
|
3032
|
+
if (!system.components) system.components = [];
|
|
3033
|
+
if (!system.sources) system.sources = [];
|
|
3034
|
+
if (!system.comments) system.comments = [];
|
|
3035
|
+
if (!system.revisions) system.revisions = [];
|
|
3036
|
+
if (!system.guidelines) system.guidelines = "";
|
|
3037
|
+
if (!system.status) system.status = system.revision > 0 ? "published" : "draft";
|
|
3038
|
+
}
|
|
3039
|
+
function applyFields(system, input, now) {
|
|
3040
|
+
if (input.title?.trim()) system.title = input.title.trim();
|
|
3041
|
+
if (input.tokens) system.tokens = cloneTokens(input.tokens);
|
|
3042
|
+
if (input.guidelines !== void 0) system.guidelines = input.guidelines;
|
|
3043
|
+
if (input.components) system.components = input.components.map((c) => ({ ...c }));
|
|
3044
|
+
if (input.sources) system.sources = input.sources.map((s) => ({ ...s }));
|
|
3045
|
+
if (input.status) system.status = input.status;
|
|
3046
|
+
system.updatedAt = now;
|
|
3047
|
+
}
|
|
3048
|
+
var InMemoryDesignSystemStore = class {
|
|
3049
|
+
systems = [];
|
|
3050
|
+
async getAll() {
|
|
3051
|
+
for (const s of this.systems) backfill(s);
|
|
3052
|
+
return this.systems;
|
|
2850
3053
|
}
|
|
3054
|
+
async get(id) {
|
|
3055
|
+
const system = this.systems.find((s) => s.id === id) ?? null;
|
|
3056
|
+
if (system) backfill(system);
|
|
3057
|
+
return system;
|
|
3058
|
+
}
|
|
3059
|
+
async getDefault() {
|
|
3060
|
+
const published = this.systems.find((s) => s.id === "default" && s.status === "published");
|
|
3061
|
+
if (published) {
|
|
3062
|
+
backfill(published);
|
|
3063
|
+
return published;
|
|
3064
|
+
}
|
|
3065
|
+
const any = this.systems.find((s) => s.status === "published") ?? this.systems[0];
|
|
3066
|
+
if (any) backfill(any);
|
|
3067
|
+
return any ?? null;
|
|
3068
|
+
}
|
|
3069
|
+
async upsert(input) {
|
|
3070
|
+
const now = Date.now();
|
|
3071
|
+
const id = slugifyDesignId(input.id ?? "default") ?? "default";
|
|
3072
|
+
const existing = this.systems.find((s) => s.id === id);
|
|
3073
|
+
if (existing) {
|
|
3074
|
+
applyFields(existing, input, now);
|
|
3075
|
+
return existing;
|
|
3076
|
+
}
|
|
3077
|
+
const system = {
|
|
3078
|
+
id,
|
|
3079
|
+
title: input.title?.trim() || "Default",
|
|
3080
|
+
revision: 0,
|
|
3081
|
+
status: input.status ?? "draft",
|
|
3082
|
+
tokens: input.tokens ? cloneTokens(input.tokens) : emptyTokens(),
|
|
3083
|
+
guidelines: input.guidelines ?? "",
|
|
3084
|
+
components: (input.components ?? []).map((c) => ({ ...c })),
|
|
3085
|
+
sources: (input.sources ?? []).map((s) => ({ ...s })),
|
|
3086
|
+
createdAt: now,
|
|
3087
|
+
updatedAt: now,
|
|
3088
|
+
revisions: [],
|
|
3089
|
+
comments: []
|
|
3090
|
+
};
|
|
3091
|
+
this.systems.push(system);
|
|
3092
|
+
return system;
|
|
3093
|
+
}
|
|
3094
|
+
async propose(id, input) {
|
|
3095
|
+
const system = await this.get(id);
|
|
3096
|
+
if (!system) return null;
|
|
3097
|
+
applyFields(system, input, Date.now());
|
|
3098
|
+
system.status = "draft";
|
|
3099
|
+
return system;
|
|
3100
|
+
}
|
|
3101
|
+
async publish(id) {
|
|
3102
|
+
const system = await this.get(id);
|
|
3103
|
+
if (!system) return null;
|
|
3104
|
+
const now = Date.now();
|
|
3105
|
+
system.revision += 1;
|
|
3106
|
+
system.status = "published";
|
|
3107
|
+
system.updatedAt = now;
|
|
3108
|
+
system.publishedAt = now;
|
|
3109
|
+
system.revisions.push(snapshotRevision(system));
|
|
3110
|
+
return system;
|
|
3111
|
+
}
|
|
3112
|
+
async remove(id) {
|
|
3113
|
+
const idx = this.systems.findIndex((s) => s.id === id);
|
|
3114
|
+
if (idx === -1) return false;
|
|
3115
|
+
this.systems.splice(idx, 1);
|
|
3116
|
+
return true;
|
|
3117
|
+
}
|
|
3118
|
+
async addComponent(id, component) {
|
|
3119
|
+
const system = await this.get(id);
|
|
3120
|
+
if (!system) return null;
|
|
3121
|
+
const slug = slugifyDesignId(component.id);
|
|
3122
|
+
if (!slug) return null;
|
|
3123
|
+
const next = {
|
|
3124
|
+
...component,
|
|
3125
|
+
id: slug,
|
|
3126
|
+
createdAt: component.createdAt ?? Date.now()
|
|
3127
|
+
};
|
|
3128
|
+
const idx = system.components.findIndex((c) => c.id === slug);
|
|
3129
|
+
if (idx >= 0) system.components[idx] = next;
|
|
3130
|
+
else system.components.push(next);
|
|
3131
|
+
system.updatedAt = Date.now();
|
|
3132
|
+
system.status = "draft";
|
|
3133
|
+
return system;
|
|
3134
|
+
}
|
|
3135
|
+
async removeComponent(id, componentId) {
|
|
3136
|
+
const system = await this.get(id);
|
|
3137
|
+
if (!system) return null;
|
|
3138
|
+
const before = system.components.length;
|
|
3139
|
+
system.components = system.components.filter((c) => c.id !== componentId);
|
|
3140
|
+
if (system.components.length === before) return system;
|
|
3141
|
+
system.updatedAt = Date.now();
|
|
3142
|
+
system.status = "draft";
|
|
3143
|
+
return system;
|
|
3144
|
+
}
|
|
3145
|
+
async addComment(id, comment) {
|
|
3146
|
+
const system = await this.get(id);
|
|
3147
|
+
if (!system) return null;
|
|
3148
|
+
system.comments.push({
|
|
3149
|
+
id: newId(),
|
|
3150
|
+
kind: comment.kind,
|
|
3151
|
+
target: comment.target,
|
|
3152
|
+
body: comment.body,
|
|
3153
|
+
status: comment.status ?? "open",
|
|
3154
|
+
createdAt: Date.now(),
|
|
3155
|
+
replies: []
|
|
3156
|
+
});
|
|
3157
|
+
system.updatedAt = Date.now();
|
|
3158
|
+
return system;
|
|
3159
|
+
}
|
|
3160
|
+
async updateComment(id, commentId, fields) {
|
|
3161
|
+
const system = await this.get(id);
|
|
3162
|
+
if (!system) return null;
|
|
3163
|
+
const comment = system.comments.find((c) => c.id === commentId);
|
|
3164
|
+
if (!comment) return null;
|
|
3165
|
+
if (fields.body !== void 0) comment.body = fields.body;
|
|
3166
|
+
if (fields.status) comment.status = fields.status;
|
|
3167
|
+
system.updatedAt = Date.now();
|
|
3168
|
+
return system;
|
|
3169
|
+
}
|
|
3170
|
+
async addReply(id, commentId, reply) {
|
|
3171
|
+
const system = await this.get(id);
|
|
3172
|
+
if (!system) return null;
|
|
3173
|
+
const comment = system.comments.find((c) => c.id === commentId);
|
|
3174
|
+
if (!comment) return null;
|
|
3175
|
+
comment.replies.push(reply);
|
|
3176
|
+
system.updatedAt = Date.now();
|
|
3177
|
+
return system;
|
|
3178
|
+
}
|
|
3179
|
+
};
|
|
3180
|
+
var FileDesignSystemStore = class {
|
|
3181
|
+
dirPath;
|
|
3182
|
+
filePath;
|
|
3183
|
+
memory = new InMemoryDesignSystemStore();
|
|
3184
|
+
loaded = false;
|
|
3185
|
+
constructor(storageDir) {
|
|
3186
|
+
this.dirPath = storageDir ?? getProjectStorageDir();
|
|
3187
|
+
this.filePath = join(this.dirPath, "design-system.json");
|
|
3188
|
+
}
|
|
3189
|
+
async load() {
|
|
3190
|
+
if (this.loaded) return;
|
|
3191
|
+
this.loaded = true;
|
|
3192
|
+
try {
|
|
3193
|
+
const raw = await readFile(this.filePath, "utf8");
|
|
3194
|
+
const parsed = JSON.parse(raw);
|
|
3195
|
+
const systems = Array.isArray(parsed.systems) ? parsed.systems : [];
|
|
3196
|
+
this.memory.systems = systems;
|
|
3197
|
+
for (const system of systems) backfill(system);
|
|
3198
|
+
} catch {}
|
|
3199
|
+
}
|
|
3200
|
+
async persist() {
|
|
3201
|
+
await mkdir(this.dirPath, { recursive: true });
|
|
3202
|
+
const systems = await this.memory.getAll();
|
|
3203
|
+
writeJsonAtomically(this.filePath, { systems });
|
|
3204
|
+
try {
|
|
3205
|
+
const tokens = systems.find((s) => s.status === "published") ?? systems[0];
|
|
3206
|
+
if (tokens) {
|
|
3207
|
+
const { tokensToCss } = await import("./design-system-types-QR9xS7o3.mjs");
|
|
3208
|
+
const dir = join(this.dirPath, "design-system");
|
|
3209
|
+
await mkdir(dir, { recursive: true });
|
|
3210
|
+
await writeFile(join(dir, `${tokens.id}.css`), tokensToCss(tokens.tokens), "utf8");
|
|
3211
|
+
}
|
|
3212
|
+
} catch {}
|
|
3213
|
+
}
|
|
3214
|
+
async getAll() {
|
|
3215
|
+
await this.load();
|
|
3216
|
+
return this.memory.getAll();
|
|
3217
|
+
}
|
|
3218
|
+
async get(id) {
|
|
3219
|
+
await this.load();
|
|
3220
|
+
return this.memory.get(id);
|
|
3221
|
+
}
|
|
3222
|
+
async getDefault() {
|
|
3223
|
+
await this.load();
|
|
3224
|
+
return this.memory.getDefault();
|
|
3225
|
+
}
|
|
3226
|
+
async upsert(input) {
|
|
3227
|
+
await this.load();
|
|
3228
|
+
const system = await this.memory.upsert(input);
|
|
3229
|
+
await this.persist();
|
|
3230
|
+
return system;
|
|
3231
|
+
}
|
|
3232
|
+
async propose(id, input) {
|
|
3233
|
+
await this.load();
|
|
3234
|
+
const system = await this.memory.propose(id, input);
|
|
3235
|
+
if (system) await this.persist();
|
|
3236
|
+
return system;
|
|
3237
|
+
}
|
|
3238
|
+
async publish(id) {
|
|
3239
|
+
await this.load();
|
|
3240
|
+
const system = await this.memory.publish(id);
|
|
3241
|
+
if (system) await this.persist();
|
|
3242
|
+
return system;
|
|
3243
|
+
}
|
|
3244
|
+
async remove(id) {
|
|
3245
|
+
await this.load();
|
|
3246
|
+
const ok = await this.memory.remove(id);
|
|
3247
|
+
if (ok) await this.persist();
|
|
3248
|
+
return ok;
|
|
3249
|
+
}
|
|
3250
|
+
async addComponent(id, component) {
|
|
3251
|
+
await this.load();
|
|
3252
|
+
const system = await this.memory.addComponent(id, component);
|
|
3253
|
+
if (system) await this.persist();
|
|
3254
|
+
return system;
|
|
3255
|
+
}
|
|
3256
|
+
async removeComponent(id, componentId) {
|
|
3257
|
+
await this.load();
|
|
3258
|
+
const system = await this.memory.removeComponent(id, componentId);
|
|
3259
|
+
if (system) await this.persist();
|
|
3260
|
+
return system;
|
|
3261
|
+
}
|
|
3262
|
+
async addComment(id, comment) {
|
|
3263
|
+
await this.load();
|
|
3264
|
+
const system = await this.memory.addComment(id, comment);
|
|
3265
|
+
if (system) await this.persist();
|
|
3266
|
+
return system;
|
|
3267
|
+
}
|
|
3268
|
+
async updateComment(id, commentId, fields) {
|
|
3269
|
+
await this.load();
|
|
3270
|
+
const system = await this.memory.updateComment(id, commentId, fields);
|
|
3271
|
+
if (system) await this.persist();
|
|
3272
|
+
return system;
|
|
3273
|
+
}
|
|
3274
|
+
async addReply(id, commentId, reply) {
|
|
3275
|
+
await this.load();
|
|
3276
|
+
const system = await this.memory.addReply(id, commentId, reply);
|
|
3277
|
+
if (system) await this.persist();
|
|
3278
|
+
return system;
|
|
3279
|
+
}
|
|
3280
|
+
};
|
|
3281
|
+
//#endregion
|
|
3282
|
+
//#region src/lib/design-system-extract.ts
|
|
3283
|
+
const SKIP_DIRS = new Set([
|
|
3284
|
+
"node_modules",
|
|
3285
|
+
".git",
|
|
3286
|
+
"dist",
|
|
3287
|
+
"build",
|
|
3288
|
+
".next",
|
|
3289
|
+
"coverage",
|
|
3290
|
+
"target",
|
|
3291
|
+
".turbo",
|
|
3292
|
+
".output"
|
|
3293
|
+
]);
|
|
3294
|
+
const CSS_FILE_RE = /\.(css|scss|sass)$/i;
|
|
3295
|
+
const TOKEN_JSON_RE = /(tokens?|theme)\.json$/i;
|
|
3296
|
+
const VAR_RE = /--([a-zA-Z0-9_-]+)\s*:\s*([^;]+);/g;
|
|
3297
|
+
const COLOR_ALIASES = [
|
|
3298
|
+
[/(?:^|-)(?:bg|background|canvas|surface-0)$/i, "bg"],
|
|
3299
|
+
[/(?:^|-)(?:surface|raised|panel)$/i, "surface"],
|
|
3300
|
+
[/(?:^|-)(?:text|fg|foreground|ink)$/i, "text"],
|
|
3301
|
+
[/(?:^|-)(?:muted|subtle|secondary-text)$/i, "muted"],
|
|
3302
|
+
[/(?:^|-)(?:accent|primary|brand|focus)$/i, "accent"],
|
|
3303
|
+
[/(?:^|-)(?:border|rule|line)$/i, "border"],
|
|
3304
|
+
[/(?:^|-)(?:danger|error|destructive)$/i, "danger"],
|
|
3305
|
+
[/(?:^|-)(?:ok|success)$/i, "ok"],
|
|
3306
|
+
[/(?:^|-)(?:warn|warning)$/i, "warn"]
|
|
2851
3307
|
];
|
|
2852
|
-
const
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
const
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
3308
|
+
const FONT_ALIASES = [[/(?:ui|sans|body|text)-?font|font-?(?:ui|sans|body)/i, "ui"], [/(?:mono|code|pre)-?font|font-?(?:mono|code)/i, "mono"]];
|
|
3309
|
+
function mapSemantic(name, value, tokens) {
|
|
3310
|
+
const trimmed = value.trim();
|
|
3311
|
+
for (const [re, key] of COLOR_ALIASES) if (re.test(name) && !tokens.color[key]) {
|
|
3312
|
+
tokens.color[key] = trimmed;
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3315
|
+
for (const [re, key] of FONT_ALIASES) if (re.test(name) && !tokens.font[key]) {
|
|
3316
|
+
tokens.font[key] = trimmed.replace(/^["']|["']$/g, "");
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
if (/radius/i.test(name) && !tokens.radius.sm) tokens.radius.sm = trimmed;
|
|
3320
|
+
if (/shadow|elevation/i.test(name) && !tokens.shadow.overlay) tokens.shadow.overlay = trimmed;
|
|
3321
|
+
}
|
|
3322
|
+
/** Pure: pull custom properties out of a CSS/JSON string. */
|
|
3323
|
+
function extractTokensFromText(text) {
|
|
3324
|
+
const tokens = emptyTokens();
|
|
3325
|
+
VAR_RE.lastIndex = 0;
|
|
3326
|
+
let match;
|
|
3327
|
+
while (match = VAR_RE.exec(text)) {
|
|
3328
|
+
const name = match[1];
|
|
3329
|
+
const value = match[2].trim();
|
|
3330
|
+
tokens.raw[`--${name}`] = value;
|
|
3331
|
+
mapSemantic(name, value, tokens);
|
|
3332
|
+
}
|
|
3333
|
+
try {
|
|
3334
|
+
const json = JSON.parse(text);
|
|
3335
|
+
if (json && typeof json === "object") collectJsonTokens(json, tokens, "");
|
|
3336
|
+
} catch {}
|
|
3337
|
+
return tokens;
|
|
3338
|
+
}
|
|
3339
|
+
function collectJsonTokens(value, tokens, path) {
|
|
3340
|
+
if (!value || typeof value !== "object") return;
|
|
3341
|
+
if (Array.isArray(value)) return;
|
|
3342
|
+
for (const [k, v] of Object.entries(value)) {
|
|
3343
|
+
const next = path ? `${path}-${k}` : k;
|
|
3344
|
+
if (typeof v === "string") {
|
|
3345
|
+
tokens.raw[next] = v;
|
|
3346
|
+
mapSemantic(next, v, tokens);
|
|
3347
|
+
} else if (v && typeof v === "object" && "value" in v) {
|
|
3348
|
+
const inner = v.value;
|
|
3349
|
+
if (typeof inner === "string") {
|
|
3350
|
+
tokens.raw[next] = inner;
|
|
3351
|
+
mapSemantic(next, inner, tokens);
|
|
3352
|
+
}
|
|
3353
|
+
} else collectJsonTokens(v, tokens, next);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function mergeTokens(base, extra) {
|
|
3357
|
+
return {
|
|
3358
|
+
color: {
|
|
3359
|
+
...base.color,
|
|
3360
|
+
...extra.color
|
|
3361
|
+
},
|
|
3362
|
+
font: {
|
|
3363
|
+
...base.font,
|
|
3364
|
+
...extra.font
|
|
3365
|
+
},
|
|
3366
|
+
space: extra.space.scale.length ? extra.space : base.space,
|
|
3367
|
+
radius: {
|
|
3368
|
+
...base.radius,
|
|
3369
|
+
...extra.radius
|
|
3370
|
+
},
|
|
3371
|
+
shadow: {
|
|
3372
|
+
...base.shadow,
|
|
3373
|
+
...extra.shadow
|
|
3374
|
+
},
|
|
3375
|
+
raw: {
|
|
3376
|
+
...base.raw,
|
|
3377
|
+
...extra.raw
|
|
3378
|
+
}
|
|
3379
|
+
};
|
|
3380
|
+
}
|
|
3381
|
+
async function walkFiles(root, dir, out, budget) {
|
|
3382
|
+
if (budget.left <= 0) return;
|
|
3383
|
+
let entries;
|
|
3384
|
+
try {
|
|
3385
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
3386
|
+
} catch {
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
for (const entry of entries) {
|
|
3390
|
+
if (budget.left <= 0) return;
|
|
3391
|
+
if (entry.name.startsWith(".") && entry.name !== ".") continue;
|
|
3392
|
+
const full = join(dir, entry.name);
|
|
3393
|
+
if (entry.isDirectory()) {
|
|
3394
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
3395
|
+
await walkFiles(root, full, out, budget);
|
|
3396
|
+
continue;
|
|
3397
|
+
}
|
|
3398
|
+
if (!entry.isFile()) continue;
|
|
3399
|
+
if (CSS_FILE_RE.test(entry.name) || TOKEN_JSON_RE.test(entry.name)) {
|
|
3400
|
+
out.push(full);
|
|
3401
|
+
budget.left -= 1;
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
/** Scan a repo for CSS variables / token JSON. Bounded and skippable. */
|
|
3406
|
+
async function extractFromRepo(repoRoot, opts) {
|
|
3407
|
+
const files = [];
|
|
3408
|
+
await walkFiles(repoRoot, repoRoot, files, { left: opts?.maxFiles ?? 80 });
|
|
3409
|
+
let tokens = emptyTokens();
|
|
3410
|
+
const hits = [];
|
|
3411
|
+
const sources = [];
|
|
3412
|
+
for (const file of files) {
|
|
3413
|
+
let text = "";
|
|
3414
|
+
try {
|
|
3415
|
+
text = await readFile(file, "utf8");
|
|
3416
|
+
} catch {
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
const extracted = extractTokensFromText(text);
|
|
3420
|
+
const count = Object.keys(extracted.raw).length;
|
|
3421
|
+
if (count === 0) continue;
|
|
3422
|
+
tokens = mergeTokens(tokens, extracted);
|
|
3423
|
+
const rel = relative(repoRoot, file) || file;
|
|
3424
|
+
hits.push({
|
|
3425
|
+
path: rel,
|
|
3426
|
+
count
|
|
3427
|
+
});
|
|
3428
|
+
sources.push({
|
|
3429
|
+
kind: "css-vars",
|
|
3430
|
+
path: rel
|
|
2878
3431
|
});
|
|
2879
3432
|
}
|
|
2880
|
-
return
|
|
3433
|
+
return {
|
|
3434
|
+
tokens,
|
|
3435
|
+
sources,
|
|
3436
|
+
files: hits
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
//#endregion
|
|
3440
|
+
//#region src/lib/mockup-shell.ts
|
|
3441
|
+
const MOCKUP_SLOT = "data-diffing-slot";
|
|
3442
|
+
const RESET_CSS = `*,*::before,*::after{box-sizing:border-box}
|
|
3443
|
+
html,body{margin:0;padding:0;background:var(--ds-color-bg,#fff);color:var(--ds-color-text,#111);font-family:var(--ds-font-ui,ui-sans-serif,system-ui,sans-serif);line-height:1.45}
|
|
3444
|
+
img,svg,video{max-width:100%;display:block}
|
|
3445
|
+
button,input,select,textarea{font:inherit}
|
|
3446
|
+
`;
|
|
3447
|
+
function appShell(components, inner) {
|
|
3448
|
+
const shell = components?.find((c) => c.id === "app-shell");
|
|
3449
|
+
if (!shell?.html) return `<main ${MOCKUP_SLOT}="content" data-diffing="content">${inner}</main>`;
|
|
3450
|
+
if (shell.html.includes("{{content}}") || shell.html.includes("data-diffing-slot=\"content\"")) return shell.html.replace("{{content}}", inner).replace(new RegExp(`(<[^>]+${MOCKUP_SLOT}=["']content["'][^>]*>)([\\s\\S]*?)(</[^>]+>)`), `$1${inner}$3`);
|
|
3451
|
+
return `${shell.html}<main ${MOCKUP_SLOT}="content" data-diffing="content">${inner}</main>`;
|
|
3452
|
+
}
|
|
3453
|
+
/** Wrap a fragment in the host document. Stored HTML stays unwrapped. */
|
|
3454
|
+
function wrapMockupFragment(fragment, opts) {
|
|
3455
|
+
const theme = opts.theme ?? "light";
|
|
3456
|
+
const css = `${RESET_CSS}\n${tokensToCss(opts.tokens)}`;
|
|
3457
|
+
const body = appShell(opts.components, fragment);
|
|
3458
|
+
return `<!DOCTYPE html>
|
|
3459
|
+
<html lang="en" data-theme="${theme}">
|
|
3460
|
+
<head>
|
|
3461
|
+
<meta charset="utf-8"/>
|
|
3462
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
3463
|
+
<title>${escapeHtml(opts.title ?? "Mockup")}</title>
|
|
3464
|
+
<style>${css}</style>
|
|
3465
|
+
</head>
|
|
3466
|
+
<body>
|
|
3467
|
+
${body}
|
|
3468
|
+
</body>
|
|
3469
|
+
</html>
|
|
3470
|
+
`;
|
|
3471
|
+
}
|
|
3472
|
+
function resolveRenderMode(requested, system) {
|
|
3473
|
+
if (requested === "document" || requested === "fragment") return requested;
|
|
3474
|
+
return system && system.status === "published" ? "fragment" : "document";
|
|
3475
|
+
}
|
|
3476
|
+
function renderMockupHtml(html, opts) {
|
|
3477
|
+
if (opts.mode !== "fragment" || !opts.system) return html;
|
|
3478
|
+
if (/<!doctype\s+html|<html[\s>]/i.test(html)) return html;
|
|
3479
|
+
return wrapMockupFragment(html, {
|
|
3480
|
+
tokens: opts.system.tokens,
|
|
3481
|
+
components: opts.system.components,
|
|
3482
|
+
title: opts.title,
|
|
3483
|
+
theme: opts.theme
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
function escapeHtml(value) {
|
|
3487
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3488
|
+
}
|
|
3489
|
+
//#endregion
|
|
3490
|
+
//#region src/lib/mockup-preview.ts
|
|
3491
|
+
const execFileAsync = promisify(execFile);
|
|
3492
|
+
const VIEWPORT_WIDTH = {
|
|
3493
|
+
desktop: 1280,
|
|
3494
|
+
tablet: 768,
|
|
3495
|
+
mobile: 390
|
|
3496
|
+
};
|
|
3497
|
+
function analyzeMockupHtml(html) {
|
|
3498
|
+
const missingImages = [];
|
|
3499
|
+
const imgRe = /<img\b[^>]*>/gi;
|
|
3500
|
+
let match;
|
|
3501
|
+
while (match = imgRe.exec(html)) {
|
|
3502
|
+
const tag = match[0];
|
|
3503
|
+
const src = /\bsrc\s*=\s*(["'])([^"']*)\1/i.exec(tag)?.[2] ?? "";
|
|
3504
|
+
if (!src || src === "#" || src.startsWith("about:")) missingImages.push(src || "(empty)");
|
|
3505
|
+
}
|
|
3506
|
+
const externalStylesheets = [...html.matchAll(/<link\b[^>]*rel=["']stylesheet["'][^>]*>/gi)].map((m) => /\bhref\s*=\s*(["'])([^"']*)\1/i.exec(m[0])?.[2] ?? "").filter((href) => /^https?:/i.test(href));
|
|
3507
|
+
const notes = [];
|
|
3508
|
+
if (/overflow:\s*visible/i.test(html) && /white-space:\s*nowrap/i.test(html)) notes.push("Possible overflow: nowrap + visible overflow");
|
|
3509
|
+
if (html.length > 2e5) notes.push("Screen HTML is large; prefer fragments");
|
|
3510
|
+
return {
|
|
3511
|
+
missingImages,
|
|
3512
|
+
externalStylesheets,
|
|
3513
|
+
notes
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3516
|
+
async function findChrome() {
|
|
3517
|
+
const names = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium"] : process.platform === "win32" ? [] : [
|
|
3518
|
+
"google-chrome",
|
|
3519
|
+
"chromium",
|
|
3520
|
+
"chromium-browser",
|
|
3521
|
+
"google-chrome-stable"
|
|
3522
|
+
];
|
|
3523
|
+
for (const name of names) try {
|
|
3524
|
+
if (name.startsWith("/")) {
|
|
3525
|
+
await execFileAsync(name, ["--version"], { timeout: 4e3 });
|
|
3526
|
+
return name;
|
|
3527
|
+
}
|
|
3528
|
+
const { stdout } = await execFileAsync("which", [name], { timeout: 4e3 });
|
|
3529
|
+
const path = stdout.trim();
|
|
3530
|
+
if (path) return path;
|
|
3531
|
+
} catch {}
|
|
3532
|
+
return null;
|
|
3533
|
+
}
|
|
3534
|
+
async function renderMockupPreview(html, opts = {}) {
|
|
3535
|
+
const viewport = opts.viewport ?? "desktop";
|
|
3536
|
+
const width = VIEWPORT_WIDTH[viewport];
|
|
3537
|
+
const report = analyzeMockupHtml(html);
|
|
3538
|
+
let chrome = null;
|
|
3539
|
+
try {
|
|
3540
|
+
chrome = await findChrome();
|
|
3541
|
+
} catch {
|
|
3542
|
+
chrome = null;
|
|
3543
|
+
}
|
|
3544
|
+
if (!chrome) return {
|
|
3545
|
+
available: false,
|
|
3546
|
+
reason: "No Chrome/Chromium on PATH — layout report only",
|
|
3547
|
+
viewport,
|
|
3548
|
+
width,
|
|
3549
|
+
report
|
|
3550
|
+
};
|
|
3551
|
+
const dir = await mkdtemp(join(tmpdir(), "diffing-preview-"));
|
|
3552
|
+
const htmlPath = join(dir, "screen.html");
|
|
3553
|
+
const pngPath = join(dir, "screen.png");
|
|
3554
|
+
try {
|
|
3555
|
+
await writeFile(htmlPath, html, "utf8");
|
|
3556
|
+
await execFileAsync(chrome, [
|
|
3557
|
+
"--headless=new",
|
|
3558
|
+
"--disable-gpu",
|
|
3559
|
+
"--hide-scrollbars",
|
|
3560
|
+
`--window-size=${width},900`,
|
|
3561
|
+
`--screenshot=${pngPath}`,
|
|
3562
|
+
`file://${htmlPath}`
|
|
3563
|
+
], { timeout: 2e4 });
|
|
3564
|
+
return {
|
|
3565
|
+
available: true,
|
|
3566
|
+
screenshotBase64: (await readFile(pngPath)).toString("base64"),
|
|
3567
|
+
mime: "image/png",
|
|
3568
|
+
viewport,
|
|
3569
|
+
width,
|
|
3570
|
+
report
|
|
3571
|
+
};
|
|
3572
|
+
} catch (err) {
|
|
3573
|
+
return {
|
|
3574
|
+
available: false,
|
|
3575
|
+
reason: err instanceof Error ? err.message : "Screenshot failed",
|
|
3576
|
+
viewport,
|
|
3577
|
+
width,
|
|
3578
|
+
report
|
|
3579
|
+
};
|
|
3580
|
+
} finally {
|
|
3581
|
+
await rm(dir, {
|
|
3582
|
+
recursive: true,
|
|
3583
|
+
force: true
|
|
3584
|
+
}).catch(() => void 0);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
//#endregion
|
|
3588
|
+
//#region src/lib/mockup-handoff.ts
|
|
3589
|
+
function buildMockupHandoff(mockup, system) {
|
|
3590
|
+
const used = /* @__PURE__ */ new Set();
|
|
3591
|
+
const blob = mockup.screens.map((s) => s.html).join("\n");
|
|
3592
|
+
for (const component of system?.components ?? []) if (blob.includes(`data-diffing="${component.id}"`) || blob.includes(component.id)) used.add(component.id);
|
|
3593
|
+
const openNits = (mockup.comments ?? []).filter((c) => c.status === "open").map((c) => ({
|
|
3594
|
+
id: c.id,
|
|
3595
|
+
screenId: c.screenId,
|
|
3596
|
+
body: c.body
|
|
3597
|
+
}));
|
|
3598
|
+
return {
|
|
3599
|
+
mockupId: mockup.id,
|
|
3600
|
+
title: mockup.title,
|
|
3601
|
+
version: mockup.version,
|
|
3602
|
+
decision: mockup.decision,
|
|
3603
|
+
designSystemId: mockup.designSystemId ?? system?.id,
|
|
3604
|
+
designRevision: mockup.designRevision ?? system?.revision,
|
|
3605
|
+
mode: mockup.mode,
|
|
3606
|
+
planId: mockup.planId,
|
|
3607
|
+
tokens: system?.tokens,
|
|
3608
|
+
tokenCss: system ? tokensToCss(system.tokens) : void 0,
|
|
3609
|
+
componentsUsed: [...used],
|
|
3610
|
+
screens: mockup.screens.map((s) => ({
|
|
3611
|
+
id: s.id,
|
|
3612
|
+
label: s.label,
|
|
3613
|
+
stateOf: s.stateOf,
|
|
3614
|
+
flow: s.flow,
|
|
3615
|
+
intent: s.stateOf ? `${s.label} (state of ${s.stateOf})` : s.label
|
|
3616
|
+
})),
|
|
3617
|
+
openNits,
|
|
3618
|
+
guidance: "This is a mockup. Match feel (type, color, density, chrome) — not class names or a pixel grid."
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
function formatMockupHandoffXml(handoff) {
|
|
3622
|
+
const screens = handoff.screens.map((s) => ` <screen id="${escape(s.id)}" label="${escape(s.label)}"${s.stateOf ? ` state-of="${escape(s.stateOf)}"` : ""}${s.flow ? ` flow="${escape(s.flow)}"` : ""}>${escape(s.intent)}</screen>`).join("\n");
|
|
3623
|
+
const nits = handoff.openNits.length === 0 ? "" : ` <open-nits>\n${handoff.openNits.map((n) => ` <nit id="${escape(n.id)}" screen="${escape(n.screenId)}">${escape(n.body)}</nit>`).join("\n")}\n </open-nits>\n`;
|
|
3624
|
+
return `<mockup-handoff>
|
|
3625
|
+
<mockup id="${escape(handoff.mockupId)}" title="${escape(handoff.title)}" version="${handoff.version}" decision="${escape(handoff.decision)}"${handoff.designSystemId ? ` design-system="${escape(handoff.designSystemId)}"` : ""}${handoff.designRevision ? ` design-revision="${handoff.designRevision}"` : ""}${handoff.mode ? ` mode="${escape(handoff.mode)}"` : ""}${handoff.planId ? ` plan="${escape(handoff.planId)}"` : ""}>
|
|
3626
|
+
<guidance>${escape(handoff.guidance)}</guidance>
|
|
3627
|
+
<screens>
|
|
3628
|
+
${screens}
|
|
3629
|
+
</screens>
|
|
3630
|
+
${nits} </mockup>
|
|
3631
|
+
</mockup-handoff>`;
|
|
3632
|
+
}
|
|
3633
|
+
function escape(value) {
|
|
3634
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3635
|
+
}
|
|
3636
|
+
//#endregion
|
|
3637
|
+
//#region src/lib/mockup-suggestion.ts
|
|
3638
|
+
const SUGGESTION_RE = /```suggestion\n([\s\S]*?)```/;
|
|
3639
|
+
function extractSuggestion(body) {
|
|
3640
|
+
const match = SUGGESTION_RE.exec(body);
|
|
3641
|
+
if (!match) return null;
|
|
3642
|
+
const text = match[1].replace(/\n$/, "");
|
|
3643
|
+
return text.length > 0 ? text : null;
|
|
2881
3644
|
}
|
|
2882
3645
|
//#endregion
|
|
2883
3646
|
//#region src/lib/state.ts
|
|
@@ -4378,7 +5141,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
4378
5141
|
bindHost: "127.0.0.1",
|
|
4379
5142
|
authToken: null,
|
|
4380
5143
|
insecureNoAuth: true
|
|
4381
|
-
}, mockupStore) {
|
|
5144
|
+
}, mockupStore, designSystemStore) {
|
|
4382
5145
|
const app = new Hono();
|
|
4383
5146
|
app.use("*", createServerAuthMiddleware(security));
|
|
4384
5147
|
let diffOpts = { ...diffOptsInput };
|
|
@@ -4386,6 +5149,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
4386
5149
|
const store = commentStore ?? new FileCommentStore();
|
|
4387
5150
|
const plans = planStore ?? new FilePlanStore();
|
|
4388
5151
|
const mockups = mockupStore ?? new FileMockupStore();
|
|
5152
|
+
const designSystems = designSystemStore ?? new FileDesignSystemStore();
|
|
4389
5153
|
const prStore = prSessionStore ?? new FilePrSessionStore();
|
|
4390
5154
|
const agentDiffCache = new AgentDiffIndexCache();
|
|
4391
5155
|
const uiStateStore = new FileUiStateStore();
|
|
@@ -4440,6 +5204,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
4440
5204
|
let commentsDebounce = null;
|
|
4441
5205
|
let plansDebounce = null;
|
|
4442
5206
|
let mockupsDebounce = null;
|
|
5207
|
+
let designSystemDebounce = null;
|
|
4443
5208
|
let prSessionDebounce = null;
|
|
4444
5209
|
watch(storageDir, (_eventType, filename) => {
|
|
4445
5210
|
if (!filename) return;
|
|
@@ -4452,6 +5217,9 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
4452
5217
|
} else if (filename.startsWith("mockups.json")) {
|
|
4453
5218
|
if (mockupsDebounce) clearTimeout(mockupsDebounce);
|
|
4454
5219
|
mockupsDebounce = setTimeout(() => broadcast("mockups", Date.now().toString()), 120);
|
|
5220
|
+
} else if (filename.startsWith("design-system.json")) {
|
|
5221
|
+
if (designSystemDebounce) clearTimeout(designSystemDebounce);
|
|
5222
|
+
designSystemDebounce = setTimeout(() => broadcast("design-system", Date.now().toString()), 120);
|
|
4455
5223
|
} else if (filename.startsWith("pr-session.json")) {
|
|
4456
5224
|
if (prSessionDebounce) clearTimeout(prSessionDebounce);
|
|
4457
5225
|
prSessionDebounce = setTimeout(() => broadcast("pr-session", Date.now().toString()), 120);
|
|
@@ -5119,7 +5887,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5119
5887
|
if (!relPath) return c.json({ error: "Forbidden file path" }, 403);
|
|
5120
5888
|
const absolutePath = resolve(root, relPath);
|
|
5121
5889
|
const content = await readFile(absolutePath, "utf-8");
|
|
5122
|
-
const { applySuggestionToContent } = await import("./apply-suggestion-
|
|
5890
|
+
const { applySuggestionToContent } = await import("./apply-suggestion-tLsWREyb.mjs");
|
|
5123
5891
|
const result = applySuggestionToContent({
|
|
5124
5892
|
content,
|
|
5125
5893
|
lineNumber: comment.lineNumber,
|
|
@@ -5366,7 +6134,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5366
6134
|
const session = await prStore.get();
|
|
5367
6135
|
if (!session) return notInPrMode(c);
|
|
5368
6136
|
try {
|
|
5369
|
-
const { fetchPrChecks } = await import("./github
|
|
6137
|
+
const { fetchPrChecks } = await import("./github-BuBcy-co.mjs");
|
|
5370
6138
|
const checks = await fetchPrChecks(resolvedFromSession(session), session.headSha);
|
|
5371
6139
|
const summary = {
|
|
5372
6140
|
total: checks.length,
|
|
@@ -5398,7 +6166,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5398
6166
|
const text = typeof body.body === "string" ? body.body.trim() : "";
|
|
5399
6167
|
if (!text) return c.json({ error: "body is required" }, 400);
|
|
5400
6168
|
try {
|
|
5401
|
-
const { replyToPrComment } = await import("./github
|
|
6169
|
+
const { replyToPrComment } = await import("./github-BuBcy-co.mjs");
|
|
5402
6170
|
const result = await replyToPrComment({
|
|
5403
6171
|
resolved: resolvedFromSession(session),
|
|
5404
6172
|
inReplyTo: id,
|
|
@@ -5611,8 +6379,8 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5611
6379
|
if (decision !== "approve" && decision !== "comment" && decision !== "request-changes" && decision !== "draft") return c.json({ error: "decision must be one of: approve, comment, request-changes, draft" }, 400);
|
|
5612
6380
|
const generalBody = typeof body.body === "string" ? body.body : "";
|
|
5613
6381
|
if (body.dryRun === true) {
|
|
5614
|
-
const { buildReviewPayload, resolvedFromSession } = await import("./github
|
|
5615
|
-
const { rewriteLocalAttachmentsInBodies } = await import("./github-attachments-
|
|
6382
|
+
const { buildReviewPayload, resolvedFromSession } = await import("./github-BuBcy-co.mjs");
|
|
6383
|
+
const { rewriteLocalAttachmentsInBodies } = await import("./github-attachments-Bwr25ItV.mjs");
|
|
5616
6384
|
const resolved = resolvedFromSession(session);
|
|
5617
6385
|
const comments = session.comments ?? [];
|
|
5618
6386
|
const rewritten = await rewriteLocalAttachmentsInBodies(resolved, [generalBody, ...comments.map((c) => c.body ?? "")], { dryRun: true });
|
|
@@ -5709,7 +6477,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5709
6477
|
const openCount = all.filter((x) => x.status === "open").length;
|
|
5710
6478
|
let diffFingerprints;
|
|
5711
6479
|
try {
|
|
5712
|
-
const { fingerprintDiffFiles } = await import("./diff-fingerprint-
|
|
6480
|
+
const { fingerprintDiffFiles } = await import("./diff-fingerprint-PnC8Fs1-.mjs");
|
|
5713
6481
|
if (prMode) {
|
|
5714
6482
|
const prSession = await prStore.get();
|
|
5715
6483
|
if (prSession?.diff) diffFingerprints = fingerprintDiffFiles(prSession.diff);
|
|
@@ -5777,7 +6545,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5777
6545
|
} catch (err) {
|
|
5778
6546
|
return c.json({ error: err?.message ?? "Failed to compute current diff" }, 500);
|
|
5779
6547
|
}
|
|
5780
|
-
const { fingerprintDiffFiles, diffSinceLast, filesToReviewSinceLast } = await import("./diff-fingerprint-
|
|
6548
|
+
const { fingerprintDiffFiles, diffSinceLast, filesToReviewSinceLast } = await import("./diff-fingerprint-PnC8Fs1-.mjs");
|
|
5781
6549
|
const delta = diffSinceLast(previous, fingerprintDiffFiles(currentPatch));
|
|
5782
6550
|
return c.json({
|
|
5783
6551
|
hasBaseline: true,
|
|
@@ -5961,6 +6729,152 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5961
6729
|
app.get("/api/plan-review/status", (c) => {
|
|
5962
6730
|
return c.json(planReviewSession.snapshot());
|
|
5963
6731
|
});
|
|
6732
|
+
async function resolveDesignSystem(id, revision) {
|
|
6733
|
+
if (!id) return designSystems.getDefault();
|
|
6734
|
+
const system = await designSystems.get(id);
|
|
6735
|
+
if (!system) return null;
|
|
6736
|
+
if (revision && system.revisions?.length) {
|
|
6737
|
+
const snap = system.revisions.find((r) => r.revision === revision);
|
|
6738
|
+
if (snap) return {
|
|
6739
|
+
...system,
|
|
6740
|
+
revision: snap.revision,
|
|
6741
|
+
status: snap.status,
|
|
6742
|
+
title: snap.title,
|
|
6743
|
+
tokens: snap.tokens,
|
|
6744
|
+
guidelines: snap.guidelines,
|
|
6745
|
+
components: snap.components,
|
|
6746
|
+
sources: snap.sources
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
return system;
|
|
6750
|
+
}
|
|
6751
|
+
app.get("/api/design-systems", async (c) => {
|
|
6752
|
+
return c.json(await designSystems.getAll());
|
|
6753
|
+
});
|
|
6754
|
+
app.get("/api/design-systems/default", async (c) => {
|
|
6755
|
+
const system = await designSystems.getDefault();
|
|
6756
|
+
if (!system) return c.json({ error: "No design system" }, 404);
|
|
6757
|
+
return c.json(system);
|
|
6758
|
+
});
|
|
6759
|
+
app.get("/api/design-systems/:id", async (c) => {
|
|
6760
|
+
const system = await designSystems.get(c.req.param("id"));
|
|
6761
|
+
if (!system) return c.json({ error: "Design system not found" }, 404);
|
|
6762
|
+
return c.json(system);
|
|
6763
|
+
});
|
|
6764
|
+
app.post("/api/design-systems", async (c) => {
|
|
6765
|
+
const body = await c.req.json().catch(() => ({}));
|
|
6766
|
+
const system = await designSystems.upsert({
|
|
6767
|
+
id: typeof body.id === "string" ? body.id : DEFAULT_DESIGN_SYSTEM_ID,
|
|
6768
|
+
title: typeof body.title === "string" ? body.title : void 0,
|
|
6769
|
+
tokens: body.tokens,
|
|
6770
|
+
guidelines: typeof body.guidelines === "string" ? body.guidelines : void 0,
|
|
6771
|
+
sources: Array.isArray(body.sources) ? body.sources : void 0,
|
|
6772
|
+
status: body.status === "published" ? "published" : "draft"
|
|
6773
|
+
});
|
|
6774
|
+
broadcast("design-system", Date.now().toString());
|
|
6775
|
+
return c.json(system, 201);
|
|
6776
|
+
});
|
|
6777
|
+
app.put("/api/design-systems/:id", async (c) => {
|
|
6778
|
+
const body = await c.req.json().catch(() => ({}));
|
|
6779
|
+
if (!await designSystems.get(c.req.param("id"))) return c.json({ error: "Design system not found" }, 404);
|
|
6780
|
+
const system = await designSystems.propose(c.req.param("id"), {
|
|
6781
|
+
title: typeof body.title === "string" ? body.title : void 0,
|
|
6782
|
+
tokens: body.tokens,
|
|
6783
|
+
guidelines: typeof body.guidelines === "string" ? body.guidelines : void 0,
|
|
6784
|
+
components: Array.isArray(body.components) ? body.components : void 0,
|
|
6785
|
+
sources: Array.isArray(body.sources) ? body.sources : void 0
|
|
6786
|
+
});
|
|
6787
|
+
broadcast("design-system", Date.now().toString());
|
|
6788
|
+
return c.json(system);
|
|
6789
|
+
});
|
|
6790
|
+
app.post("/api/design-systems/:id/publish", async (c) => {
|
|
6791
|
+
const system = await designSystems.publish(c.req.param("id"));
|
|
6792
|
+
if (!system) return c.json({ error: "Design system not found" }, 404);
|
|
6793
|
+
broadcast("design-system", Date.now().toString());
|
|
6794
|
+
return c.json(system);
|
|
6795
|
+
});
|
|
6796
|
+
app.post("/api/design-systems/:id/extract", async (c) => {
|
|
6797
|
+
const body = await c.req.json().catch(() => ({}));
|
|
6798
|
+
const from = typeof body.from === "string" ? body.from : "css";
|
|
6799
|
+
let extracted;
|
|
6800
|
+
if (from === "text" && typeof body.text === "string") extracted = {
|
|
6801
|
+
tokens: extractTokensFromText(body.text),
|
|
6802
|
+
sources: [{
|
|
6803
|
+
kind: "css-vars",
|
|
6804
|
+
path: "paste"
|
|
6805
|
+
}],
|
|
6806
|
+
files: []
|
|
6807
|
+
};
|
|
6808
|
+
else if (from === "url" && typeof body.url === "string") {
|
|
6809
|
+
const url = body.url;
|
|
6810
|
+
if (!/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])/i.test(url)) return c.json({ error: "Capture URL must be a local http(s) address" }, 400);
|
|
6811
|
+
const res = await fetch(url).catch(() => null);
|
|
6812
|
+
const text = res && res.ok ? await res.text() : "";
|
|
6813
|
+
extracted = {
|
|
6814
|
+
tokens: extractTokensFromText(text),
|
|
6815
|
+
sources: [{
|
|
6816
|
+
kind: "url",
|
|
6817
|
+
url
|
|
6818
|
+
}],
|
|
6819
|
+
files: text ? [{
|
|
6820
|
+
path: url,
|
|
6821
|
+
count: Object.keys(extractTokensFromText(text).raw).length
|
|
6822
|
+
}] : []
|
|
6823
|
+
};
|
|
6824
|
+
} else extracted = await extractFromRepo(repoRoot);
|
|
6825
|
+
const id = c.req.param("id");
|
|
6826
|
+
const system = await designSystems.get(id) ? await designSystems.propose(id, {
|
|
6827
|
+
tokens: extracted.tokens,
|
|
6828
|
+
sources: extracted.sources
|
|
6829
|
+
}) : await designSystems.upsert({
|
|
6830
|
+
id,
|
|
6831
|
+
title: typeof body.title === "string" ? body.title : "Default",
|
|
6832
|
+
tokens: extracted.tokens,
|
|
6833
|
+
sources: extracted.sources,
|
|
6834
|
+
status: "draft"
|
|
6835
|
+
});
|
|
6836
|
+
broadcast("design-system", Date.now().toString());
|
|
6837
|
+
return c.json({
|
|
6838
|
+
system,
|
|
6839
|
+
extract: extracted
|
|
6840
|
+
});
|
|
6841
|
+
});
|
|
6842
|
+
app.post("/api/design-systems/:id/components", async (c) => {
|
|
6843
|
+
const body = await c.req.json().catch(() => ({}));
|
|
6844
|
+
if (typeof body.id !== "string" || typeof body.html !== "string") return c.json({ error: "id and html are required" }, 400);
|
|
6845
|
+
const system = await designSystems.addComponent(c.req.param("id"), {
|
|
6846
|
+
id: body.id,
|
|
6847
|
+
label: typeof body.label === "string" ? body.label : body.id,
|
|
6848
|
+
html: body.html,
|
|
6849
|
+
source: body.source === "promote" ? "promote" : "human"
|
|
6850
|
+
});
|
|
6851
|
+
if (!system) return c.json({ error: "Design system not found" }, 404);
|
|
6852
|
+
broadcast("design-system", Date.now().toString());
|
|
6853
|
+
return c.json(system);
|
|
6854
|
+
});
|
|
6855
|
+
app.post("/api/design-systems/:id/comments", async (c) => {
|
|
6856
|
+
const body = await c.req.json().catch(() => ({}));
|
|
6857
|
+
if (typeof body.body !== "string" || !body.body.trim()) return c.json({ error: "body is required" }, 400);
|
|
6858
|
+
const kind = body.kind === "token" || body.kind === "component" || body.kind === "guidelines" ? body.kind : "general";
|
|
6859
|
+
const system = await designSystems.addComment(c.req.param("id"), {
|
|
6860
|
+
kind,
|
|
6861
|
+
target: typeof body.target === "string" ? body.target : void 0,
|
|
6862
|
+
body: body.body
|
|
6863
|
+
});
|
|
6864
|
+
if (!system) return c.json({ error: "Design system not found" }, 404);
|
|
6865
|
+
broadcast("design-system", Date.now().toString());
|
|
6866
|
+
return c.json(system);
|
|
6867
|
+
});
|
|
6868
|
+
app.put("/api/design-systems/:id/comments/:commentId", async (c) => {
|
|
6869
|
+
const body = await c.req.json().catch(() => ({}));
|
|
6870
|
+
const system = await designSystems.updateComment(c.req.param("id"), c.req.param("commentId"), {
|
|
6871
|
+
body: typeof body.body === "string" ? body.body : void 0,
|
|
6872
|
+
status: body.status === "resolved" || body.status === "open" ? body.status : void 0
|
|
6873
|
+
});
|
|
6874
|
+
if (!system) return c.json({ error: "Not found" }, 404);
|
|
6875
|
+
broadcast("design-system", Date.now().toString());
|
|
6876
|
+
return c.json(system);
|
|
6877
|
+
});
|
|
5964
6878
|
const summarizeMockup = (mockup, includeComments = false) => ({
|
|
5965
6879
|
id: mockup.id,
|
|
5966
6880
|
title: mockup.title,
|
|
@@ -5981,6 +6895,8 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
5981
6895
|
open: mockup.comments.filter((comment) => comment.status === "open").length,
|
|
5982
6896
|
resolved: mockup.comments.filter((comment) => comment.status === "resolved").length
|
|
5983
6897
|
},
|
|
6898
|
+
designSystemId: mockup.designSystemId,
|
|
6899
|
+
planId: mockup.planId,
|
|
5984
6900
|
...includeComments ? { comments: mockup.comments } : {}
|
|
5985
6901
|
});
|
|
5986
6902
|
app.get("/api/mockups", async (c) => {
|
|
@@ -6045,7 +6961,17 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6045
6961
|
version: versionN,
|
|
6046
6962
|
viewport
|
|
6047
6963
|
});
|
|
6048
|
-
|
|
6964
|
+
const snapBinding = snap;
|
|
6965
|
+
const mode = snapBinding.mode ?? mockup.mode ?? "document";
|
|
6966
|
+
const theme = c.req.query("theme") === "dark" ? "dark" : "light";
|
|
6967
|
+
const system = await resolveDesignSystem(snapBinding.designSystemId ?? mockup.designSystemId, snapBinding.designRevision ?? mockup.designRevision);
|
|
6968
|
+
const rendered = renderMockupHtml(screen.html, {
|
|
6969
|
+
mode,
|
|
6970
|
+
system,
|
|
6971
|
+
title: mockup.title,
|
|
6972
|
+
theme
|
|
6973
|
+
});
|
|
6974
|
+
return c.html(injectMockupProbe(rendered, {
|
|
6049
6975
|
nonce,
|
|
6050
6976
|
viewport,
|
|
6051
6977
|
passive
|
|
@@ -6060,7 +6986,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6060
6986
|
const mockup = await mockups.get(c.req.param("id"));
|
|
6061
6987
|
if (!mockup) return c.json({ error: "Mockup not found" }, 404);
|
|
6062
6988
|
const view = c.req.query("view") || "summary";
|
|
6063
|
-
if (view !== "summary" && view !== "comments" && view !== "comment" && view !== "screen") return c.json({ error: "view must be summary|comments|comment|screen" }, 400);
|
|
6989
|
+
if (view !== "summary" && view !== "comments" && view !== "comment" && view !== "screen" && view !== "preview") return c.json({ error: "view must be summary|comments|comment|screen|preview" }, 400);
|
|
6064
6990
|
const status = c.req.query("status");
|
|
6065
6991
|
if (status !== void 0 && status !== "open" && status !== "resolved") return c.json({ error: "status must be open or resolved" }, 400);
|
|
6066
6992
|
const screenFilter = c.req.query("screen");
|
|
@@ -6108,6 +7034,26 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6108
7034
|
}
|
|
6109
7035
|
});
|
|
6110
7036
|
}
|
|
7037
|
+
if (view === "preview") {
|
|
7038
|
+
const screenId = c.req.query("screen") || mockup.screens[0]?.id;
|
|
7039
|
+
const screen = mockup.screens.find((s) => s.id === screenId);
|
|
7040
|
+
if (!screen) return c.json({ error: "Screen not found" }, 404);
|
|
7041
|
+
const viewport = normalizeMockupViewport(c.req.query("viewport"));
|
|
7042
|
+
const theme = c.req.query("theme") === "dark" ? "dark" : "light";
|
|
7043
|
+
const system = await resolveDesignSystem(mockup.designSystemId, mockup.designRevision);
|
|
7044
|
+
const preview = await renderMockupPreview(renderMockupHtml(screen.html, {
|
|
7045
|
+
mode: mockup.mode ?? "document",
|
|
7046
|
+
system,
|
|
7047
|
+
title: mockup.title,
|
|
7048
|
+
theme
|
|
7049
|
+
}), { viewport });
|
|
7050
|
+
return c.json({
|
|
7051
|
+
view: "preview",
|
|
7052
|
+
mockupId: mockup.id,
|
|
7053
|
+
screenId: screen.id,
|
|
7054
|
+
...preview
|
|
7055
|
+
});
|
|
7056
|
+
}
|
|
6111
7057
|
if (view === "comment") {
|
|
6112
7058
|
const id = c.req.query("id");
|
|
6113
7059
|
if (!id) return c.json({ error: "id is required for view=comment" }, 400);
|
|
@@ -6186,6 +7132,10 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6186
7132
|
error: result.error,
|
|
6187
7133
|
code: "exact-text-not-found"
|
|
6188
7134
|
}, 409);
|
|
7135
|
+
if (result.error?.includes("not found") && result.error.startsWith("Region ")) return c.json({
|
|
7136
|
+
error: result.error,
|
|
7137
|
+
code: "region-not-found"
|
|
7138
|
+
}, 409);
|
|
6189
7139
|
if (result.error === "Mockup not found" || result.error?.endsWith("not found")) return c.json({ error: result.error }, 404);
|
|
6190
7140
|
return c.json({ error: result.error ?? "Screen operation failed" }, 400);
|
|
6191
7141
|
};
|
|
@@ -6217,14 +7167,26 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6217
7167
|
const mockupId = c.req.param("id");
|
|
6218
7168
|
const screenId = c.req.param("screenId");
|
|
6219
7169
|
const body = await c.req.json().catch(() => ({}));
|
|
7170
|
+
const expectedVersion = Number.isFinite(body.expectedVersion) ? Number(body.expectedVersion) : void 0;
|
|
7171
|
+
const region = typeof body.region === "string" ? body.region.trim() : "";
|
|
7172
|
+
if (region) {
|
|
7173
|
+
if (typeof body.replacement !== "string") return c.json({ error: "replacement is required" }, 400);
|
|
7174
|
+
const result = await mockups.replaceRegion(mockupId, screenId, {
|
|
7175
|
+
region,
|
|
7176
|
+
replacement: body.replacement
|
|
7177
|
+
}, { expectedVersion });
|
|
7178
|
+
if (!result.mockup) return screenOpError(c, result);
|
|
7179
|
+
return c.json({
|
|
7180
|
+
mockup: result.mockup,
|
|
7181
|
+
occurrences: result.occurrences
|
|
7182
|
+
}, 200);
|
|
7183
|
+
}
|
|
6220
7184
|
const expectedText = typeof body.expectedText === "string" ? body.expectedText : "";
|
|
6221
|
-
|
|
6222
|
-
if (!expectedText) return c.json({ error: "expectedText is required" }, 400);
|
|
7185
|
+
if (!expectedText) return c.json({ error: "expectedText or region is required" }, 400);
|
|
6223
7186
|
if (typeof body.replacement !== "string") return c.json({ error: "replacement is required" }, 400);
|
|
6224
|
-
const expectedVersion = Number.isFinite(body.expectedVersion) ? Number(body.expectedVersion) : void 0;
|
|
6225
7187
|
const result = await mockups.patchScreen(mockupId, screenId, {
|
|
6226
7188
|
expectedText,
|
|
6227
|
-
replacement
|
|
7189
|
+
replacement: body.replacement
|
|
6228
7190
|
}, { expectedVersion });
|
|
6229
7191
|
if (!result.mockup) return screenOpError(c, result);
|
|
6230
7192
|
return c.json({
|
|
@@ -6262,15 +7224,38 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6262
7224
|
});
|
|
6263
7225
|
app.post("/api/mockups", async (c) => {
|
|
6264
7226
|
const body = await c.req.json().catch(() => ({}));
|
|
7227
|
+
if (typeof body.fromMockupId === "string") {
|
|
7228
|
+
const source = await mockups.get(body.fromMockupId);
|
|
7229
|
+
if (!source) return c.json({ error: "Source mockup not found" }, 404);
|
|
7230
|
+
body.screens = source.screens.map((s) => ({
|
|
7231
|
+
id: s.id,
|
|
7232
|
+
label: s.label,
|
|
7233
|
+
html: s.html,
|
|
7234
|
+
stateOf: s.stateOf,
|
|
7235
|
+
flow: s.flow
|
|
7236
|
+
}));
|
|
7237
|
+
if (!body.designSystemId && !body.designSystem) body.designSystemId = source.designSystemId;
|
|
7238
|
+
if (!body.mode) body.mode = source.mode;
|
|
7239
|
+
if (!body.flows) body.flows = source.flows;
|
|
7240
|
+
} else if (body.blank === true && !body.html && !body.screens) body.html = "";
|
|
7241
|
+
if (body.blank === true && body.html === "") body.html = "<!-- empty -->";
|
|
6265
7242
|
const normalized = screensFromSubmitBody(body);
|
|
6266
7243
|
if (!normalized.ok) return c.json({ error: normalized.error }, 400);
|
|
6267
7244
|
const title = typeof body.title === "string" && body.title.trim() ? body.title.trim() : "Untitled mockup";
|
|
7245
|
+
const requestedMode = body.mode;
|
|
7246
|
+
const system = await resolveDesignSystem(typeof body.designSystem === "string" ? body.designSystem : typeof body.designSystemId === "string" ? body.designSystemId : void 0);
|
|
7247
|
+
const mode = resolveRenderMode(requestedMode, system);
|
|
6268
7248
|
const mockup = await mockups.upsert({
|
|
6269
7249
|
id: typeof body.id === "string" && body.id ? body.id : void 0,
|
|
6270
7250
|
title,
|
|
6271
7251
|
screens: normalized.screens,
|
|
6272
7252
|
source: typeof body.source === "string" ? body.source : void 0,
|
|
6273
|
-
model: typeof body.model === "string" ? body.model : void 0
|
|
7253
|
+
model: typeof body.model === "string" ? body.model : void 0,
|
|
7254
|
+
designSystemId: system?.id,
|
|
7255
|
+
designRevision: system?.revision,
|
|
7256
|
+
mode,
|
|
7257
|
+
planId: typeof body.planId === "string" ? body.planId : void 0,
|
|
7258
|
+
flows: Array.isArray(body.flows) ? body.flows : void 0
|
|
6274
7259
|
});
|
|
6275
7260
|
const hints = lintMockupScreens(normalized.screens);
|
|
6276
7261
|
return c.json(hints.length > 0 ? {
|
|
@@ -6296,6 +7281,37 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6296
7281
|
if (!updated) return c.json({ error: "Mockup not found" }, 404);
|
|
6297
7282
|
return c.json(updated);
|
|
6298
7283
|
});
|
|
7284
|
+
app.get("/api/mockups/:id/handoff", async (c) => {
|
|
7285
|
+
const mockup = await mockups.get(c.req.param("id"));
|
|
7286
|
+
if (!mockup) return c.json({ error: "Mockup not found" }, 404);
|
|
7287
|
+
const handoff = buildMockupHandoff(mockup, await resolveDesignSystem(mockup.designSystemId, mockup.designRevision));
|
|
7288
|
+
return c.json({
|
|
7289
|
+
...handoff,
|
|
7290
|
+
xml: formatMockupHandoffXml(handoff)
|
|
7291
|
+
});
|
|
7292
|
+
});
|
|
7293
|
+
app.post("/api/mockups/:id/comments/:commentId/apply-suggestion", async (c) => {
|
|
7294
|
+
const mockup = await mockups.get(c.req.param("id"));
|
|
7295
|
+
if (!mockup) return c.json({ error: "Mockup not found" }, 404);
|
|
7296
|
+
const comment = mockup.comments.find((x) => x.id === c.req.param("commentId"));
|
|
7297
|
+
if (!comment) return c.json({ error: "Comment not found" }, 404);
|
|
7298
|
+
const suggestion = extractSuggestion(comment.body);
|
|
7299
|
+
if (!suggestion) return c.json({ error: "No ```suggestion block in comment" }, 400);
|
|
7300
|
+
const reqBody = await c.req.json().catch(() => ({}));
|
|
7301
|
+
const expectedVersion = Number.isFinite(reqBody.expectedVersion) ? Number(reqBody.expectedVersion) : mockup.version;
|
|
7302
|
+
let result;
|
|
7303
|
+
if (comment.target) result = await mockups.replaceRegion(mockup.id, comment.screenId, {
|
|
7304
|
+
region: comment.target,
|
|
7305
|
+
replacement: suggestion
|
|
7306
|
+
}, { expectedVersion });
|
|
7307
|
+
else if (comment.html) result = await mockups.patchScreen(mockup.id, comment.screenId, {
|
|
7308
|
+
expectedText: comment.html,
|
|
7309
|
+
replacement: suggestion
|
|
7310
|
+
}, { expectedVersion });
|
|
7311
|
+
else return c.json({ error: "Comment has no target or html to replace" }, 400);
|
|
7312
|
+
if (!result.mockup) return screenOpError(c, result);
|
|
7313
|
+
return c.json(result.mockup);
|
|
7314
|
+
});
|
|
6299
7315
|
app.delete("/api/mockups/:id", async (c) => {
|
|
6300
7316
|
if (!await mockups.remove(c.req.param("id"))) return c.json({ error: "Mockup not found" }, 404);
|
|
6301
7317
|
return c.json({ ok: true });
|
|
@@ -6318,6 +7334,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6318
7334
|
}
|
|
6319
7335
|
if (typeof body.body !== "string" || !body.body.trim()) return c.json({ error: "body is required" }, 400);
|
|
6320
7336
|
const viewport = normalizeMockupViewport(body.viewport);
|
|
7337
|
+
const theme = body.theme === "dark" ? "dark" : "light";
|
|
6321
7338
|
const severityRaw = body.severity;
|
|
6322
7339
|
const severity = severityRaw === "blocking" || severityRaw === "nit" || severityRaw === "question" || severityRaw === "praise" || severityRaw === "none" ? severityRaw : void 0;
|
|
6323
7340
|
const rect = body.rect && typeof body.rect === "object" ? body.rect : void 0;
|
|
@@ -6346,6 +7363,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
|
|
|
6346
7363
|
createdAt: Date.now(),
|
|
6347
7364
|
createdAtMockupVersion: Number.isFinite(body.createdAtMockupVersion) ? Number(body.createdAtMockupVersion) : mockup.version,
|
|
6348
7365
|
viewport,
|
|
7366
|
+
theme,
|
|
6349
7367
|
replies: [],
|
|
6350
7368
|
...severity && severity !== "none" ? { severity } : {}
|
|
6351
7369
|
};
|