@hazem3500/komo 0.0.0-stage → 0.8.0
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/LICENSE +21 -0
- package/NODE.md +60 -0
- package/NOTICE.md +45 -0
- package/README.md +399 -2
- package/cli/agent.mjs +615 -0
- package/cli/config.mjs +34 -0
- package/cli/index.mjs +405 -0
- package/cli/project.mjs +136 -0
- package/cli/workflow.mjs +42 -0
- package/dist/MorphingMenu.d.ts +30 -0
- package/dist/PointerIcon.d.ts +2 -0
- package/dist/SmileIcon.d.ts +2 -0
- package/dist/THIRD_PARTY_NOTICES.txt +387 -0
- package/dist/accent-picker-HSUCA4KE.js +1 -0
- package/dist/accent-picker.d.ts +4 -0
- package/dist/accent.d.ts +2 -0
- package/dist/account-usage.d.ts +2 -0
- package/dist/action-menu.d.ts +10 -0
- package/dist/agent-prompt.d.ts +6 -0
- package/dist/agent-prompt.js +1 -0
- package/dist/anchors.d.ts +48 -0
- package/dist/api.d.ts +39 -0
- package/dist/approved-sites.d.ts +9 -0
- package/dist/card-motion.d.ts +8 -0
- package/dist/chunk-7YUXYVQA.js +1 -0
- package/dist/chunk-E5X4IQJ2.js +1 -0
- package/dist/chunk-I7AUKTXE.js +1 -0
- package/dist/chunk-RPKLGTEU.js +1 -0
- package/dist/chunk-Y2GRLW4Q.js +1 -0
- package/dist/chunk-Z2ABBESO.js +1 -0
- package/dist/chunk-ZUNTYWJF.js +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/connect-project.d.ts +14 -0
- package/dist/connection-issue.d.ts +7 -0
- package/dist/design-export-BRBI7S3V.js +1 -0
- package/dist/design-export.d.ts +8 -0
- package/dist/design-mode-styles.d.ts +1 -0
- package/dist/design-mode.d.ts +19 -0
- package/dist/design-mode.js +1 -0
- package/dist/design-panel-BPVXFERR.js +1 -0
- package/dist/design-panel-styles.d.ts +1 -0
- package/dist/design-panel.d.ts +33 -0
- package/dist/design-tokens.d.ts +76 -0
- package/dist/dom.d.ts +10 -0
- package/dist/drawer-expand-motion.d.ts +46 -0
- package/dist/drawer-presence.d.ts +4 -0
- package/dist/edge-sidebar.d.ts +39 -0
- package/dist/emoji-history.d.ts +8 -0
- package/dist/emoji.d.ts +1 -0
- package/dist/floating-drag.d.ts +9 -0
- package/dist/icon-markup.d.ts +4 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1 -0
- package/dist/inline-text-editor-F7B6FQQ3.js +1 -0
- package/dist/inline-text-editor.d.ts +70 -0
- package/dist/lazy-accent-picker.d.ts +4 -0
- package/dist/lazy-toolbar.d.ts +19 -0
- package/dist/message-links.d.ts +2 -0
- package/dist/morphing-menu-styles.d.ts +1 -0
- package/dist/node.mjs +2454 -0
- package/dist/onboarding-YA63RUKK.js +1 -0
- package/dist/onboarding.d.ts +6 -0
- package/dist/optimistic.d.ts +26 -0
- package/dist/page.d.ts +2 -0
- package/dist/picker-WQN53YJ5.js +1 -0
- package/dist/pin-direction.d.ts +12 -0
- package/dist/pin-stacks.d.ts +3 -0
- package/dist/polling.d.ts +10 -0
- package/dist/postgres/001_initial.sql +128 -0
- package/dist/postgres/002_thread_tags.sql +1 -0
- package/dist/postgres/003_project_webhooks.sql +4 -0
- package/dist/project-management.d.ts +2 -0
- package/dist/react-inspector.d.ts +43 -0
- package/dist/react-props-panel-XSMDR2CY.js +1 -0
- package/dist/react-props-panel.d.ts +39 -0
- package/dist/react.d.ts +38 -0
- package/dist/react.js +1 -0
- package/dist/reaction-picker.d.ts +1 -0
- package/dist/review-layout.d.ts +23 -0
- package/dist/setup-browser.d.ts +1 -0
- package/dist/setup.d.ts +4 -0
- package/dist/setup.js +1 -0
- package/dist/shortcuts.d.ts +15 -0
- package/dist/static-svg.d.ts +3 -0
- package/dist/styles.d.ts +1 -0
- package/dist/tag-editor-VDPZ6CO3.js +1 -0
- package/dist/tag-editor.d.ts +12 -0
- package/dist/toolbar-runtime-MYOLQPTJ.js +1 -0
- package/dist/toolbar-runtime.d.ts +5 -0
- package/dist/types.d.ts +150 -0
- package/package.json +114 -4
- package/server/digest.ts +8 -0
- package/server/import-project.ts +164 -0
- package/server/index.ts +1565 -0
- package/server/migrations/0001_comments.sql +54 -0
- package/server/migrations/0002_identity.sql +2 -0
- package/server/migrations/0003_accent_color.sql +1 -0
- package/server/migrations/0004_workspaces.sql +46 -0
- package/server/migrations/0005_domains.sql +6 -0
- package/server/migrations/0006_member_budget.sql +16 -0
- package/server/migrations/0007_revisions.sql +17 -0
- package/server/migrations/0008_starter_comments.sql +1 -0
- package/server/migrations/0009_retention.sql +19 -0
- package/server/migrations/0010_reviewer_history.sql +1 -0
- package/server/migrations/0011_project_management.sql +52 -0
- package/server/migrations/0012_setup_sites.sql +3 -0
- package/server/migrations/0013_project_sites.sql +8 -0
- package/server/migrations/0014_project_site_removals.sql +3 -0
- package/server/migrations/0015_query_efficiency.sql +6 -0
- package/server/migrations/0016_thread_tags.sql +1 -0
- package/server/migrations/0017_project_webhooks.sql +4 -0
- package/server/node-webhooks.ts +126 -0
- package/server/project-management.ts +387 -0
- package/server/project-sites.ts +117 -0
- package/server/secrets.d.ts +18 -0
- package/server/setup-client.txt +2904 -0
- package/server/setup-page.ts +14 -0
- package/server/tsconfig.json +12 -0
- package/server/validation.ts +220 -0
- package/server/webhooks.ts +206 -0
- package/server/workspaces.ts +168 -0
- package/src/emoji.ts +7 -0
- package/src/page.ts +4 -0
- package/src/types.ts +153 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-I7AUKTXE.js";var e=".design-inspector__controls>*+*{border-top:1px solid #ffffff0d}.design-group{padding:0 12px 10px}.design-section>.design-group{padding-bottom:12px}.design-group summary{cursor:pointer;display:flex;align-items:center;gap:4px;height:36px;color:#e9e6e1;font-size:11px;font-weight:600;list-style:none}.design-group summary::-webkit-details-marker{display:none}.design-group summary:after{content:\"\";width:5px;height:5px;margin:-2px 0 0 3px;border:solid #77736f;border-width:0 1.2px 1.2px 0;rotate:45deg;opacity:0;transition:opacity .12s,rotate .2s var(--ease-smooth-out)}.design-group:not([open]) summary:after{rotate:-45deg;margin:0 0 0 2px}.design-group summary:is(:hover,:focus-visible):after,.design-group:not([open]) summary:after{opacity:1}.design-group summary:focus-visible{outline-offset:-2px;border-radius:6px}.design-section{position:relative}.design-section__title{display:flex;align-items:center;height:36px;padding:0 12px;color:#898680;font-size:11px;font-weight:600}.design-section__tools{position:absolute;z-index:1;top:6px;right:8px;display:flex;gap:2px}.design-section__tools button{display:grid;place-items:center;width:24px;height:24px;border-radius:6px;color:#898680;font-size:16px;line-height:1}.design-section__tools button:hover{background:#ffffff12;color:#f1efeb}.design-text-edit{font-size:0!important}.design-text-edit:before{content:\"T\";font:600 13px/1 Georgia,Times New Roman,serif}.design-text-edit[aria-label^=Finish]{background:#bda6ef33;color:#e6dcfb!important}.design-text-edit:disabled{opacity:.35;cursor:default}.design-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:6px 8px}.design-field{position:relative;--lead:4px;min-width:0;height:28px;display:flex;align-items:center;border-radius:6px;background:#ffffff0a;color:#898680;transition:box-shadow .12s var(--ease-smooth-out)}.design-field:hover{box-shadow:inset 0 0 0 1px #ffffff1c}.design-field:focus-within{box-shadow:inset 0 0 0 1px #bda6ef}.design-field:has([aria-invalid=true]){box-shadow:inset 0 0 0 1px #fda29b}.design-field[data-wide]{grid-column:1/-1}.design-field:has(.design-field__glyph){--lead:22px}.design-field--color{--lead:26px}.design-field>input:not([type=color]),.design-field>select{flex:1;box-sizing:border-box;width:100%;min-width:0;height:100%;margin:0;padding:0 8px;border:0;border-radius:6px;outline:0;box-shadow:none;background:none;color:#f1efeb;font:inherit;font-size:11px;font-variant-numeric:tabular-nums;text-overflow:ellipsis}.design-field>:is(input,select):focus-visible{outline:0}.design-field>select{appearance:none;padding-right:20px;cursor:pointer;background:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M1.5 3 4 5.5 6.5 3' fill='none' stroke='%23898680' stroke-width='1.2'/%3E%3C/svg%3E\") right 7px center/8px no-repeat}.design-field>select option{background:#191919}.design-field input[type=number]{appearance:textfield;-moz-appearance:textfield}.design-field input::-webkit-inner-spin-button{appearance:none}.design-field__glyph{flex:none;display:grid;place-items:center;width:22px;height:100%;margin-right:-6px;color:#77736f;font-size:10px;font-weight:600;cursor:ew-resize;user-select:none;touch-action:none}.design-field__glyph:hover{color:#e9e6e1}.design-field__glyph svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:1.1}.design-field input[type=color]{flex:none;width:16px;height:16px;margin:0 -2px 0 6px;padding:0;border:0;border-radius:4px;box-shadow:inset 0 0 0 1px #ffffff26;background:none;cursor:pointer}.design-field input[type=color]::-webkit-color-swatch-wrapper{padding:0}.design-field input[type=color]::-webkit-color-swatch{border:0;border-radius:4px;box-shadow:inset 0 0 0 1px #ffffff26}.design-field input[type=color]::-moz-color-swatch{border:0;border-radius:4px}\n",t=[{property:"width",label:"Width",kind:"size",glyph:"W"},{property:"height",label:"Height",kind:"size",glyph:"H"},{property:"padding",label:"Padding",kind:"length",glyph:"M1.5 1.5h9v9h-9zM4.5 4.5h3v3h-3z",max:200},{property:"gap",label:"Gap",kind:"length",glyph:"M1.5 1.5h3v9h-3zM7.5 1.5h3v9h-3z",max:200},{property:"margin",label:"Margin",kind:"length",glyph:"M4 4h4v4H4zM1 3V1h2m6 0h2v2m0 6v2H9m-6 0H1V9",max:200},{property:"border-radius",label:"Corner radius",kind:"length",glyph:"M1.5 10.5V6A4.5 4.5 0 0 1 6 1.5h4.5",max:96},{property:"display",label:"Layout",kind:"select",values:["block","flex","grid","inline-flex","inline-grid"]},{property:"font-family",label:"Font family",kind:"text",wide:!0,placeholder:"system-ui, sans-serif"},{property:"font-size",label:"Font size",kind:"length",glyph:"M2 3V1.5h8V3M6 1.5v9m-1.5 0h3",min:1,max:160},{property:"line-height",label:"Line height",kind:"text",glyph:"M1 1.5h10M1 10.5h10M3.5 8.5 6 3l2.5 5.5",placeholder:"1.4 or 24px"},{property:"font-weight",label:"Weight",kind:"select",values:["300","400","500","600","700","800"]},{property:"text-align",label:"Alignment",kind:"select",values:["left","center","right","justify"]},{property:"color",label:"Text color",kind:"color",wide:!0},{property:"background-color",label:"Fill",kind:"color",wide:!0},{property:"border-width",label:"Border width",kind:"length",glyph:"M1 2.5h10M1 6h10M1 9.5h10",max:24},{property:"border-style",label:"Border style",kind:"select",values:["none","solid","dashed","dotted"]},{property:"border-color",label:"Border color",kind:"color"}],n=["font-family","font-size","line-height","font-weight","text-align","color"];function i(e){if(/^#[\da-f]{6}$/i.test(e))return e;if(/^#[\da-f]{3}$/i.test(e))return`#${[...e.slice(1)].map(e=>e+e).join("")}`;let t=e.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);return t?`#${t.slice(1,4).map(e=>Number(e).toString(16).padStart(2,"0")).join("")}`:"#000000"}function r(e){let t=e.match(/^rgba\(.*,\s*([\d.]+)\)$/)?.[1];return void 0===t?/^rgb\(/.test(e)?i(e):e||"transparent":Number(t)?e:"transparent"}function o(e,t){let n=getComputedStyle(t).getPropertyValue(e.property).trim();if("color"===e.kind)return r(n);if("length"===e.kind||"size"===e.kind){let t=n.match(/^(-?\d+(?:\.\d+)?)px$/);return t?String(Math.round(10*Number(t[1]))/10):"size"===e.kind?n||"auto":"0"}return n}function l(e,t){let{element:n}=t,r=document.createElement("div");r.className=`design-field design-field--${e.kind}`,r.dataset.fieldProperty=e.property,r.toggleAttribute("data-wide",!!e.wide);let l="select"===e.kind?document.createElement("select"):document.createElement("input");if(l instanceof HTMLSelectElement)for(let t of e.values??[]){let e=document.createElement("option");e.value=e.textContent=t,l.append(e)}else l.type="length"===e.kind?"number":"text",l.autocomplete="off",l.spellcheck=!1,l.placeholder=e.placeholder??"","length"===e.kind&&(l.min=String(e.min??0),l.max=String(e.max??1e3),l.setAttribute("inputmode","decimal"));if(l.dataset.property=e.property,l.setAttribute("aria-label",e.label),l.title=e.label,l.value=o(e,n),l instanceof HTMLSelectElement&&!l.value){let t=document.createElement("option");t.value=t.textContent=o(e,n),l.prepend(t),l.value=t.value}let d=()=>t.begin(l,e.property),a=()=>{t.apply(l,e.property,function(e,t){let n=t.value.trim();if("color"===e.kind){let e=document.createElement("span").style;return e.color=n,e.color?n:null}if("length"===e.kind){if(!n)return null;let t=Number(n);return!Number.isFinite(t)||t<(e.min??-200)||t>(e.max??1e3)?null:`${t}px`}return"size"===e.kind||"line-height"===e.property?/^(auto|normal|(?:\d+(?:\.\d+)?)(?:px|%|vw|vh|rem|em)?)$/.test(n)?"size"===e.kind&&/^[\d.]+$/.test(n)?`${n}px`:n:null:n}(e,l)),t.commit(l,e.property,!1)},p=()=>t.commit(l,e.property);if(l.addEventListener("focus",d),l.addEventListener("input",a),l.addEventListener("change",p),l.addEventListener("blur",p),"color"===e.kind){let t=document.createElement("input");t.type="color",t.setAttribute("aria-label",`${e.label} picker`);let n=()=>{t.value=i(l.value)};n(),t.addEventListener("pointerdown",d),t.addEventListener("focus",d),t.addEventListener("input",()=>{l.value=t.value,a()}),t.addEventListener("change",p),l.addEventListener("input",n),r.append(t)}if(e.glyph){let t=t=>{let i=l.value.trim().match(/^(-?\d*\.?\d+)([a-z%]*)$/i),r=i?i[2]:"text"===e.kind?"px":"",o=i?Number(i[1]):parseFloat(getComputedStyle(n).getPropertyValue(e.property))||0,d=Math.round(10*(o+t*("text"!==e.kind||r?1:.1)))/10;"text"!==e.kind&&(d=Math.min(e.max??1e4,Math.max(e.min??0,d))),l.value=`${d}${r}`,a()},i=document.createElement("span");i.className="design-field__glyph",i.setAttribute("aria-hidden","true"),i.title=`${e.label} · drag to adjust`,e.glyph.length>1?i.innerHTML=`<svg viewBox="0 0 12 12"><path d="${e.glyph}"/></svg>`:i.textContent=e.glyph,i.addEventListener("pointerdown",e=>{e.preventDefault(),d(),i.setPointerCapture?.(e.pointerId);let n=e.clientX,r=e=>{let i=Math.trunc((e.clientX-n)/2);i&&(n+=2*i,t(i*(e.shiftKey?10:1)))},o=()=>{i.removeEventListener("pointermove",r),p()};i.addEventListener("pointermove",r),i.addEventListener("pointerup",o,{once:!0}),i.addEventListener("pointercancel",o,{once:!0})}),l.addEventListener("keydown",e=>{"ArrowUp"!==e.key&&"ArrowDown"!==e.key||(e.preventDefault(),t(("ArrowUp"===e.key?1:-1)*(e.shiftKey?10:1)))}),r.append(i)}return l instanceof HTMLInputElement&&l.addEventListener("keydown",e=>{"Enter"===e.key&&l.blur()}),r.append(l),r}function d(e,n,i,r=[]){let o=document.createElement("section");o.className="design-section";let d=document.createElement("div");if(d.className="design-section__tools",d.append(...r),!i){let e=document.createElement("span");return e.className="design-section__title",e.textContent=n,o.append(e,d),o}let a=document.createElement("details");a.className="design-group",a.open=!e.collapsed.has(n);let p=document.createElement("summary");p.textContent=n;let s=document.createElement("div");s.className="design-grid";for(let n of i)s.append(l(t.find(e=>e.property===n),e));return a.append(p,s),a.addEventListener("toggle",()=>{a.isConnected&&(a.open?e.collapsed.delete(n):e.collapsed.add(n))}),o.append(d,a),o}function a(e,t){let{element:i,changed:o}=t,l=getComputedStyle(i),a=e=>e.some(e=>o?.has(e)),p=[...i.childNodes].some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim())||/(?:^|;)\s*(?:color|font|line-height|text-align)/.test(i.getAttribute("style")??"")||a(n),s=/flex|grid/.test(l.display)||a(["gap"]),g="transparent"!==r(l.backgroundColor)||a(["background-color"]),c=["top","right","bottom","left"].some(e=>parseFloat(l.getPropertyValue(`border-${e}-width`))>0&&"none"!==l.getPropertyValue(`border-${e}-style`))||a(["border","border-width","border-style","border-color"]),u=(n,i,r,o)=>function(e,t,n){let i=document.createElement("button");return i.type="button",i.title=e,i.setAttribute("aria-label",e),i.textContent=t,i.addEventListener("click",n),i}(`Add ${n}`,"+",()=>{let n=document.createElement("input");t.begin(n,i),t.apply(n,i,r),t.commit(n,i),t.rerender(),e.querySelector(`[data-property="${o}"]`)?.focus()});e.append(d(t,"Layout",["width","height","padding",...s?["gap"]:[],"margin",...c?[]:["border-radius"],"display"])),p&&e.append(d(t,"Typography",n,[t.editText])),e.append(g?d(t,"Fill",["background-color"]):d(t,"Fill",null,[u("fill","background-color","#ffffff","background-color")]),c?d(t,"Border",["border-width","border-radius","border-style","border-color"]):d(t,"Border",null,[u("border","border","1px solid","border-width")]))}export{e as designPanelStyles,l as fieldControl,t as fields,a as renderPanel,i as toHex,o as valueFor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const designPanelStyles = "\n/* Property controls: compact 28px fields in a two-column grid, icon or letter\n glyphs instead of a label column, and quiet section headers. */\n.design-inspector__controls > * + * { border-top:1px solid #ffffff0d; }\n.design-group { padding:0 12px 10px; }\n.design-section > .design-group { padding-bottom:12px; }\n.design-group summary { cursor:pointer; display:flex; align-items:center; gap:4px; height:36px; color:#e9e6e1; font-size:11px; font-weight:600; list-style:none; }\n.design-group summary::-webkit-details-marker { display:none; }\n.design-group summary::after { content:''; width:5px; height:5px; margin:-2px 0 0 3px; border:solid #77736f; border-width:0 1.2px 1.2px 0; rotate:45deg; opacity:0; transition:opacity .12s,rotate .2s var(--ease-smooth-out); }\n.design-group:not([open]) summary::after { rotate:-45deg; margin:0 0 0 2px; }\n.design-group summary:is(:hover,:focus-visible)::after,.design-group:not([open]) summary::after { opacity:1; }\n.design-group summary:focus-visible { outline-offset:-2px; border-radius:6px; }\n.design-section { position:relative; }\n.design-section__title { display:flex; align-items:center; height:36px; padding:0 12px; color:#898680; font-size:11px; font-weight:600; }\n.design-section__tools { position:absolute; z-index:1; top:6px; right:8px; display:flex; gap:2px; }\n.design-section__tools button { display:grid; place-items:center; width:24px; height:24px; border-radius:6px; color:#898680; font-size:16px; line-height:1; }\n.design-section__tools button:hover { background:#ffffff12; color:#f1efeb; }\n.design-text-edit { font-size:0 !important; }\n.design-text-edit::before { content:'T'; font:600 13px/1 Georgia,\"Times New Roman\",serif; }\n.design-text-edit[aria-label^=\"Finish\"] { background:#bda6ef33; color:#e6dcfb !important; }\n.design-text-edit:disabled { opacity:.35; cursor:default; }\n.design-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:6px 8px; }\n.design-field { position:relative; --lead:4px; min-width:0; height:28px; display:flex; align-items:center; border-radius:6px; background:#ffffff0a; color:#898680; transition:box-shadow .12s var(--ease-smooth-out); }\n.design-field:hover { box-shadow:inset 0 0 0 1px #ffffff1c; }\n.design-field:focus-within { box-shadow:inset 0 0 0 1px #bda6ef; }\n.design-field:has([aria-invalid=\"true\"]) { box-shadow:inset 0 0 0 1px #fda29b; }\n.design-field[data-wide] { grid-column:1/-1; }\n.design-field:has(.design-field__glyph) { --lead:22px; }\n.design-field--color { --lead:26px; }\n.design-field > input:not([type=\"color\"]),.design-field > select { flex:1; box-sizing:border-box; width:100%; min-width:0; height:100%; margin:0; padding:0 8px; border:0; border-radius:6px; outline:0; box-shadow:none; background:none; color:#f1efeb; font:inherit; font-size:11px; font-variant-numeric:tabular-nums; text-overflow:ellipsis; }\n.design-field > :is(input,select):focus-visible { outline:0; }\n.design-field > select { appearance:none; padding-right:20px; cursor:pointer; background:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M1.5 3 4 5.5 6.5 3' fill='none' stroke='%23898680' stroke-width='1.2'/%3E%3C/svg%3E\") right 7px center/8px no-repeat; }\n.design-field > select option { background:#191919; }\n.design-field input[type=\"number\"] { appearance:textfield; -moz-appearance:textfield; }\n.design-field input::-webkit-inner-spin-button { appearance:none; }\n.design-field__glyph { flex:none; display:grid; place-items:center; width:22px; height:100%; margin-right:-6px; color:#77736f; font-size:10px; font-weight:600; cursor:ew-resize; user-select:none; touch-action:none; }\n.design-field__glyph:hover { color:#e9e6e1; }\n.design-field__glyph svg { width:12px; height:12px; fill:none; stroke:currentColor; stroke-width:1.1; }\n.design-field input[type=\"color\"] { flex:none; width:16px; height:16px; margin:0 -2px 0 6px; padding:0; border:0; border-radius:4px; box-shadow:inset 0 0 0 1px #ffffff26; background:none; cursor:pointer; }\n.design-field input[type=\"color\"]::-webkit-color-swatch-wrapper { padding:0; }\n.design-field input[type=\"color\"]::-webkit-color-swatch { border:0; border-radius:4px; box-shadow:inset 0 0 0 1px #ffffff26; }\n.design-field input[type=\"color\"]::-moz-color-swatch { border:0; border-radius:4px; }\n";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { designPanelStyles } from "./design-panel-styles.js";
|
|
2
|
+
/** The property controls load as their own chunk, so the Design mode entry stays small. */
|
|
3
|
+
export { designPanelStyles };
|
|
4
|
+
export type Control = HTMLInputElement | HTMLSelectElement;
|
|
5
|
+
export type Field = {
|
|
6
|
+
property: string;
|
|
7
|
+
label: string;
|
|
8
|
+
kind: "color" | "length" | "size" | "select" | "text";
|
|
9
|
+
/** Short letter or icon path; numeric fields scrub when it is dragged. */
|
|
10
|
+
glyph?: string;
|
|
11
|
+
wide?: boolean;
|
|
12
|
+
values?: string[];
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
};
|
|
17
|
+
export interface PanelContext {
|
|
18
|
+
element: HTMLElement;
|
|
19
|
+
changed?: ReadonlyMap<string, string>;
|
|
20
|
+
collapsed: Set<string>;
|
|
21
|
+
editText: HTMLButtonElement;
|
|
22
|
+
begin(control: Control, property: string): void;
|
|
23
|
+
apply(control: Control, property: string, value: string | null): void;
|
|
24
|
+
commit(control: Control, property: string, final?: boolean): void;
|
|
25
|
+
rerender(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare const fields: Field[];
|
|
28
|
+
export declare function toHex(value: string): string;
|
|
29
|
+
export declare function valueFor(field: Field, element: HTMLElement): string;
|
|
30
|
+
/** Shared by every section, including the shared theme tokens. */
|
|
31
|
+
export declare function fieldControl(field: Field, context: Pick<PanelContext, "element" | "begin" | "apply" | "commit">): HTMLDivElement;
|
|
32
|
+
/** Render only the sections that apply to the element; empty Fill and Border offer a quiet add. */
|
|
33
|
+
export declare function renderPanel(container: HTMLElement, context: PanelContext): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Explicit bridge from a site's named CSS custom properties into Design mode. */
|
|
2
|
+
export type KomoDesignTokenType = "color" | "dimension" | "fontFamily" | "fontWeight" | "lineHeight";
|
|
3
|
+
export interface KomoDesignToken {
|
|
4
|
+
/** Stable DTCG-style token path, e.g. "color.brand.primary". */
|
|
5
|
+
path: string;
|
|
6
|
+
/** Existing CSS custom property that supplies this token. */
|
|
7
|
+
cssVariable: string;
|
|
8
|
+
/** DTCG scalar type; composite types remain unsupported. */
|
|
9
|
+
type: KomoDesignTokenType;
|
|
10
|
+
/** Explicit CSS properties that may use this token on the selected element. */
|
|
11
|
+
properties: string[];
|
|
12
|
+
/** Explicit scope used only to estimate global theme impact. */
|
|
13
|
+
impactSelector: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RegisteredDesignTokenSource {
|
|
16
|
+
root: HTMLElement;
|
|
17
|
+
tokens: KomoDesignToken[];
|
|
18
|
+
themeEdits: Map<string, {
|
|
19
|
+
before: string;
|
|
20
|
+
after: string;
|
|
21
|
+
inlineBefore: string;
|
|
22
|
+
}>;
|
|
23
|
+
elementOriginals: Map<HTMLElement, Map<string, {
|
|
24
|
+
value: string;
|
|
25
|
+
priority: string;
|
|
26
|
+
}>>;
|
|
27
|
+
elementBindings: Map<HTMLElement, Map<string, ElementTokenBinding>>;
|
|
28
|
+
previousMarker: string | null;
|
|
29
|
+
}
|
|
30
|
+
export interface ElementTokenBinding {
|
|
31
|
+
tokenPath: string;
|
|
32
|
+
initialValue: string;
|
|
33
|
+
initialPriority: string;
|
|
34
|
+
mode: "token" | "custom";
|
|
35
|
+
customValue?: string;
|
|
36
|
+
/** Token the element used before Komo changed it, from its inline style or stylesheet. */
|
|
37
|
+
declaredPath?: string;
|
|
38
|
+
}
|
|
39
|
+
/** A token reference the page itself declares for one element property. */
|
|
40
|
+
export interface DeclaredTokenReference {
|
|
41
|
+
token: KomoDesignToken;
|
|
42
|
+
origin: "inline" | "stylesheet";
|
|
43
|
+
}
|
|
44
|
+
export declare function registerDesignTokenSource(root: HTMLElement, tokens: readonly KomoDesignToken[]): () => void;
|
|
45
|
+
export declare function tokenSourceFor(target: Element | null): RegisteredDesignTokenSource | null;
|
|
46
|
+
export declare function tokenValue(source: RegisteredDesignTokenSource, token: KomoDesignToken): string;
|
|
47
|
+
export declare function tokenImpactCount(source: RegisteredDesignTokenSource, token: KomoDesignToken): number;
|
|
48
|
+
/**
|
|
49
|
+
* Preview a shared theme value. With `merge`, a live edit (typing, scrubbing, picking)
|
|
50
|
+
* extends the last undo step for the same token instead of adding one per keystroke.
|
|
51
|
+
*/
|
|
52
|
+
export declare function previewThemeToken(source: RegisteredDesignTokenSource, token: KomoDesignToken, value: string, merge?: boolean): boolean;
|
|
53
|
+
/** Return one shared token to the page's value; like Reset, it leaves no undo step. */
|
|
54
|
+
export declare function resetThemeToken(source: RegisteredDesignTokenSource, token: KomoDesignToken): void;
|
|
55
|
+
export declare function applyTokenToElement(source: RegisteredDesignTokenSource, element: HTMLElement, token: KomoDesignToken, property: string, options?: {
|
|
56
|
+
recordHistory?: boolean;
|
|
57
|
+
}): boolean;
|
|
58
|
+
/** Keep the token reference as provenance when the user types a custom override. */
|
|
59
|
+
export declare function noteElementTokenOverride(source: RegisteredDesignTokenSource, element: HTMLElement, property: string, value: string): void;
|
|
60
|
+
/** Reconcile token provenance after the style editor's undo restores a declaration. */
|
|
61
|
+
export declare function syncElementTokenBinding(source: RegisteredDesignTokenSource, element: HTMLElement, property: string): void;
|
|
62
|
+
export declare function elementTokenBindingFor(source: RegisteredDesignTokenSource, element: HTMLElement, property: string): ElementTokenBinding | null;
|
|
63
|
+
/**
|
|
64
|
+
* Report the token a property is connected to only when the winning declared
|
|
65
|
+
* value references it. Equal computed values are never treated as a token.
|
|
66
|
+
* Cross-origin sheets are skipped; cascade layers and scoping are not ranked.
|
|
67
|
+
*/
|
|
68
|
+
export declare function declaredTokenFor(source: RegisteredDesignTokenSource, element: HTMLElement, property: string, inline?: string, inlinePriority?: string): DeclaredTokenReference | null;
|
|
69
|
+
/** Drop Komo's element preview for one property and return to the page's own value. */
|
|
70
|
+
export declare function restoreElementProperty(source: RegisteredDesignTokenSource, element: HTMLElement, property: string): void;
|
|
71
|
+
export declare function compatibleTokens(source: RegisteredDesignTokenSource, property: string): KomoDesignToken[];
|
|
72
|
+
export declare function hasDesignTokenChanges(element: HTMLElement): boolean;
|
|
73
|
+
export declare function canUndoDesignToken(element: HTMLElement | null): boolean;
|
|
74
|
+
export declare function undoDesignToken(element: HTMLElement): void;
|
|
75
|
+
export declare function resetDesignTokens(element: HTMLElement): void;
|
|
76
|
+
export declare function proposedDesignTokenChanges(element: HTMLElement): string | undefined;
|
package/dist/dom.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Identity } from "./types.js";
|
|
2
|
+
import { iconMarkup as icons } from "./icon-markup.js";
|
|
3
|
+
export { icons };
|
|
4
|
+
export declare function el<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string, text?: string): HTMLElementTagNameMap[K];
|
|
5
|
+
export declare function icon(name: keyof typeof icons): SVGSVGElement;
|
|
6
|
+
export declare function button(label: string, action: () => void, className?: string, glyph?: keyof typeof icons): HTMLButtonElement;
|
|
7
|
+
export declare const initials: (name: string) => string;
|
|
8
|
+
export declare function age(time: number): string;
|
|
9
|
+
export declare function avatar(user: Identity): HTMLSpanElement;
|
|
10
|
+
export declare function googleLogo(): HTMLImageElement;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Motion shared by the drawer menu expand and the drawer → edge sidebar hand-off. */
|
|
2
|
+
export declare const drawerExpandSpring: {
|
|
3
|
+
readonly type: "spring";
|
|
4
|
+
readonly duration: 0.4;
|
|
5
|
+
readonly bounce: 0.24;
|
|
6
|
+
};
|
|
7
|
+
export declare const drawerExpandCompress: {
|
|
8
|
+
readonly duration: 0.1;
|
|
9
|
+
readonly ease: readonly [0.4, 0, 0.2, 1];
|
|
10
|
+
};
|
|
11
|
+
export declare const drawerCollapse: {
|
|
12
|
+
readonly duration: 0.25;
|
|
13
|
+
readonly ease: readonly [0.22, 1, 0.36, 1];
|
|
14
|
+
};
|
|
15
|
+
export declare const drawerBarExit: {
|
|
16
|
+
readonly duration: 0.15;
|
|
17
|
+
};
|
|
18
|
+
export declare const drawerBarEnter: {
|
|
19
|
+
readonly duration: 0.22;
|
|
20
|
+
readonly delay: 0.08;
|
|
21
|
+
readonly ease: "easeOut";
|
|
22
|
+
};
|
|
23
|
+
export declare const drawerRowEnter: {
|
|
24
|
+
readonly type: "spring";
|
|
25
|
+
readonly duration: 0.4;
|
|
26
|
+
readonly bounce: 0.3;
|
|
27
|
+
};
|
|
28
|
+
export declare const drawerRowExit: {
|
|
29
|
+
readonly type: "spring";
|
|
30
|
+
readonly duration: 0.12;
|
|
31
|
+
readonly bounce: 0.3;
|
|
32
|
+
};
|
|
33
|
+
export declare const drawerRowEnterDelay = 0.2;
|
|
34
|
+
export declare const drawerRowStagger = 0.02;
|
|
35
|
+
/** Same squish the drawer shell uses before it springs open. */
|
|
36
|
+
export declare function compressedDrawerSize(barWidth: number, barHeight: number): {
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
};
|
|
40
|
+
/** Bottom-center anchor, matching `.morphing-menu__shell`. */
|
|
41
|
+
export declare function boxFromAnchor(anchorX: number, anchorBottom: number, width: number, height: number): {
|
|
42
|
+
left: number;
|
|
43
|
+
top: number;
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Placement } from "./floating-drag.js";
|
|
2
|
+
export declare const EDGE_SIDEBAR_WIDTH = 380;
|
|
3
|
+
export declare const EDGE_MARGIN = 16;
|
|
4
|
+
/** How many pixels of the closed sidebar stay visible while peeking. */
|
|
5
|
+
export declare const EDGE_PEEK_SLIVER = 76;
|
|
6
|
+
/** Clearance the closed sidebar parks beyond the viewport edge. */
|
|
7
|
+
export declare const EDGE_PARK_CLEARANCE = 24;
|
|
8
|
+
/** Panel height cap; a taller viewport still parks flush against the edge. */
|
|
9
|
+
export declare const EDGE_SIDEBAR_MAX_HEIGHT = 680;
|
|
10
|
+
export declare const EDGE_SIDEBAR_MIN_WIDTH = 240;
|
|
11
|
+
export declare const EDGE_SIDEBAR_MIN_HEIGHT = 200;
|
|
12
|
+
export type EdgeResizeDir = "n" | "s" | "e" | "w" | "ne" | "nw" | "se" | "sw";
|
|
13
|
+
export interface EdgeBox {
|
|
14
|
+
left: number;
|
|
15
|
+
top: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}
|
|
19
|
+
/** Resize from an edge or corner. Same screen-space math as panels. */
|
|
20
|
+
export declare function resizeEdgeBox(dir: EdgeResizeDir, start: EdgeBox, dx: number, dy: number, vw: number, vh: number): EdgeBox;
|
|
21
|
+
export type EdgeSide = "left" | "right";
|
|
22
|
+
export interface EdgeSidebarGeometry {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
side: EdgeSide;
|
|
28
|
+
}
|
|
29
|
+
/** The sidebar's on-screen placement, docked to the right by default. */
|
|
30
|
+
export declare function edgeSidebarPlacement(placement: Placement | undefined, width: number, height: number, vw: number, vh: number): EdgeSidebarGeometry;
|
|
31
|
+
/**
|
|
32
|
+
* Horizontal translates (style px) that park the sidebar off its docked edge
|
|
33
|
+
* and reveal it as a peek sliver. Applied on top of the placed position, so a
|
|
34
|
+
* dragged placement keeps its park direction consistent with its dock.
|
|
35
|
+
*/
|
|
36
|
+
export declare function edgeSidebarOffsets(g: EdgeSidebarGeometry, vw: number): {
|
|
37
|
+
parked: number;
|
|
38
|
+
peek: number;
|
|
39
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const DEFAULT_EMOJI: string[];
|
|
2
|
+
export type EmojiHistory = {
|
|
3
|
+
counts: Record<string, number>;
|
|
4
|
+
recent?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function rankedEmoji(history: EmojiHistory, selected?: string): string[];
|
|
7
|
+
export declare function emojiHistory(key: string): EmojiHistory;
|
|
8
|
+
export declare function recordEmoji(key: string, emoji: string): void;
|
package/dist/emoji.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isEmoji(value: unknown): value is string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface Placement {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
edgeX?: "left" | "right";
|
|
5
|
+
edgeY?: "top" | "bottom";
|
|
6
|
+
}
|
|
7
|
+
export declare function constrain(p: Placement, width: number, height: number, vw: number, vh: number): Placement;
|
|
8
|
+
export declare function settle(p: Placement, width: number, height: number, vw: number, vh: number): Placement;
|
|
9
|
+
export declare function floatingDrag(element: HTMLElement, handle: HTMLElement, save: (p: Placement) => void, signal: AbortSignal, beforeDrag?: () => void, onSettled?: () => void, enabled?: () => boolean, allowInteractiveStart?: (target: Element) => boolean): void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type ComponentType, type SVGProps } from "react";
|
|
2
|
+
export type IconName = "chevron" | "drawer" | "edit" | "trash" | "info" | "more" | "person" | "search" | "plus" | "comment" | "copy" | "pointer" | "expand" | "close" | "check" | "arrow" | "link" | "code" | "smile" | "tag" | "design";
|
|
3
|
+
export declare const components: Record<IconName, ComponentType<SVGProps<SVGSVGElement>>>;
|
|
4
|
+
export declare const iconMarkup: Record<IconName, string>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type KomoConfig } from "./config.js";
|
|
2
|
+
export type { KomoConfig } from "./config.js";
|
|
3
|
+
import type { CommentsController, CommentsOptions } from "./types.js";
|
|
4
|
+
export type * from "./types.js";
|
|
5
|
+
/** Initialize from inline public project settings. */
|
|
6
|
+
export declare function initKomo(config: KomoConfig): CommentsController;
|
|
7
|
+
/** Mount once on the client. The returned destroy() restores the host page. */
|
|
8
|
+
export declare function initComments(options: CommentsOptions): CommentsController;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as o,c as s}from"./chunk-ZUNTYWJF.js";import"./chunk-RPKLGTEU.js";import"./chunk-Y2GRLW4Q.js";import"./chunk-7YUXYVQA.js";import"./chunk-E5X4IQJ2.js";import"./chunk-I7AUKTXE.js";export{s as initComments,o as initKomo};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-I7AUKTXE.js";var e="script,style,svg,input,textarea,select,[data-private],[data-sensitive]";function t(e){let t=document.createTreeWalker(e,NodeFilter.SHOW_TEXT),n=[];for(;t.nextNode();)n.push(t.currentNode);return n}function n(e,t,n){if(n&&e.childElementCount)for(let[e,t]of n)e.data=t;else e.textContent=t}function a(){let e=new Map,t=new Map,a=[];return{record(n){!n.reactProp&&n.before!==n.after&&(e.has(n.element)||(e.set(n.element,n.before),t.set(n.element,n.nodes)),a.push(n))},remember(t,n){e.has(t)||e.set(t,n)},original:t=>e.get(t),reconcile(t,n){e.get(t)===n&&e.delete(t)},reset(r){let i=e.get(r);void 0!==i&&n(r,i,t.get(r)),e.delete(r);for(let e=a.length-1;e>=0;e--)a[e].element===r&&a.splice(e,1)},proposal(t){let n=e.get(t),a=t.textContent??"";return void 0!==n&&n!==a?`Proposed inline text change (preview only)\n- Text: ${JSON.stringify(n)} → ${JSON.stringify(a)}\n- Source markup is unchanged; refresh resets this preview.`:void 0},restoreAll(){for(let[a,r]of e)n(a,r,t.get(a))},undo(){let t=a.pop();return t?(n(t.element,t.before,t.nodes),e.get(t.element)===t.before&&e.delete(t.element),t.element):null}}}function r(e){let t=a(),n=null,r=!1;function i(t=!1){let a=n;a&&(n=null,a.finish(t),e.onChange())}return{get element(){return n?.element??null},get busy(){return r},isEditing:e=>!!n&&n.element===e,toggle:async function(a){let l=e.selected();if(n&&n.element===l)return void i();if(!l||!e.active()||r||n)return;let s=o(l);if(s)e.status.textContent=s;else{r=!0,e.status.textContent="Loading inline text editor…",e.onChange();try{let r=await d(l,{loadReactBridge:async()=>await import("./react-props-panel-XSMDR2CY.js"),onFinish(n,a){a||n.reactProp||n.before===n.after?n.reactProp&&(e.status.textContent="React state preview · resets on refresh"):(t.record(n),e.onCommit(l),e.status.textContent="Text preview · resets on refresh")}});r?e.active()&&e.selected()===l?(n=r,a&&function(e,t,n){let a=document.caretPositionFromPoint?.(t,n),r=a?document.createRange():document.caretRangeFromPoint?.(t,n);if(a&&r&&r.setStart(a.offsetNode,a.offset),!r||!e.contains(r.startContainer))return;r.collapse(!0);let i=document.getSelection();i?.removeAllRanges(),i?.addRange(r)}(l,a.clientX,a.clientY),e.status.textContent=l.hasAttribute("data-komo-text-prop")?"React state preview · resets on refresh":"Editing text · preview only"):r.finish(!0):e.status.textContent="This text couldn't be connected to its component prop."}catch{e.status.textContent="Inline text editing is unavailable."}finally{r=!1,e.onChange()}}},finish:i,handleKey(e){if(!n||!e.composedPath().includes(n.element)||!n.handleKey(e))return!1;let t=n.element;return i("Escape"===e.key),t.blur(),!0},hasChanges:e=>void 0!==t.original(e),undo(){let n=t.undo();return e.onChange(),n},reset(n){t.reset(n),e.onChange()},reconcile(n,a){t.reconcile(n,a),e.onChange()},proposal:e=>t.proposal(e),restoreAll(){t.restoreAll()}}}function i(e){let t=r(e);return e.button.addEventListener("click",e=>{t.toggle(e)}),t}function o(n){return n.matches("input,textarea,select")?"Form fields can't be edited as text.":n.matches("script,style,svg,canvas,video,audio,iframe")?"Graphics, media, and frames can't be edited.":n.closest("[data-private],[data-sensitive]")?"This text is marked private, so it stays read-only.":n.closest("[contenteditable]")?"This text is already editable on the page.":t(n).some(t=>t.data.trim()&&!t.parentElement?.closest(e))?null:"No text here. Double-click the text itself."}function l(e){return!o(e)}function s(e){let t=e.textContent??"",n=document.getSelection(),a=n?.rangeCount?n.getRangeAt(0):null;return a&&e.contains(a.commonAncestorContainer)?a.startContainer===e?Math.min([...e.childNodes].slice(0,a.startOffset).reduce((e,t)=>e+(t.textContent?.length??0),0),t.length):a.startContainer.nodeType===Node.TEXT_NODE?Math.min(a.startOffset,t.length):t.length:-1}function c(e,t){let n=e.firstChild,a=document.getSelection();if(!n||!a)return;let r=document.createRange();r.setStart(n,Math.min(t,n.textContent?.length??0)),r.collapse(!0),a.removeAllRanges(),a.addRange(r)}async function d(a,r){if(!l(a))return null;let i=null,o=null;if(a.dataset.komoTextProp&&(i=await r.loadReactBridge(),o=i.beginReactTextEdit(a),!o))return null;let d=o?.before??(a.textContent??"").slice(0,500),u=!o&&a.childElementCount>0,g=u?t(a).map(e=>[e,e.data]):void 0,p=a.getAttribute("role"),f=a.getAttribute("aria-label"),m=a.getAttribute("aria-multiline"),h=a.getAttribute("spellcheck"),x=new AbortController,C=!1;let v=-1,b=new MutationObserver(()=>{v>=0&&c(a,v)}),T=()=>{u||(a.childElementCount&&(a.textContent=a.textContent??""),(a.textContent??"").length>500&&(a.textContent=(a.textContent??"").slice(0,500)),o&&i&&(v=s(a),i.previewReactTextEdit(a,o.propName,a.textContent??"")||(a.textContent=d)))},E=(t,n)=>{let r=t.startContainer;if(r!==t.endContainer||!(r instanceof Text)||!a.contains(r)||r.parentElement?.closest(e))return;let i=t.endOffset-t.startOffset,o=n.replace(/[\r\n]+/g," ").slice(0,Math.max(0,500-(a.textContent??"").length+i));r.replaceData(t.startOffset,i,o);let l=document.createRange();l.setStart(r,t.startOffset+o.length);let s=document.getSelection();s?.removeAllRanges(),s?.addRange(l)};a.addEventListener("beforeinput",e=>{if(v=-1,!u||"insertCompositionText"===e.inputType)return;e.preventDefault();let t=e.getTargetRanges()[0],n=e.inputType;t&&(n.startsWith("delete")||"insertText"===n||"insertReplacementText"===n)&&E(t,n.startsWith("delete")?"":e.data??e.dataTransfer?.getData("text/plain")??"")},{signal:x.signal}),a.addEventListener("input",T,{signal:x.signal}),o&&b.observe(a,{childList:!0,characterData:!0,subtree:!0}),a.addEventListener("paste",e=>{e.preventDefault();let t=e.clipboardData?.getData("text/plain")??"",n=document.getSelection();u?n?.rangeCount&&E(n.getRangeAt(0),t):(function(e,t){let n=s(e);if(n<0)return;let a=e.textContent??"",r=t.slice(0,Math.max(0,500-a.length));e.textContent=a.slice(0,n)+r+a.slice(n),c(e,n+r.length)}(a,t.replace(/[\r\n]+/g," ").slice(0,500)),T())},{signal:x.signal}),a.addEventListener("drop",e=>e.preventDefault(),{signal:x.signal}),a.setAttribute("contenteditable","true"),a.setAttribute("role","textbox"),a.setAttribute("aria-label","Inline text preview"),a.setAttribute("aria-multiline","false"),a.dataset.komoInlineEditing="true",a.spellcheck=!1,a.focus({preventScroll:!0});let R=document.getSelection(),y=document.createRange();return y.selectNodeContents(a),y.collapse(!1),R?.removeAllRanges(),R?.addRange(y),{element:a,finish(e=!1){if(C)return;C=!0,x.abort(),b.disconnect(),!u&&a.childElementCount&&(a.textContent=a.textContent??"");let t=(a.textContent??"").slice(0,500);(function(){a.removeAttribute("contenteditable"),a.removeAttribute("data-komo-inline-editing");for(let[e,t]of[["role",p],["aria-label",f],["aria-multiline",m],["spellcheck",h]])null===t?a.removeAttribute(e):a.setAttribute(e,t)})(),e?(o&&i&&i.cancelReactTextEdit(a,o.propName,d),n(a,d,g)):o&&i&&i.finishReactTextEdit(a,o.propName,d,t),r.onFinish({element:a,before:d,after:t,nodes:g,...o?{reactProp:o.propName}:{}},e)},handleKey:e=>!e.isComposing&&(("Enter"===e.key||"Escape"===e.key)&&(e.preventDefault(),e.stopImmediatePropagation(),!0))}}export{i as bindInlineTextTools,l as canEditText,r as createInlineTextTools,a as createTextPreviewHistory,d as startInlineTextEditor,o as textEditBlocker};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface ReactTextBridge {
|
|
2
|
+
beginReactTextEdit(element: HTMLElement): {
|
|
3
|
+
propName: string;
|
|
4
|
+
before: string;
|
|
5
|
+
} | null;
|
|
6
|
+
previewReactTextEdit(element: HTMLElement, propName: string, value: string): boolean;
|
|
7
|
+
finishReactTextEdit(element: HTMLElement, propName: string, before: string, after: string): void;
|
|
8
|
+
cancelReactTextEdit(element: HTMLElement, propName: string, before: string): void;
|
|
9
|
+
}
|
|
10
|
+
export interface InlineTextEditResult {
|
|
11
|
+
element: HTMLElement;
|
|
12
|
+
before: string;
|
|
13
|
+
after: string;
|
|
14
|
+
reactProp?: string;
|
|
15
|
+
/** Text node values before the edit, for elements with nested markup. */
|
|
16
|
+
nodes?: TextSnapshot;
|
|
17
|
+
}
|
|
18
|
+
export type TextSnapshot = Array<[Text, string]>;
|
|
19
|
+
export interface InlineTextEditor {
|
|
20
|
+
readonly element: HTMLElement;
|
|
21
|
+
finish(cancel?: boolean): void;
|
|
22
|
+
handleKey(event: KeyboardEvent): boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface InlineTextEditorOptions {
|
|
25
|
+
loadReactBridge(): Promise<ReactTextBridge>;
|
|
26
|
+
onFinish(result: InlineTextEditResult, cancel: boolean): void;
|
|
27
|
+
}
|
|
28
|
+
export interface InlineTextToolsOptions {
|
|
29
|
+
button: HTMLButtonElement;
|
|
30
|
+
status: HTMLElement;
|
|
31
|
+
selected(): HTMLElement | null;
|
|
32
|
+
active(): boolean;
|
|
33
|
+
onChange(): void;
|
|
34
|
+
onCommit(element: HTMLElement): void;
|
|
35
|
+
}
|
|
36
|
+
export interface InlineTextTools {
|
|
37
|
+
readonly element: HTMLElement | null;
|
|
38
|
+
readonly busy: boolean;
|
|
39
|
+
isEditing(element: HTMLElement | null): boolean;
|
|
40
|
+
/** Start or finish editing; a point inside the text places the caret there. */
|
|
41
|
+
toggle(point?: {
|
|
42
|
+
clientX: number;
|
|
43
|
+
clientY: number;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
finish(cancel?: boolean): void;
|
|
46
|
+
handleKey(event: KeyboardEvent): boolean;
|
|
47
|
+
hasChanges(element: HTMLElement): boolean;
|
|
48
|
+
undo(): HTMLElement | null;
|
|
49
|
+
reset(element: HTMLElement): void;
|
|
50
|
+
reconcile(element: HTMLElement, text: string): void;
|
|
51
|
+
proposal(element: HTMLElement): string | undefined;
|
|
52
|
+
restoreAll(): void;
|
|
53
|
+
}
|
|
54
|
+
export interface TextPreviewHistory {
|
|
55
|
+
record(result: InlineTextEditResult): void;
|
|
56
|
+
remember(element: HTMLElement, before: string): void;
|
|
57
|
+
original(element: HTMLElement): string | undefined;
|
|
58
|
+
reconcile(element: HTMLElement, text: string): void;
|
|
59
|
+
reset(element: HTMLElement): void;
|
|
60
|
+
proposal(element: HTMLElement): string | undefined;
|
|
61
|
+
restoreAll(): void;
|
|
62
|
+
undo(): HTMLElement | null;
|
|
63
|
+
}
|
|
64
|
+
export declare function createTextPreviewHistory(): TextPreviewHistory;
|
|
65
|
+
export declare function createInlineTextTools(options: InlineTextToolsOptions): InlineTextTools;
|
|
66
|
+
export declare function bindInlineTextTools(options: InlineTextToolsOptions): InlineTextTools;
|
|
67
|
+
/** Why an element's text can't be edited inline, or null when it can. */
|
|
68
|
+
export declare function textEditBlocker(element: HTMLElement): string | null;
|
|
69
|
+
export declare function canEditText(element: HTMLElement): boolean;
|
|
70
|
+
export declare function startInlineTextEditor(element: HTMLElement, options: InlineTextEditorOptions): Promise<InlineTextEditor | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MenuItem, MorphingMenuProps } from "./MorphingMenu.js";
|
|
2
|
+
import { type icons } from "./dom.js";
|
|
3
|
+
import type { Identity } from "./types.js";
|
|
4
|
+
export type ToolbarIcon = {
|
|
5
|
+
glyph: keyof typeof icons;
|
|
6
|
+
} | {
|
|
7
|
+
user: Identity | null;
|
|
8
|
+
};
|
|
9
|
+
export type ToolbarItem = Omit<MenuItem, "icon" | "activeIcon" | "children"> & {
|
|
10
|
+
icon: ToolbarIcon;
|
|
11
|
+
};
|
|
12
|
+
export type ToolbarProps = Omit<MorphingMenuProps, "items"> & {
|
|
13
|
+
items: readonly ToolbarItem[];
|
|
14
|
+
};
|
|
15
|
+
/** Pins and the compact drawer work immediately; React's menu loads on intent. */
|
|
16
|
+
export declare function createToolbar(element: HTMLElement, glyph: (id: string) => Node): {
|
|
17
|
+
render(next: ToolbarProps): void;
|
|
18
|
+
unmount(): void;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const morphingMenuStyles = "/* Self-contained: inherit the host's color-scheme and font; customize --mm-*.\n The inverted surface matches the original menu in both light and dark mode. */\n.morphing-menu {\n --mm-surface: light-dark(#0d0d0d, #fff);\n --mm-ink: light-dark(rgb(255 255 255 / 90%), rgb(13 13 13 / 90%));\n --mm-hover: light-dark(rgb(255 255 255 / 6%), rgb(0 0 0 / 4%));\n --mm-button: 44px;\n --mm-inset: 4px;\n --mm-bar-height: calc(var(--mm-button) + 2 * var(--mm-inset));\n --mm-bar-width: min(calc(var(--mm-count) * var(--mm-button) + 2 * var(--mm-inset)), calc(100vw - 32px));\n --mm-panel-width: min(268px, calc(100vw - 32px));\n --mm-panel-padding: 8px;\n --mm-radius: calc(var(--mm-bar-height) / 2);\n position: relative;\n width: var(--mm-bar-width);\n height: var(--mm-bar-height);\n flex: none;\n font-family: inherit;\n -webkit-tap-highlight-color: transparent;\n}\n\n.morphing-menu *, .morphing-menu *::before, .morphing-menu *::after { box-sizing: border-box; }\n\n.morphing-menu__shell {\n position: absolute;\n bottom: 0;\n left: 50%;\n translate: -50% 0;\n width: var(--mm-bar-width);\n height: var(--mm-bar-height);\n border-radius: var(--mm-radius);\n background: var(--mm-surface, #0d0d0d);\n color: var(--mm-ink, #fff);\n box-shadow:\n inset 0 1px 1px light-dark(rgb(255 255 255 / 8%), rgb(255 255 255 / 40%)),\n inset 0 -1px 2px light-dark(rgb(0 0 0 / 50%), rgb(0 0 0 / 20%)),\n 0 1px 2px rgb(0 0 0 / 12%);\n overflow: hidden;\n isolation: isolate;\n}\n\n.morphing-menu__bar {\n position: absolute;\n bottom: 0;\n left: 50%;\n translate: -50% 0;\n display: flex;\n align-items: center;\n padding: var(--mm-inset);\n width: var(--mm-bar-width);\n height: var(--mm-bar-height);\n}\n\n.morphing-menu__shortcut, .morphing-menu__row {\n appearance: none;\n display: flex;\n align-items: center;\n flex: none;\n border: 0;\n margin: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 15px;\n font-weight: 500;\n line-height: 20px;\n text-decoration: none;\n cursor: pointer;\n touch-action: manipulation;\n scale: 1;\n transition: background 150ms ease, scale 180ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n\n.morphing-menu__shortcut {\n justify-content: center;\n width: 0;\n flex: 1;\n height: var(--mm-button);\n padding: 0;\n border-radius: 999px;\n}\n\n.morphing-menu__panel {\n position: absolute;\n top: 0;\n left: 50%;\n translate: -50% 0;\n width: var(--mm-panel-width);\n padding: var(--mm-panel-padding);\n max-height: calc(100dvh - 64px);\n overflow-y: auto;\n overscroll-behavior: contain;\n scrollbar-width: thin;\n}\n\n.morphing-menu [aria-hidden=\"true\"] { pointer-events: none; }\n.morphing-menu__row {\n gap: 8px;\n width: 100%;\n min-height: 40px;\n padding: 10px 12px;\n border-radius: calc(var(--mm-radius) - var(--mm-panel-padding));\n text-align: left;\n}\n.morphing-menu__panel[aria-hidden=\"true\"] .morphing-menu__row { opacity: 0; }\n.morphing-menu__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.morphing-menu__icon { display: flex; flex: none; }\n.morphing-menu svg { width: 20px; height: 20px; flex-shrink: 0; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }\n.morphing-menu__chevron { margin-left: auto; opacity: 0.6; }\n.morphing-menu__shortcut:focus-visible, .morphing-menu__row:focus-visible { outline: 2px solid currentColor; outline-offset: -3px; }\n\n.morphing-menu__tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n translate: -50% 0;\n padding: 6px 10px;\n border-radius: 14px;\n background: light-dark(#fff, #171717);\n color: light-dark(#111, #fefefe);\n box-shadow: 0 0 0 1px light-dark(rgb(0 0 0 / 6%), rgb(255 255 255 / 10%)), 0 1px 2px -1px rgb(0 0 0 / 6%), 0 2px 4px rgb(0 0 0 / 4%);\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n white-space: nowrap;\n pointer-events: none;\n}\n\n@media (hover: hover) {\n .morphing-menu__shortcut:hover, .morphing-menu__row:hover { background: var(--mm-hover); }\n}\n@media (pointer: coarse) {\n .morphing-menu { --mm-button: 48px; --mm-bar-width: min(296px, calc(100vw - 32px)); --mm-panel-width: min(296px, calc(100vw - 32px)); --mm-panel-padding: 12px; }\n .morphing-menu__row { min-height: 44px; padding-block: 12px; }\n}\n@media (prefers-reduced-motion: no-preference) {\n .morphing-menu__shortcut:active, .morphing-menu__row:active { transition-duration: 100ms; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .morphing-menu__shortcut:active { scale: 0.95; }\n .morphing-menu__row:active { scale: 0.96; }\n}\n@media (prefers-reduced-motion: reduce) {\n .morphing-menu__shortcut, .morphing-menu__row { transition: none; scale: 1; }\n}\n";
|