@panphora/clayjs 1.23.0 → 1.24.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.
- package/dist/all.js +1 -1
- package/dist/blockeditor/themes/light-1.css +431 -0
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +30285 -26613
- package/dist/m/c-277QP5ET.js +1 -0
- package/dist/m/c-2BLVA6IN.js +5 -0
- package/dist/m/c-2OJMOUPC.js +1 -0
- package/dist/m/c-2PVGJFI5.js +1 -0
- package/dist/m/{c-AYVGD6A6.js → c-3UW5EJ77.js} +1 -1
- package/dist/m/{c-T5NK2TVY.js → c-42ZNV7LI.js} +1 -1
- package/dist/m/{c-3TLG3OLG.js → c-46LJAZZJ.js} +1 -1
- package/dist/m/c-47IBJGPQ.js +1 -0
- package/dist/m/c-5FXEKU4Y.js +1 -0
- package/dist/m/c-5KXDKC5G.js +1 -0
- package/dist/m/{c-EUGJVHDD.js → c-5RZNFTWD.js} +1 -1
- package/dist/m/c-65LJU2H2.js +72 -0
- package/dist/m/{c-NQJRVJ56.js → c-6IJEVE6B.js} +1 -1
- package/dist/m/{c-NW54NHKE.js → c-6LEYVQJK.js} +1 -1
- package/dist/m/{c-GSRLXYWL.js → c-6Q6RGFBF.js} +1 -1
- package/dist/m/c-6RGEQNVV.js +1 -0
- package/dist/m/c-7CM4GMTS.js +6 -0
- package/dist/m/c-7NCWENHV.js +1 -0
- package/dist/m/c-7V6MGMX2.js +1 -0
- package/dist/m/{c-LKSL4AUJ.js → c-7WZSUTYP.js} +1 -1
- package/dist/m/c-A4L7APIU.js +1 -0
- package/dist/m/c-APGHGDZM.js +1 -0
- package/dist/m/c-AT7GLQGV.js +34 -0
- package/dist/m/{c-ZRXJD4G6.js → c-B7GOFGQR.js} +1 -1
- package/dist/m/{c-KDXIJEEF.js → c-BGN7RCTJ.js} +1 -1
- package/dist/m/c-BKM46LUJ.js +1 -0
- package/dist/m/c-BOMQIQ3V.js +1 -0
- package/dist/m/{c-62HZW5DH.js → c-C5AKJVRH.js} +1 -1
- package/dist/m/{c-BXYSDKAN.js → c-CJ7QYEWI.js} +1 -1
- package/dist/m/c-CTGRDT6E.js +1 -0
- package/dist/m/c-CV4T65JE.js +6 -0
- package/dist/m/c-CVSWLX3X.js +1 -0
- package/dist/m/c-CZHTXGJS.js +1 -0
- package/dist/m/c-D7NT5S4F.js +1 -0
- package/dist/m/c-DD26VXAP.js +1 -0
- package/dist/m/{c-ANNFZFLN.js → c-DDS23EY6.js} +1 -1
- package/dist/m/{c-ALDVFPWL.js → c-DFKBGSN2.js} +1 -1
- package/dist/m/c-DIKR7H57.js +147 -0
- package/dist/m/c-DXPOFLEJ.js +1 -0
- package/dist/m/c-EDE45NM7.js +1 -0
- package/dist/m/c-EILLN2H2.js +1 -0
- package/dist/m/c-EODTTNVA.js +1 -0
- package/dist/m/{c-G6COVIZB.js → c-ESK4S67I.js} +1 -1
- package/dist/m/c-FDWR3FL7.js +1 -0
- package/dist/m/{c-WQZTI5P2.js → c-FEC6LGFF.js} +1 -1
- package/dist/m/c-FLEIU6CG.js +1 -0
- package/dist/m/c-FQAQRXGK.js +1 -0
- package/dist/m/{c-W4JCVLAB.js → c-FSQXFG5T.js} +1 -1
- package/dist/m/c-FVPNX2GO.js +135 -0
- package/dist/m/c-FYDKYW6E.js +1 -0
- package/dist/m/c-GNI7WHKF.js +32 -0
- package/dist/m/c-GO635CRU.js +3 -0
- package/dist/m/c-H3FNLBHN.js +2 -0
- package/dist/m/c-HDVH3MVC.js +72 -0
- package/dist/m/c-HIX5H5L7.js +1 -0
- package/dist/m/c-HKSLOVU4.js +1 -0
- package/dist/m/{c-SGRIYOAJ.js → c-HL6BLJNA.js} +1 -1
- package/dist/m/c-HTLYXCUH.js +1 -0
- package/dist/m/{c-QIR5CAKM.js → c-IUL7PU4P.js} +1 -1
- package/dist/m/c-IXO7EI2Q.js +1 -0
- package/dist/m/{c-W5E5P2TZ.js → c-JQF2EUCG.js} +1 -1
- package/dist/m/{c-NEL7HQW3.js → c-JWDCQL6J.js} +1 -1
- package/dist/m/c-KQKKIVLW.js +20 -0
- package/dist/m/c-LATNQ6Q5.js +1 -0
- package/dist/m/c-LD6UVV5R.js +1 -0
- package/dist/m/c-LS7IVQVW.js +1 -0
- package/dist/m/{c-JCOQVZYS.js → c-LSVN3OLG.js} +1 -1
- package/dist/m/{c-JDZDF2QM.js → c-LTSPHVAK.js} +1 -1
- package/dist/m/c-MFOSMAVM.js +3 -0
- package/dist/m/c-MJ7GVE46.js +1 -0
- package/dist/m/c-MP6S4AZU.js +6 -0
- package/dist/m/c-MQKSKQHG.js +2 -0
- package/dist/m/{c-UHUNXWKN.js → c-MQYEDF3Q.js} +1 -1
- package/dist/m/{c-IHUWUKIU.js → c-MR75YPRT.js} +1 -1
- package/dist/m/c-ND3Q2G2I.js +1 -0
- package/dist/m/c-NEQRFC4O.js +1 -0
- package/dist/m/{c-5FKRZNJV.js → c-NFQFV7OP.js} +1 -1
- package/dist/m/c-NHXOBD6A.js +1 -0
- package/dist/m/{c-QIKT6SKM.js → c-NICICT4P.js} +1 -1
- package/dist/m/c-NQAPWM7O.js +1 -0
- package/dist/m/{c-QUGCKMWF.js → c-NVU3DORS.js} +1 -1
- package/dist/m/{c-LWIDDWAR.js → c-OCYG4NBQ.js} +22 -22
- package/dist/m/c-OH2FPYSY.js +19 -0
- package/dist/m/c-OYHI67B6.js +1 -0
- package/dist/m/{c-R2XUODVY.js → c-OYW5MFPZ.js} +1 -1
- package/dist/m/{c-5B4YBZXC.js → c-P2HKMJAW.js} +1 -1
- package/dist/m/{c-TSBVMWAH.js → c-PCNJETUA.js} +1 -1
- package/dist/m/c-PDBNCJCT.js +1 -0
- package/dist/m/c-PGSBXHUE.js +1 -0
- package/dist/m/{c-5TIZ2I64.js → c-PYG2NGWU.js} +1 -1
- package/dist/m/c-Q26XHBLQ.js +1 -0
- package/dist/m/c-Q6BG3ZLO.js +1 -0
- package/dist/m/c-QENKN7QI.js +1 -0
- package/dist/m/c-QQFHEODI.js +1 -0
- package/dist/m/c-QTEUO2K6.js +1 -0
- package/dist/m/c-R23UHMXG.js +1 -0
- package/dist/m/c-RETTCOWO.js +10 -0
- package/dist/m/c-SE3SIY7Y.js +7 -0
- package/dist/m/{c-XX75FFNL.js → c-SGGGCIVD.js} +1 -1
- package/dist/m/c-SGH3UTF3.js +1 -0
- package/dist/m/c-SIM7Z3U7.js +1 -0
- package/dist/m/{c-X5LNHET5.js → c-SKMIR5HQ.js} +1 -1
- package/dist/m/{c-R27DWQKX.js → c-STRI73GQ.js} +1 -1
- package/dist/m/c-SUXHUL7U.js +1 -0
- package/dist/m/c-SXGEWPFC.js +10 -0
- package/dist/m/c-SXNSSHPP.js +1 -0
- package/dist/m/c-T3P3AXEW.js +1 -0
- package/dist/m/{c-PXLTUYZC.js → c-TFLRT5JX.js} +1 -1
- package/dist/m/{c-YCLU4UNT.js → c-TGIMBAWE.js} +1 -1
- package/dist/m/c-TGSHHFO6.js +1 -0
- package/dist/m/c-U4NTUVDZ.js +1 -0
- package/dist/m/c-UOOIF2AL.js +1 -0
- package/dist/m/{c-6TYGD5AB.js → c-UOUUI265.js} +1 -1
- package/dist/m/c-V2U3F4QX.js +1 -0
- package/dist/m/c-V2V5FKXX.js +1 -0
- package/dist/m/c-VBXRXTVS.js +26 -0
- package/dist/m/c-VHSMPITY.js +1 -0
- package/dist/m/c-VPXCXBDT.js +1 -0
- package/dist/m/c-WCG6AKA7.js +1 -0
- package/dist/m/c-WNOLMHI2.js +1 -0
- package/dist/m/{c-CRPIDGXC.js → c-X6MOYATI.js} +1 -1
- package/dist/m/{c-FQGHR32O.js → c-X75G43SZ.js} +1 -1
- package/dist/m/c-XB6EAIAR.js +13 -0
- package/dist/m/c-XMIDHEKF.js +1 -0
- package/dist/m/c-XUZMRQ7O.js +4 -0
- package/dist/m/{c-2Z22FCFK.js → c-XW5TIYYB.js} +1 -1
- package/dist/m/c-YDW4VCHP.js +1 -0
- package/dist/m/{c-QGAPG7OM.js → c-YLHTD3KX.js} +1 -1
- package/dist/m/c-YNIEIPBS.js +1 -0
- package/dist/m/c-Z4G2UK6T.js +4 -0
- package/dist/m/c-Z6MROEQ4.js +1 -0
- package/dist/m/c-ZFO5RW6G.js +2 -0
- package/dist/m/c-ZGCO4J3H.js +8 -0
- package/dist/m/c-ZKKAF4UT.js +3 -0
- package/dist/m/c-ZRGEN6QQ.js +4 -0
- package/dist/m/core/{admin-attrs-U7IJNYBG.js → admin-attrs-ONRWVXPK.js} +1 -1
- package/dist/m/core/{autosave-F5YQCKVL.js → autosave-TBKAAAFB.js} +1 -1
- package/dist/m/core/conflict-notice-FOEE6I7E.js +1 -0
- package/dist/m/core/draft-mode-4MCL3LYI.js +1 -0
- package/dist/m/core/{host-permission-6EVJGNPL.js → host-permission-X4BENZEU.js} +1 -1
- package/dist/m/core/{is-edit-mode-GCX6767E.js → is-edit-mode-FFHCQ5DR.js} +1 -1
- package/dist/m/core/{persist-CJXIL7D4.js → persist-6ILUR34S.js} +1 -1
- package/dist/m/core/{recovery-DRFZRJED.js → recovery-APWADU4K.js} +1 -1
- package/dist/m/core/save-6HT2LABE.js +1 -0
- package/dist/m/core/save-core-EVMNZQ4V.js +1 -0
- package/dist/m/core/site-edits-G4BOZMV4.js +1 -0
- package/dist/m/core/{unsaved-warning-HNQVTA2D.js → unsaved-warning-ITZDIZ2I.js} +1 -1
- package/dist/m/internals/{index-NRPNDTSA.js → index-HS3CQDFW.js} +1 -1
- package/dist/m/loader-GLLVDUZA.js +1 -0
- package/dist/m/manifest-50FF1C45.js +1 -0
- package/dist/m/manifest-edit-BE1A621D.js +1 -0
- package/dist/m/manifest.json +2072 -1811
- package/dist/m/page/editor-KAZDJNND.js +307 -0
- package/dist/m/plugins/ai-edit-LABECCSN.js +6 -0
- package/dist/m/plugins/{cms-H7DTMUQX.js → cms-T2HLI7JG.js} +16 -16
- package/dist/m/plugins/{demo-42BSYQ7T.js → demo-T7B4RIKL.js} +1 -1
- package/dist/m/plugins/{derive-QVI3LI3A.js → derive-6LPJOCAV.js} +1 -1
- package/dist/m/plugins/indicator-J4ABFARX.js +1 -0
- package/dist/m/plugins/{live-DCUBKXY4.js → live-PGBIEMGB.js} +1 -1
- package/dist/m/plugins/menu-YZL3X2AJ.js +1 -0
- package/dist/m/plugins/page-WOCNX2IN.js +2 -0
- package/dist/m/plugins/page-view-SJODOOGE.js +1 -0
- package/dist/m/plugins/{people-5XRNRP5U.js → people-C4TCWFOQ.js} +1 -1
- package/dist/m/plugins/{quickcrop-WWF5I422.js → quickcrop-X5MRIBAP.js} +2 -2
- package/dist/m/plugins/richclay-Y2NV44NA.js +549 -0
- package/dist/m/plugins/sortable-XCHQG65Z.js +1 -0
- package/dist/m/plugins/{source-6BKMK4PO.js → source-YK7MO23W.js} +1 -1
- package/dist/m/plugins/{undo-3JEYK5QY.js → undo-VLRBQNPN.js} +1 -1
- package/dist/m/plugins/upgrade-NNTJBSK2.js +1 -0
- package/dist/m/plugins/{upload-TTCQRIM5.js → upload-ZKKJV76X.js} +1 -1
- package/dist/m/plugins/visitor-drafts-RTZNONHT.js +20 -0
- package/dist/m/plugins/{visitor-drafts-owner-Z5B2OQBR.js → visitor-drafts-owner-LHUHFU3L.js} +1 -1
- package/dist/m/plugins/{visitor-menu-4LQMKLPP.js → visitor-menu-FY4KM2SW.js} +1 -1
- package/dist/m/plugins/wire-M2NR354P.js +1 -0
- package/dist/m/{reader-core-POFQCKRV.js → reader-core-T2S3JC4E.js} +2 -2
- package/dist/m/sync/live-sync-Z64YREWQ.js +1 -0
- package/dist/m/ui/index-MGTWOW2C.js +1 -0
- package/dist/m/vendor/{sap-core.vendor-OA5SKL7Q.js → sap-core.vendor-QAEPMETX.js} +1 -1
- package/dist/offline-manifest.json +1250 -1082
- package/dist/server/core.cjs +132 -109
- package/dist/server/page-compaction-worker.cjs +5 -5
- package/dist/server/rooms.mjs +3 -1
- package/docs/guide.md +89 -44
- package/entries/clay-data.js +1 -1
- package/entries/sap.js +1 -1
- package/package.json +5 -3
- package/src/blockeditor/themes/content.css +70 -0
- package/src/blockeditor/themes/light-1.css +431 -0
- package/src/blockeditor/themes/light-1.source.css +270 -0
- package/src/blockeditor/themes/light-2.css +211 -0
- package/src/blockeditor/themes/light-3.css +130 -0
- package/src/blockeditor/themes/light-4.css +299 -0
- package/src/core/conflict-notice.js +28 -0
- package/src/core/draft-mode.js +6 -0
- package/src/core/host-meta.js +6 -6
- package/src/core/is-edit-mode.js +9 -3
- package/src/core/mode-support.js +3 -0
- package/src/core/mode-switch.js +4 -4
- package/src/core/mode-transition.js +3 -1
- package/src/core/page-format-wire.js +1 -1
- package/src/core/page-format.js +8 -8
- package/src/core/save-accepted.js +6 -7
- package/src/core/save-core.js +52 -23
- package/src/core/save-failure-notice.js +37 -0
- package/src/core/save-feedback.js +6 -0
- package/src/core/save-replace.js +1 -1
- package/src/core/save-size.js +2 -2
- package/src/core/save.js +78 -15
- package/src/core/site-edits.js +166 -0
- package/src/core/unsaved-warning.js +2 -2
- package/src/core/upgrade-run.js +4 -4
- package/src/lib/ai-comment.js +23 -0
- package/src/lib/effect.js +8 -0
- package/src/lib/idb-error-code.js +20 -0
- package/src/live/fast-lane.js +10 -10
- package/src/live/local-lane.js +10 -10
- package/src/live/page-compaction.js +8 -8
- package/src/live/server-lane.js +1 -1
- package/src/loader-logic.js +48 -18
- package/src/loader.js +11 -6
- package/src/page/adopt-boot.js +1 -1
- package/src/page/adopt.js +2 -2
- package/src/page/ai-area.js +182 -0
- package/src/page/ai-preview.js +80 -0
- package/src/page/ai-surface.js +257 -0
- package/src/page/ai-tool.js +308 -145
- package/src/page/announce.js +2 -2
- package/src/page/awareness.js +18 -18
- package/src/page/blocks.js +35 -34
- package/src/page/blocksel.js +2 -2
- package/src/page/board-drag.js +11 -11
- package/src/page/board-face.js +1 -1
- package/src/page/board-panel.js +26 -26
- package/src/page/board-style.js +127 -127
- package/src/page/board-view.js +48 -48
- package/src/page/chart.js +29 -29
- package/src/page/checkpoint-proof.js +19 -19
- package/src/page/chrome.js +74 -65
- package/src/page/clipboard-shape.js +1 -1
- package/src/page/csv-import.js +4 -4
- package/src/page/drag.js +5 -5
- package/src/page/editor.js +301 -296
- package/src/page/export-html.js +8 -8
- package/src/page/features/checklist.js +36 -36
- package/src/page/features/export-html.js +19 -19
- package/src/page/features/export-markdown.js +10 -10
- package/src/page/features/import-markdown.js +13 -13
- package/src/page/features/media.js +50 -50
- package/src/page/features/table.js +17 -17
- package/src/page/features/title-cover.js +17 -17
- package/src/page/features/title-icon.js +7 -7
- package/src/page/features/toc.js +1 -1
- package/src/page/features/tool.js +42 -18
- package/src/page/format-command.js +4 -4
- package/src/page/format-gate.js +6 -6
- package/src/page/format-host.js +6 -6
- package/src/page/format-level.js +3 -3
- package/src/page/format-native-commands.js +2 -2
- package/src/page/highlight-slot.js +1 -1
- package/src/page/highlight.js +4 -4
- package/src/page/history-intent.js +2 -2
- package/src/page/image-actions.js +4 -4
- package/src/page/link.js +10 -10
- package/src/page/media.js +3 -3
- package/src/page/mention-picker.js +7 -7
- package/src/page/merge.js +1 -1
- package/src/page/placeholder.js +3 -3
- package/src/page/projection.js +11 -11
- package/src/page/reader-patch.js +2 -2
- package/src/page/recover.js +26 -26
- package/src/page/recovery-ui.js +28 -28
- package/src/page/rich/chart.js +1 -1
- package/src/page/rich/page-tool.js +21 -6
- package/src/page/rich/tool-runtime.js +3 -2
- package/src/page/save-projection.js +2 -2
- package/src/page/save-states.js +9 -9
- package/src/page/schema.js +9 -9
- package/src/page/table-style.js +21 -21
- package/src/page/toc-link.js +1 -1
- package/src/page/tool-capability.js +8 -3
- package/src/page/tool-transport.js +52 -0
- package/src/page/tool-views.js +4 -0
- package/src/page/tools.js +6 -1
- package/src/page/view.js +6 -6
- package/src/page/ybind/binding.js +1 -1
- package/src/page/ybind/conversion.js +1 -1
- package/src/page/ybind/layout.js +4 -4
- package/src/page/ybind/migrate.js +1 -1
- package/src/page/ybind/origins.js +2 -2
- package/src/page/ybind/provider.js +2 -2
- package/src/page/ybind/recovery-proof.js +3 -3
- package/src/page/ybind/recovery-replay.js +2 -2
- package/src/plugins/ai-edit/picker.js +235 -0
- package/src/plugins/ai-edit.js +220 -34
- package/src/plugins/indicator-chip.js +39 -34
- package/src/plugins/indicator.js +13 -4
- package/src/plugins/live.js +21 -13
- package/src/plugins/menu/actions.js +89 -17
- package/src/plugins/menu/seo.js +32 -6
- package/src/plugins/menu/shell-style.js +26 -7
- package/src/plugins/menu/shell.js +12 -7
- package/src/plugins/menu/variants.js +6 -10
- package/src/plugins/menu/visitor.js +44 -34
- package/src/plugins/menu.js +3 -2
- package/src/plugins/page-view.js +2 -2
- package/src/plugins/page.js +257 -98
- package/src/plugins/sortable.js +7 -0
- package/src/plugins/upgrade.js +2 -1
- package/src/plugins/visitor-drafts/commit.js +303 -0
- package/src/plugins/visitor-drafts/idb.js +221 -0
- package/src/plugins/visitor-drafts/intro-style.js +1 -1
- package/src/plugins/visitor-drafts/intro.js +8 -25
- package/src/plugins/visitor-drafts/prompt.js +30 -4
- package/src/plugins/visitor-drafts/session.js +345 -0
- package/src/plugins/visitor-drafts/store.js +351 -32
- package/src/plugins/visitor-drafts.js +158 -27
- package/src/server/checkpoint/index.js +8 -8
- package/src/server/rooms/index.js +2 -2
- package/src/server/rooms/live-data-rooms.js +35 -35
- package/src/server/rooms/live-page-history.js +2 -2
- package/src/server/rooms/page-admission.js +34 -6
- package/src/server/rooms/page-conversion.js +1 -1
- package/src/server/rooms/page-lineage.js +4 -4
- package/src/server/rooms/page-rebase.js +4 -4
- package/src/server/rooms/page-room-conformance.js +14 -14
- package/src/server/rooms/page-state.js +2 -2
- package/src/server/rooms/page-write-admission.js +7 -7
- package/src/server/rooms/page-write-guard.js +3 -3
- package/src/sync/data-guard-notice.js +265 -0
- package/src/sync/live-sync.js +54 -11
- package/src/sync/page-relay.js +4 -4
- package/src/sync/presence-store.js +1 -1
- package/src/sync/presence-target.js +3 -3
- package/src/sync/presence.js +1 -1
- package/src/sync/stream.js +54 -13
- package/src/sync/wire-profile.js +8 -3
- package/src/ui/bevel.js +1 -1
- package/src/ui/index.js +9 -10
- package/src/ui/skins/cms.js +1 -1
- package/src/ui/skins/quickcrop.js +1 -1
- package/src/ui/skins/richclay.js +2 -2
- package/src/ui/toast-lazy.js +12 -3
- package/src/ui/toast.js +9 -3
- package/src/vendor/hyper-html-api-data.vendor.js +1 -1
- package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
- package/src/vendor/hypercms.vendor.js +16 -16
- package/src/vendor/richclay.vendor.js +48 -20
- package/src/vendor/sap-core.vendor.js +1 -1
- package/tool-generation/context.md +44 -0
- package/tool-generation/system.md +23 -0
- package/dist/m/c-24PMUJIV.js +0 -2
- package/dist/m/c-2AYVYEI4.js +0 -1
- package/dist/m/c-2AZDZWCK.js +0 -1
- package/dist/m/c-2L6O5PDS.js +0 -6
- package/dist/m/c-2RYOGUQM.js +0 -2
- package/dist/m/c-2ZVOEU4M.js +0 -1
- package/dist/m/c-37NAONZA.js +0 -1
- package/dist/m/c-3GJ4SDE2.js +0 -1
- package/dist/m/c-3O7OBDCM.js +0 -1
- package/dist/m/c-5OJ7YF2N.js +0 -1
- package/dist/m/c-5WMXEKFH.js +0 -135
- package/dist/m/c-6RDOKLN7.js +0 -1
- package/dist/m/c-A4IP6MBC.js +0 -1
- package/dist/m/c-A6L3FMUS.js +0 -1
- package/dist/m/c-ATKAOH23.js +0 -1
- package/dist/m/c-B6O7AJYG.js +0 -32
- package/dist/m/c-BCGWJYXW.js +0 -1
- package/dist/m/c-BGKAT2FL.js +0 -7
- package/dist/m/c-BYTKL6UX.js +0 -1
- package/dist/m/c-DHBRA6DL.js +0 -1
- package/dist/m/c-DTMZ3ZHI.js +0 -1
- package/dist/m/c-E3ZXARWA.js +0 -1
- package/dist/m/c-EK63G3CG.js +0 -1
- package/dist/m/c-ELYIIYRS.js +0 -1
- package/dist/m/c-EXKHPOVC.js +0 -3
- package/dist/m/c-FCSZ3DC5.js +0 -20
- package/dist/m/c-FTWGYI5E.js +0 -1
- package/dist/m/c-G4PWUDD5.js +0 -1
- package/dist/m/c-GBKB5WDL.js +0 -2
- package/dist/m/c-GBTVCJXC.js +0 -1
- package/dist/m/c-GC2HICFJ.js +0 -1
- package/dist/m/c-GRWMD6O5.js +0 -1
- package/dist/m/c-HCNWJE4A.js +0 -4
- package/dist/m/c-HG2C77BM.js +0 -4
- package/dist/m/c-JGHEACGK.js +0 -1
- package/dist/m/c-JGLX6TBK.js +0 -1
- package/dist/m/c-JNRKHYWO.js +0 -1
- package/dist/m/c-KBS3UV6B.js +0 -1
- package/dist/m/c-KQ7E3PYY.js +0 -1
- package/dist/m/c-L3F5ZLIV.js +0 -1
- package/dist/m/c-L4KMT7FO.js +0 -6
- package/dist/m/c-L5VFATT6.js +0 -1
- package/dist/m/c-LCYPMULG.js +0 -1
- package/dist/m/c-LIGQYZ7M.js +0 -6
- package/dist/m/c-LKIWEPAV.js +0 -1
- package/dist/m/c-MPSKIU2I.js +0 -1
- package/dist/m/c-N7JPUPGC.js +0 -1
- package/dist/m/c-NK33MMYF.js +0 -1
- package/dist/m/c-NQLWOHLH.js +0 -10
- package/dist/m/c-NTHUTABN.js +0 -1
- package/dist/m/c-NWX3MFT4.js +0 -1
- package/dist/m/c-OKXI52N7.js +0 -1
- package/dist/m/c-OPONJ2UK.js +0 -128
- package/dist/m/c-OWQWOGYI.js +0 -1
- package/dist/m/c-P6Q2ZTFQ.js +0 -1
- package/dist/m/c-QEN6HZCA.js +0 -1
- package/dist/m/c-QIXT3Q7C.js +0 -28
- package/dist/m/c-QM5LXRDT.js +0 -9
- package/dist/m/c-QYYUVLVG.js +0 -1
- package/dist/m/c-RQRMPZXD.js +0 -1
- package/dist/m/c-SBCBXKOZ.js +0 -26
- package/dist/m/c-SKF5HL4I.js +0 -4
- package/dist/m/c-SRPC3VBE.js +0 -1
- package/dist/m/c-TANBH7FI.js +0 -1
- package/dist/m/c-THGQQDYY.js +0 -1
- package/dist/m/c-UD5JZTDL.js +0 -1
- package/dist/m/c-UDGEFWNU.js +0 -1
- package/dist/m/c-UGNV5MFB.js +0 -1
- package/dist/m/c-VIRFF5HG.js +0 -8
- package/dist/m/c-VJYEVRYK.js +0 -1
- package/dist/m/c-VQCEOGKH.js +0 -13
- package/dist/m/c-VYJ7O4RI.js +0 -34
- package/dist/m/c-W7IPVCAA.js +0 -1
- package/dist/m/c-WBGWGOM7.js +0 -1
- package/dist/m/c-WJ5DRBHT.js +0 -72
- package/dist/m/c-X5WC4PX5.js +0 -1
- package/dist/m/c-XCFQEWW6.js +0 -3
- package/dist/m/c-XGH4GS2T.js +0 -1
- package/dist/m/c-XLQ3BDMM.js +0 -1
- package/dist/m/c-ZQXNHZZT.js +0 -72
- package/dist/m/core/conflict-notice-42H4ASLA.js +0 -1
- package/dist/m/core/draft-mode-WSQAT67S.js +0 -1
- package/dist/m/core/save-ESPSQPLV.js +0 -1
- package/dist/m/core/save-core-6SQHOR6W.js +0 -1
- package/dist/m/loader-FBDE2UAB.js +0 -1
- package/dist/m/manifest-1D2F5CA5.js +0 -1
- package/dist/m/manifest-edit-F7CC5CFD.js +0 -1
- package/dist/m/page/editor-OSKOW6RS.js +0 -304
- package/dist/m/plugins/ai-edit-VV4QTINH.js +0 -6
- package/dist/m/plugins/indicator-WVPGMOZ7.js +0 -1
- package/dist/m/plugins/menu-Q4MFFQ6E.js +0 -1
- package/dist/m/plugins/page-OAWUJQEL.js +0 -2
- package/dist/m/plugins/page-view-DZ26ZDDQ.js +0 -1
- package/dist/m/plugins/richclay-PGOOJ4OK.js +0 -517
- package/dist/m/plugins/sortable-266HY6TR.js +0 -1
- package/dist/m/plugins/upgrade-BNZVFHMZ.js +0 -1
- package/dist/m/plugins/visitor-drafts-2AUYKAIJ.js +0 -20
- package/dist/m/plugins/wire-W2EV36KZ.js +0 -1
- package/dist/m/sync/live-sync-XGUPHI3I.js +0 -1
- package/dist/m/ui/index-VGYAKAKI.js +0 -1
- package/dist/page/themes/light-1.css +0 -431
- package/src/page/themes/content.css +0 -70
- package/src/page/themes/light-1.css +0 -431
- package/src/page/themes/light-1.source.css +0 -270
- package/src/page/themes/light-2.css +0 -211
- package/src/page/themes/light-3.css +0 -130
- package/src/page/themes/light-4.css +0 -299
- /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
- /package/dist/m/plugins/{quickcrop-WWF5I422.js.LEGAL.txt → quickcrop-X5MRIBAP.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-PGOOJ4OK.js.LEGAL.txt → richclay-Y2NV44NA.js.LEGAL.txt} +0 -0
package/src/plugins/ai-edit.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*
|
|
22
22
|
* The bus version previewed the reply as it streamed. The wire carries status lines
|
|
23
23
|
* only — replaceable progress, capped and droppable — so the page shows throttled
|
|
24
|
-
* status lines ("Writing, 1.8 KB") and then the result once, with Keep/
|
|
24
|
+
* status lines ("Writing, 1.8 KB") and then the result once, with Keep/Discard as
|
|
25
25
|
* before. `@page` reads the saved file from disk rather than a copy in the payload:
|
|
26
26
|
* the plugin saves first and then sends `{ page: true }`, because a whole page never
|
|
27
27
|
* fits the envelope.
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
* The comment box is compose only: a click anywhere outside it closes it, keeping the
|
|
30
30
|
* text as a draft for that target, and Send moves the whole live state to one compact
|
|
31
31
|
* bar at the bottom of the viewport. The bar carries the host's progress and Stop, the
|
|
32
|
-
* reply's warnings and Keep/
|
|
32
|
+
* reply's warnings and Keep/Discard, and then the save, so no part of a running edit
|
|
33
33
|
* depends on a popover the person may want out of the way.
|
|
34
34
|
*
|
|
35
35
|
* Undo integration: observers pause at request start. On Keep the element is rewound
|
|
36
36
|
* to the snapshot while still paused, observers resume, then one final morph lands
|
|
37
|
-
* the whole AI edit as a single undoable step. On
|
|
37
|
+
* the whole AI edit as a single undoable step. On Discard the rewind happens under
|
|
38
38
|
* pause, so history never sees the edit at all.
|
|
39
39
|
*
|
|
40
40
|
* All injected chrome is runtime-only (`no-save no-watch no-snapshot`): never saved,
|
|
@@ -62,10 +62,7 @@ const UNIT_SELECTOR = "h1,h2,h3,h4,h5,h6,p,figure";
|
|
|
62
62
|
const BLOCK_SELECTOR = "p,h1,h2,h3,h4,h5,h6,li,dt,dd,blockquote,pre,figcaption,td,th";
|
|
63
63
|
const CONTAINER_SELECTOR = "section,article,aside,header,footer,nav,main,div,ul,ol,dl,table,figure,form";
|
|
64
64
|
const NOT_TEXT = "script,style,template,textarea,input,select,button,iframe,svg,math";
|
|
65
|
-
|
|
66
|
-
// here, where the message can say what to do about it.
|
|
67
|
-
const MAX_PAYLOAD_BYTES = 900 * 1024;
|
|
68
|
-
const TOO_LARGE = "This section is too large for AI editing; select a smaller part.";
|
|
65
|
+
const TOO_LARGE = "This section is too large for AI editing. Select a smaller part.";
|
|
69
66
|
const SAVE_FAILED = "The save did not finish; the edit is still on the page.";
|
|
70
67
|
|
|
71
68
|
let requestCounter = 0;
|
|
@@ -98,6 +95,7 @@ let helperState = null; // "ready" or "unavailable"
|
|
|
98
95
|
let listeners = null; // the AbortController every handler one session added listens on
|
|
99
96
|
let observers = []; // the page observers that session opened
|
|
100
97
|
let styleEl = null; // the one stylesheet the chrome needed
|
|
98
|
+
let picker = null; // the area picker while it is over the page
|
|
101
99
|
let chromeToken = 0; // the latest init(); a teardown retires the one before it
|
|
102
100
|
|
|
103
101
|
function dropDraft(el) {
|
|
@@ -127,6 +125,15 @@ async function helperStateNow() {
|
|
|
127
125
|
return helper && (helper.state === 'ready' || helper.state === 'unavailable') ? helper.state : null;
|
|
128
126
|
}
|
|
129
127
|
|
|
128
|
+
// The page half of "rewrite a chosen area", read at use time: it is null until the
|
|
129
|
+
// page plugin's own dynamic import lands, and on any page that is no Block document.
|
|
130
|
+
const pageAi = () => window.clay?.blockeditor?.ai || null;
|
|
131
|
+
|
|
132
|
+
// A Block document whose own surface has not loaded yet (or failed): the DOM path
|
|
133
|
+
// must stand down, because morphing the editor would replace it.
|
|
134
|
+
const isBlockDoc = () => !!window.clay?.editPlugins?.includes('blockeditor') && !!document.querySelector('article[data-blockeditor]');
|
|
135
|
+
const blockDocWithoutSurface = () => isBlockDoc() && !pageAi()?.active();
|
|
136
|
+
|
|
130
137
|
// ---------------------------------------------------------------- observers (undo/autosave)
|
|
131
138
|
|
|
132
139
|
function pauseObservers() {
|
|
@@ -168,6 +175,9 @@ function liveRange() {
|
|
|
168
175
|
// else it is the block around the whole selection, so a backward selection and a
|
|
169
176
|
// forward one land on the same element.
|
|
170
177
|
function targetFromRange(range) {
|
|
178
|
+
// A Block document has its own selection toolbar, and its blocks are ProseMirror
|
|
179
|
+
// DOM: the page surface names the area instead.
|
|
180
|
+
if (pageAi() || blockDocWithoutSurface()) return null;
|
|
171
181
|
let node = range.commonAncestorContainer;
|
|
172
182
|
if (node.nodeType !== 1) node = node.parentElement;
|
|
173
183
|
if (!node || !document.body.contains(node)) return null;
|
|
@@ -270,16 +280,8 @@ function candidateFrom(html, tag) {
|
|
|
270
280
|
|
|
271
281
|
// ---------------------------------------------------------------- session lifecycle
|
|
272
282
|
|
|
273
|
-
function newSession(el, comment, quote) {
|
|
283
|
+
function newSession(el, comment, quote, { contextRefsOf, wantsPage }) {
|
|
274
284
|
const docMode = el === document.body;
|
|
275
|
-
// Context refs are @tokens containing a dot or slash (@notes.md, @src/x.js). Bare
|
|
276
|
-
// @words are not refs: a leading one is an engine token (@fable, @codex — routed
|
|
277
|
-
// helper-side), and mid-text ones are prose.
|
|
278
|
-
// The @ has to start a word, so an email address or a URL is prose too.
|
|
279
|
-
const contextRefs = [...comment.matchAll(/(?<![\w\u00C0-\u024F.@\/:-])@([\w.-]*[/.][\w./-]*)/g)]
|
|
280
|
-
.map(m => m[1].replace(/\.+$/, '')) // a sentence's full stop is not part of the name
|
|
281
|
-
.filter(ref => ref !== 'page')
|
|
282
|
-
.filter(ref => /[/.]/.test(ref));
|
|
283
285
|
const elementHTML = docMode ? strippedBodyHTML() : el.outerHTML;
|
|
284
286
|
const payload = {
|
|
285
287
|
id: 'req-' + Date.now().toString(36) + '-' + (++requestCounter),
|
|
@@ -287,7 +289,7 @@ function newSession(el, comment, quote) {
|
|
|
287
289
|
tag: el.tagName.toLowerCase(),
|
|
288
290
|
elementHTML,
|
|
289
291
|
comment,
|
|
290
|
-
contextRefs
|
|
292
|
+
contextRefs: contextRefsOf(comment)
|
|
291
293
|
};
|
|
292
294
|
if (quote) {
|
|
293
295
|
payload.quote = quote.text;
|
|
@@ -295,7 +297,7 @@ function newSession(el, comment, quote) {
|
|
|
295
297
|
}
|
|
296
298
|
// Document mode reads the live page itself, and every other @page request reads
|
|
297
299
|
// the file from disk: the plugin saves first, then asks for it by flag.
|
|
298
|
-
if (!docMode &&
|
|
300
|
+
if (!docMode && wantsPage(comment)) payload.page = true;
|
|
299
301
|
return {
|
|
300
302
|
id: payload.id,
|
|
301
303
|
el,
|
|
@@ -312,7 +314,11 @@ function newSession(el, comment, quote) {
|
|
|
312
314
|
}
|
|
313
315
|
|
|
314
316
|
async function sendRequest(el, comment, quote) {
|
|
315
|
-
|
|
317
|
+
if (el.aiTarget) return sendTargetRequest(el, comment, quote);
|
|
318
|
+
// The comment conventions load here, where a request is sent, not at boot: a page
|
|
319
|
+
// whose composer is never typed into does not fetch them.
|
|
320
|
+
const { MAX_PAYLOAD_BYTES, contextRefsOf, wantsPage } = await import("../lib/ai-comment.js");
|
|
321
|
+
session = newSession(el, comment, quote, { contextRefsOf, wantsPage });
|
|
316
322
|
// Refused here, before anything is sent or paused: the envelope would refuse it
|
|
317
323
|
// anyway, and a refusal the page cannot explain is worse than one it can.
|
|
318
324
|
if (new Blob([JSON.stringify(session.payload)]).size > MAX_PAYLOAD_BYTES) {
|
|
@@ -352,6 +358,71 @@ async function sendRequest(el, comment, quote) {
|
|
|
352
358
|
else onError(outcome.error || 'the helper reported an error');
|
|
353
359
|
}
|
|
354
360
|
|
|
361
|
+
// A page area's request. Nothing morphs and no save hold is taken, so the page keeps
|
|
362
|
+
// autosaving while a person types during a long request, and Keep is the target's own
|
|
363
|
+
// one undoable transaction. Only the bar and the panel belong to this plugin here.
|
|
364
|
+
async function sendTargetRequest(target, comment, quote) {
|
|
365
|
+
const live = {
|
|
366
|
+
id: null,
|
|
367
|
+
el: target,
|
|
368
|
+
target,
|
|
369
|
+
docMode: false,
|
|
370
|
+
state: 'requesting',
|
|
371
|
+
paused: false,
|
|
372
|
+
held: false,
|
|
373
|
+
handle: null,
|
|
374
|
+
finalCandidate: null
|
|
375
|
+
};
|
|
376
|
+
session = live;
|
|
377
|
+
// A refusal is the box's answer: the comment stays in it, unsent and still a draft.
|
|
378
|
+
let started;
|
|
379
|
+
try {
|
|
380
|
+
started = await target.start(comment, quote);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
onError(error?.message || 'the helper reported an error');
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (session !== live) return; // stopped while the request was being built
|
|
386
|
+
if (started.status !== 'ok') {
|
|
387
|
+
session = null;
|
|
388
|
+
setStatus(started.message, 'warn');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
live.id = started.id;
|
|
392
|
+
keepDraft(target, comment);
|
|
393
|
+
closePanelForSend();
|
|
394
|
+
barLifecycle = {};
|
|
395
|
+
showBar('working');
|
|
396
|
+
setBarStatus('Sending\u2026');
|
|
397
|
+
const handle = started.send({
|
|
398
|
+
onStatus: ({ text }) => { if (session === live) setBarStatus(text); }
|
|
399
|
+
});
|
|
400
|
+
live.handle = handle;
|
|
401
|
+
const outcome = await handle.done;
|
|
402
|
+
if (session !== live) return;
|
|
403
|
+
if (outcome.state !== 'done') {
|
|
404
|
+
if (outcome.state === 'cancelled') onError('HTML Clay stopped this edit.');
|
|
405
|
+
else onError(outcome.error || 'the helper reported an error');
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
live.state = 'deciding';
|
|
409
|
+
let result;
|
|
410
|
+
try {
|
|
411
|
+
result = await target.propose(outcome.result || {});
|
|
412
|
+
} catch (error) {
|
|
413
|
+
onError(error?.message || 'the reply could not be applied');
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (session !== live) return;
|
|
417
|
+
if (result.status !== 'ready') {
|
|
418
|
+
onError(result.message);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
dropDraft(target);
|
|
422
|
+
showBar('ready');
|
|
423
|
+
setBarStatus(readyText(result.warnings || [], (outcome.result || {}).model), result.warnings?.length ? 'warn' : '');
|
|
424
|
+
}
|
|
425
|
+
|
|
355
426
|
// What a reply may not add: anything that runs code. Each part is counted, and the
|
|
356
427
|
// reply is refused when it holds more of any part than the element the model was
|
|
357
428
|
// given, so a page's own scripts and handlers are not a reason to refuse an edit
|
|
@@ -469,6 +540,11 @@ function revertSession() {
|
|
|
469
540
|
const s = session;
|
|
470
541
|
session = null;
|
|
471
542
|
if (!s) return;
|
|
543
|
+
if (s.target) {
|
|
544
|
+
s.target.discard();
|
|
545
|
+
positionChrome();
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
472
548
|
try {
|
|
473
549
|
if ((s.state === 'requesting' || s.state === 'deciding') && loadedEngine()) {
|
|
474
550
|
sessionMorph(s, s.snapshot, { rewind: true });
|
|
@@ -484,6 +560,7 @@ async function keepSession() {
|
|
|
484
560
|
const s = session;
|
|
485
561
|
if (!s) return;
|
|
486
562
|
session = null;
|
|
563
|
+
if (s.target) return keepTarget(s.target);
|
|
487
564
|
// What is on screen is what Keep keeps: the preview may have been edited by hand.
|
|
488
565
|
const liveResult = s.docMode ? strippedBodyHTML() : s.el.outerHTML;
|
|
489
566
|
try {
|
|
@@ -509,6 +586,29 @@ async function keepSession() {
|
|
|
509
586
|
scheduleBarClose();
|
|
510
587
|
}
|
|
511
588
|
|
|
589
|
+
// The page area keeps itself: one transaction, its own preview cleared, and the save
|
|
590
|
+
// flushed by the surface. A stale result touches nothing and says so on the bar.
|
|
591
|
+
async function keepTarget(target) {
|
|
592
|
+
positionChrome();
|
|
593
|
+
showBar('saving');
|
|
594
|
+
setBarStatus('Saving\u2026');
|
|
595
|
+
const lifecycle = barLifecycle;
|
|
596
|
+
let result;
|
|
597
|
+
try {
|
|
598
|
+
result = await target.keep();
|
|
599
|
+
} catch (error) {
|
|
600
|
+
result = { status: 'failed', message: error?.message || SAVE_FAILED };
|
|
601
|
+
}
|
|
602
|
+
if (lifecycle !== barLifecycle) return;
|
|
603
|
+
if (!result || result.status !== 'kept') {
|
|
604
|
+
showBar('error');
|
|
605
|
+
setBarStatus((result && result.message) || SAVE_FAILED, 'warn');
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
setBarStatus(result.message || 'Saved');
|
|
609
|
+
scheduleBarClose();
|
|
610
|
+
}
|
|
611
|
+
|
|
512
612
|
// Stop is live from the moment the request starts, which includes the window where an
|
|
513
613
|
// @page save is still in flight and there is no handle yet. Nothing is left on screen:
|
|
514
614
|
// the edit is rewound and the bar goes with it.
|
|
@@ -613,7 +713,7 @@ function buildChrome() {
|
|
|
613
713
|
|
|
614
714
|
barStop = part(bevelButton('Stop', { extra: ['flex:none'] }), 'bar-stop');
|
|
615
715
|
barKeep = part(bevelButton('Keep', { variant: 'primary', extra: ['flex:none'] }), 'bar-keep');
|
|
616
|
-
barRevert = part(bevelButton('
|
|
716
|
+
barRevert = part(bevelButton('Discard', { variant: 'quiet', extra: ['flex:none'] }), 'bar-revert');
|
|
617
717
|
barClose = part(bevelIconButton(GLYPHS.toastClose, { label: 'Dismiss error' }), 'bar-close');
|
|
618
718
|
barClose.pin({ flex: 'none' });
|
|
619
719
|
for (const control of [barStop, barKeep, barRevert, barClose]) setShown(control, false, 'inline-flex');
|
|
@@ -887,6 +987,10 @@ function closeBar(lifecycle) {
|
|
|
887
987
|
// The ring follows the target while anything is on screen for it, which includes every
|
|
888
988
|
// bar mode; the panel is placed only while it is the thing on screen.
|
|
889
989
|
function positionChrome() {
|
|
990
|
+
// A Block document keeps a fixed bar of its own at the bottom on a phone, and the
|
|
991
|
+
// editor already reserves the room for it: the status bar sits above that.
|
|
992
|
+
const coarse = window.matchMedia?.('(pointer: coarse)')?.matches;
|
|
993
|
+
set(bar, 'bottom', pageAi()?.active() && coarse ? '112px' : '16px');
|
|
890
994
|
if (!anchorEl) return;
|
|
891
995
|
if (!anchorEl.isConnected) { abandonTarget(); return; }
|
|
892
996
|
if (anchorEl === document.body) {
|
|
@@ -933,9 +1037,13 @@ function positionChrome() {
|
|
|
933
1037
|
}
|
|
934
1038
|
|
|
935
1039
|
function openPanel(el, quote) {
|
|
936
|
-
// A composer already open on
|
|
937
|
-
//
|
|
938
|
-
|
|
1040
|
+
// A composer already open on the same target keeps its box as it is. On another target
|
|
1041
|
+
// its text travels with the person and the old target's draft is cleared.
|
|
1042
|
+
const wasOpen = !panel.hidden;
|
|
1043
|
+
const sameTarget = wasOpen && anchorEl === el;
|
|
1044
|
+
const changedTarget = wasOpen && !!anchorEl && anchorEl !== el;
|
|
1045
|
+
const carried = changedTarget ? textarea.value : null;
|
|
1046
|
+
if (changedTarget) dropDraft(anchorEl);
|
|
939
1047
|
const range = quote ? liveRange() : null; // read before focus moves into the panel
|
|
940
1048
|
const active = document.activeElement;
|
|
941
1049
|
returnFocus = active && active !== document.body && !panel.contains(active) ? active : null;
|
|
@@ -944,10 +1052,12 @@ function openPanel(el, quote) {
|
|
|
944
1052
|
hideChip();
|
|
945
1053
|
setShown(panel, true);
|
|
946
1054
|
pendingSelection = quote;
|
|
947
|
-
const shown = quote
|
|
1055
|
+
const shown = quote
|
|
1056
|
+
? (quote.text.trim().length > 400 ? quote.text.trim().slice(0, 400) + '\u2026' : quote.text.trim())
|
|
1057
|
+
: (el.aiTarget ? el.label : '');
|
|
948
1058
|
panel.dataset.quote = shown;
|
|
949
1059
|
setStatus('');
|
|
950
|
-
textarea.value = drafts.get(el) || '';
|
|
1060
|
+
if (!sameTarget) textarea.value = carried ? carried : (drafts.get(el) || '');
|
|
951
1061
|
highlight(anchorRange);
|
|
952
1062
|
applyHelperState();
|
|
953
1063
|
positionChrome();
|
|
@@ -975,6 +1085,7 @@ function closePanel() {
|
|
|
975
1085
|
returnFocus = null;
|
|
976
1086
|
pendingSelection = undefined;
|
|
977
1087
|
clearHighlight();
|
|
1088
|
+
picker?.close();
|
|
978
1089
|
setShown(panel, false);
|
|
979
1090
|
setShown(ring, false);
|
|
980
1091
|
textarea.value = '';
|
|
@@ -985,6 +1096,7 @@ function closePanel() {
|
|
|
985
1096
|
// The send path closes the box without cancelling anything and without letting the
|
|
986
1097
|
// target go: the request is now about that element, and the ring stays on it.
|
|
987
1098
|
function closePanelForSend() {
|
|
1099
|
+
picker?.close();
|
|
988
1100
|
anchorRange = null;
|
|
989
1101
|
returnFocus = null;
|
|
990
1102
|
pendingSelection = undefined;
|
|
@@ -1063,7 +1175,7 @@ function wireInteractions() {
|
|
|
1063
1175
|
const startedInPanel = panelPointerDown;
|
|
1064
1176
|
panelPointerDown = false;
|
|
1065
1177
|
if (startedInPanel) return;
|
|
1066
|
-
if (panel.contains(event.target) || bar.contains(event.target) || chip.contains(event.target) || docBubble.contains(event.target)) return;
|
|
1178
|
+
if (panel.contains(event.target) || bar.contains(event.target) || chip.contains(event.target) || docBubble.contains(event.target) || picker?.contains(event.target)) return;
|
|
1067
1179
|
if (session || barMode) return; // one edit at a time, and the bar owns the screen
|
|
1068
1180
|
const section = event.target.closest?.('[data-edit-id]');
|
|
1069
1181
|
const unit = section ? unitFrom(event.target) : null;
|
|
@@ -1093,6 +1205,17 @@ function wireInteractions() {
|
|
|
1093
1205
|
document.addEventListener('keydown', (event) => {
|
|
1094
1206
|
if (isShortcut(event)) {
|
|
1095
1207
|
if (barMode || session || pageOwnsFocus()) return;
|
|
1208
|
+
const ai = pageAi();
|
|
1209
|
+
if (ai?.active()) {
|
|
1210
|
+
const chosen = ai.targetFromSelection();
|
|
1211
|
+
// Nothing chosen: leave the key to the browser.
|
|
1212
|
+
if (!chosen) return;
|
|
1213
|
+
event.preventDefault();
|
|
1214
|
+
openPanel(chosen, chosen.quote);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
// A Block document whose surface is not ready has no DOM path to fall back on.
|
|
1218
|
+
if (blockDocWithoutSurface()) return;
|
|
1096
1219
|
if (anchorEl && !anchorEl.isConnected) closePanel();
|
|
1097
1220
|
const range = liveRange();
|
|
1098
1221
|
let target = range ? targetFromRange(range) : null;
|
|
@@ -1141,6 +1264,13 @@ function build() {
|
|
|
1141
1264
|
wireInteractions();
|
|
1142
1265
|
}
|
|
1143
1266
|
|
|
1267
|
+
// Whether this host lists an ai-edit helper at all, ready or turned off. The menu asks
|
|
1268
|
+
// before it offers Ask AI, so a page whose host cannot answer never shows the item.
|
|
1269
|
+
async function available() {
|
|
1270
|
+
await (boot || loadHelper());
|
|
1271
|
+
return helperState === 'ready' || helperState === 'unavailable';
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1144
1274
|
async function init() {
|
|
1145
1275
|
if (!startsAtImport() || panel) return; // an owner/editor feature, built once
|
|
1146
1276
|
if (!(await loadHelper())) return;
|
|
@@ -1164,18 +1294,72 @@ function canOpenTarget(el) {
|
|
|
1164
1294
|
// are not valid targets. A listed but disabled helper opens the existing setup
|
|
1165
1295
|
// explanation with Send unavailable, just like the keyboard shortcut.
|
|
1166
1296
|
async function open(el, { prompt } = {}) {
|
|
1297
|
+
// A Block document is rewritten through its own surface: only the whole document
|
|
1298
|
+
// is a DOM entry point, and any element inside the editor is refused. This is the
|
|
1299
|
+
// guard that keeps the morph path off the editor.
|
|
1300
|
+
if (blockDocWithoutSurface()) return false;
|
|
1301
|
+
if (pageAi()?.active() && el !== document.body) return false;
|
|
1167
1302
|
if (!canOpenTarget(el)) return false;
|
|
1168
1303
|
await init();
|
|
1169
1304
|
// A click listener that calls this runs its microtasks before the same click reaches
|
|
1170
1305
|
// the click-away handler, so the opening has to wait for that event to finish.
|
|
1171
1306
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
1172
|
-
if (!panel || session || barMode
|
|
1173
|
-
|
|
1307
|
+
if (!panel || session || barMode) return false;
|
|
1308
|
+
const ai = pageAi();
|
|
1309
|
+
const target = ai?.active() ? ai.wholePage() : el;
|
|
1310
|
+
if (target === el ? !canOpenTarget(el) : !target.isConnected) return false;
|
|
1311
|
+
if (!panel.hidden && anchorEl === target) {
|
|
1312
|
+
textarea.focus();
|
|
1313
|
+
return true;
|
|
1314
|
+
}
|
|
1315
|
+
if (typeof prompt === 'string' && !drafts.has(target)) keepDraft(target, prompt);
|
|
1316
|
+
openPanel(target, null);
|
|
1317
|
+
return true;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// `clay.aiEdit.openTarget(target)` opens the composer on a target the page built
|
|
1321
|
+
// itself, such as a tool block's Revise or the new tool `/ai` asked for. Returns true
|
|
1322
|
+
// when the composer is showing for it.
|
|
1323
|
+
async function openTarget(target) {
|
|
1324
|
+
if (!target || target.aiTarget !== true) return false;
|
|
1325
|
+
await init();
|
|
1326
|
+
// The click or the key that asked is still travelling to the click-away handler, as
|
|
1327
|
+
// in open().
|
|
1328
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
1329
|
+
if (!panel || session || barMode) return false;
|
|
1330
|
+
if (!target.isConnected) return false;
|
|
1331
|
+
if (!panel.hidden && anchorEl === target) {
|
|
1174
1332
|
textarea.focus();
|
|
1175
1333
|
return true;
|
|
1176
1334
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1335
|
+
openPanel(target, target.quote ?? null);
|
|
1336
|
+
return true;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// `await clay.aiEdit.pick()` starts the area picker: the page under the pointer, one
|
|
1340
|
+
// click per block, and the composer on whatever is chosen. A Block document names its
|
|
1341
|
+
// own targets; anywhere else the picker hit-tests the page itself. Returns true when
|
|
1342
|
+
// the picker is up, false when there is nothing to pick with.
|
|
1343
|
+
async function pick() {
|
|
1344
|
+
await init();
|
|
1345
|
+
if (blockDocWithoutSurface()) return false;
|
|
1346
|
+
// The click that called this is still travelling to the click-away handler, exactly
|
|
1347
|
+
// as in open().
|
|
1348
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
1349
|
+
if (!panel || session || barMode) return false;
|
|
1350
|
+
if (picker?.picking) return true;
|
|
1351
|
+
const ai = pageAi();
|
|
1352
|
+
// The overlay loads here, where it is opened, not at boot. A failed fetch is the
|
|
1353
|
+
// same answer as nothing to pick with: the menu says so and nothing else changes.
|
|
1354
|
+
const mod = await import("./ai-edit/picker.js").catch(() => null);
|
|
1355
|
+
if (!mod) return false;
|
|
1356
|
+
picker = mod.createPicker({
|
|
1357
|
+
surface: ai?.active() ? ai : null,
|
|
1358
|
+
onPicked: (target) => openPanel(target, target.quote),
|
|
1359
|
+
onWholePage: () => openPanel(ai?.active() ? ai.wholePage() : document.body),
|
|
1360
|
+
onCancel: () => { if (!panel.hidden) closePanel(); },
|
|
1361
|
+
});
|
|
1362
|
+
picker.open();
|
|
1179
1363
|
return true;
|
|
1180
1364
|
}
|
|
1181
1365
|
|
|
@@ -1206,6 +1390,8 @@ function teardown() {
|
|
|
1206
1390
|
listeners = null;
|
|
1207
1391
|
for (const observer of observers) observer.disconnect();
|
|
1208
1392
|
observers = [];
|
|
1393
|
+
picker?.close();
|
|
1394
|
+
picker = null;
|
|
1209
1395
|
for (const node of [styleEl, ring, panel, bar, chip, docBubble]) node?.remove();
|
|
1210
1396
|
styleEl = ring = panel = textarea = statusEl = pointer = bar = barStatus = null;
|
|
1211
1397
|
barStop = barKeep = barRevert = barClose = chip = docBubble = null;
|
|
@@ -1231,7 +1417,7 @@ function teardown() {
|
|
|
1231
1417
|
* the committed mode is still view; the commit that enters edit builds the chrome, inert
|
|
1232
1418
|
* until somebody opens it, exactly as `clay.aiEdit.init()` does. What a session adds is
|
|
1233
1419
|
* everything still unresolved — a request on the wire, a preview waiting for Keep or
|
|
1234
|
-
*
|
|
1420
|
+
* Discard, a comment typed and not sent — so `quiesce` refuses the exit and `resume`
|
|
1235
1421
|
* leaves that same preview, comment and selection timer exactly as they were.
|
|
1236
1422
|
* `dispose` (after the barrier) stops every editor-only handler and takes the chrome
|
|
1237
1423
|
* down, so a save or selection event in view draws nothing. `initial: true` adopts the
|
|
@@ -1249,10 +1435,10 @@ export function aiEditParticipant() {
|
|
|
1249
1435
|
if (!initial) build();
|
|
1250
1436
|
},
|
|
1251
1437
|
quiesce() {
|
|
1252
|
-
const unsent =
|
|
1253
|
-
[...drafted].some((el) => el.isConnected);
|
|
1438
|
+
const unsent = panel && !panel.hidden && textarea.value.trim() !== '';
|
|
1254
1439
|
const pending = [];
|
|
1255
1440
|
if (session) pending.push(session.id);
|
|
1441
|
+
if (picker?.picking) pending.push('picker');
|
|
1256
1442
|
if (barMode === 'saving') pending.push('save');
|
|
1257
1443
|
if (unsent) pending.push('prompt');
|
|
1258
1444
|
if (!pending.length) return;
|
|
@@ -1276,4 +1462,4 @@ onDomReady(() => { boot = init().then(() => {}, () => {}); });
|
|
|
1276
1462
|
|
|
1277
1463
|
export { aiEditParticipant as modeParticipant };
|
|
1278
1464
|
|
|
1279
|
-
export const aiEdit = { init, open };
|
|
1465
|
+
export const aiEdit = { init, open, openTarget, pick, available };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { set, style } from "../lib/hostile-css.js";
|
|
4
4
|
import { bevelSurface, pageScheme, paintLook } from "../ui/bevel-controls.js";
|
|
5
5
|
import { TOKENS, FONT_SANS } from "../ui/bevel.js";
|
|
6
|
-
import { saveFeedback } from "../core/save-feedback.js";
|
|
6
|
+
import { saveFeedback, FAILURE_CLAIMED } from "../core/save-feedback.js";
|
|
7
7
|
|
|
8
8
|
// No 'conflict' here on purpose. core/conflict-notice.js owns that state now,
|
|
9
9
|
// and it ships in every document rather than only the ones that turned this chip on.
|
|
@@ -12,25 +12,20 @@ import { saveFeedback } from "../core/save-feedback.js";
|
|
|
12
12
|
// leaves core unaware that this plugin exists, which is the direction that
|
|
13
13
|
// dependency has to point.
|
|
14
14
|
const LABELS = {
|
|
15
|
-
saving: "Saving…",
|
|
16
|
-
saved: "Saved",
|
|
17
15
|
error: "Couldn't save",
|
|
18
|
-
offline: "Offline,
|
|
19
|
-
retrying: "Saving…",
|
|
16
|
+
offline: "Offline, will retry",
|
|
20
17
|
blocked: "Can't save here",
|
|
21
|
-
pending: "Saving…",
|
|
22
|
-
acknowledged: "Saving…",
|
|
23
|
-
checkpointed: "Saved",
|
|
24
18
|
unavailable: "Can't store changes: download a copy",
|
|
25
19
|
};
|
|
26
20
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
21
|
+
// The chip has words only for a save that did not go through. A save on its way says
|
|
22
|
+
// nothing and leaves a standing failure up, and a save that landed takes the chip down.
|
|
23
|
+
// The block editor's retrying takes down a plain "Couldn't save" (it keeps trying on its
|
|
24
|
+
// own and raises its own notice if that runs long) but leaves "Offline, will retry" up,
|
|
25
|
+
// which is still true.
|
|
26
|
+
const QUIET = new Set(["saving", "pending", "acknowledged"]);
|
|
27
|
+
const CLEARS = new Set(["saved", "checkpointed"]);
|
|
30
28
|
|
|
31
|
-
const ALARMING = new Set(["error", "offline", "blocked", "unavailable"]);
|
|
32
|
-
|
|
33
|
-
const REST = [`background:${TOKENS.surface}`, `color:${TOKENS.ink}`, `border-color:${TOKENS["line-2"]}`];
|
|
34
29
|
const ALARM = [
|
|
35
30
|
`background:${TOKENS["ox-soft"]}`,
|
|
36
31
|
`color:${TOKENS.ox}`,
|
|
@@ -38,7 +33,6 @@ const ALARM = [
|
|
|
38
33
|
];
|
|
39
34
|
|
|
40
35
|
let el = null;
|
|
41
|
-
let hideTimer = null;
|
|
42
36
|
|
|
43
37
|
function ensure() {
|
|
44
38
|
// Looked up on every call, never trusted once detached: a live-sync morph or a
|
|
@@ -57,35 +51,46 @@ function ensure() {
|
|
|
57
51
|
return el;
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
|
|
54
|
+
function hide() {
|
|
55
|
+
if (!el) return;
|
|
56
|
+
set(el, "opacity", "0");
|
|
57
|
+
el.textContent = "";
|
|
58
|
+
delete el.dataset.state;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Another notice is already saying this, so the chip says nothing: two save notices at once
|
|
62
|
+
// read as one problem reported twice.
|
|
63
|
+
export function standDown() {
|
|
64
|
+
hide();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function show(state, detail) {
|
|
68
|
+
if (QUIET.has(state)) return;
|
|
69
|
+
if (CLEARS.has(state)) return hide();
|
|
70
|
+
if (state === "retrying") {
|
|
71
|
+
if (el?.dataset.state === "error") hide();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
61
74
|
if (!LABELS[state]) return;
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
set(el, "opacity", "0");
|
|
71
|
-
el.textContent = "";
|
|
72
|
-
delete el.dataset.state;
|
|
73
|
-
}
|
|
75
|
+
// The block editor put its own notice up for this failure. Read here, after every
|
|
76
|
+
// listener has seen the event, so the order the two subscribed in does not matter.
|
|
77
|
+
if (state === "error" && detail?.[FAILURE_CLAIMED]) return;
|
|
78
|
+
// A failed or offline save is the persistent toast's to report when clay-ui is loaded.
|
|
79
|
+
// Blocked is the exception: the page cannot save at all, so the chip stays even beside
|
|
80
|
+
// the generic toast.
|
|
81
|
+
if (saveFeedback.toasts && state !== "blocked") {
|
|
82
|
+
hide();
|
|
74
83
|
return;
|
|
75
84
|
}
|
|
76
85
|
const node = ensure();
|
|
77
86
|
node.textContent = LABELS[state];
|
|
78
87
|
node.dataset.state = state;
|
|
79
|
-
style(node,
|
|
80
|
-
paintLook(node, { background:
|
|
88
|
+
style(node, ALARM);
|
|
89
|
+
paintLook(node, { background: null });
|
|
81
90
|
set(node, "opacity", "1");
|
|
82
|
-
clearTimeout(hideTimer);
|
|
83
|
-
if (!STICKY.has(state)) hideTimer = setTimeout(() => { set(node, "opacity", "0"); }, 2200);
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
export function remove() {
|
|
87
|
-
clearTimeout(hideTimer);
|
|
88
|
-
hideTimer = null;
|
|
89
94
|
el?.remove();
|
|
90
95
|
el = null;
|
|
91
96
|
}
|
package/src/plugins/indicator.js
CHANGED
|
@@ -7,8 +7,16 @@ const SAVE_EVENTS = ["saving", "saved", "error", "offline", "retrying", "blocked
|
|
|
7
7
|
// The drawing arrives on demand; states that come before it are shown in order once it has.
|
|
8
8
|
let chip = null;
|
|
9
9
|
const loadChip = () => (chip ??= import("./indicator-chip.js"));
|
|
10
|
-
const show = (state) => {
|
|
11
|
-
loadChip().then((m) => { if (listeners) m.show(state); }, () => {});
|
|
10
|
+
const show = (state, detail) => {
|
|
11
|
+
loadChip().then((m) => { if (listeners) m.show(state, detail); }, () => {});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// The page editor puts up its own notice, with Download and Copy text, whenever it cannot
|
|
15
|
+
// save at all (plugins/page.js showBlocked and showFull), so the chip stands down for those.
|
|
16
|
+
const PAGE_NOTICED = new Set(["blocked", "unavailable"]);
|
|
17
|
+
const showPageState = (state) => {
|
|
18
|
+
if (!PAGE_NOTICED.has(state)) return show(state);
|
|
19
|
+
loadChip().then((m) => { if (listeners) m.standDown(); }, () => {});
|
|
12
20
|
};
|
|
13
21
|
|
|
14
22
|
let listeners = null;
|
|
@@ -25,9 +33,10 @@ function arm() {
|
|
|
25
33
|
document.addEventListener(type, loadChip, { signal, once: true, capture: true });
|
|
26
34
|
}
|
|
27
35
|
for (const state of SAVE_EVENTS) {
|
|
28
|
-
document.addEventListener("clay:save-" + state, () => show(state), { signal });
|
|
36
|
+
document.addEventListener("clay:save-" + state, (event) => show(state, event.detail), { signal });
|
|
29
37
|
}
|
|
30
|
-
document.addEventListener("clay:
|
|
38
|
+
document.addEventListener("clay:blockeditor-save-state", (event) => showPageState(event.detail?.state), { signal });
|
|
39
|
+
document.addEventListener("clay:save-status", (event) => show(event.detail?.state), { signal });
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
function disarm() {
|