@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 t=12,e=/^[\p{L}\p{N}][\p{L}\p{N} ._-]{0,31}$/u;function a(t){let a=t.trim().replace(/^#/,"").toLocaleLowerCase("en-US");return e.test(a)?a:null}function n(t,e){let n=[...e.tags],r=!1,o=!1,i=(...e)=>{o=!0,t.replaceChildren(...e),o=!1},l=`komo-tags-${Math.random().toString(36).slice(2)}`;!function t(d=!1){let p=document.createElement("style");p.textContent='\n.tag-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; min-height: 24px; font-size: 12px; }\n.tag-row .tag-chip {\n display: inline-flex; align-items: center; gap: 2px; max-width: 160px; height: 22px; padding: 0 2px 0 7px;\n border-radius: 6px; font-weight: 500; line-height: 1; color: var(--accent, #bda6ef);\n background: color-mix(in srgb, var(--accent, #bda6ef) 14%, transparent);\n}\n.tag-row .tag-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.tag-row .tag-chip button, .tag-row .tag-add {\n display: inline-flex; align-items: center; border: 0; padding: 0; color: inherit; background: none; cursor: pointer; font-family: inherit;\n}\n.tag-row .tag-chip button { justify-content: center; width: 18px; height: 18px; border-radius: 4px; opacity: .6; }\n.tag-row .tag-chip button:hover, .tag-row .tag-chip button:focus-visible { opacity: 1; background: color-mix(in srgb, currentColor 16%, transparent); }\n.tag-row .tag-add {\n gap: 4px; height: 22px; padding: 0 6px; border-radius: 6px; font-size: 12px; color: #858585;\n transition: color var(--duration-quick, 150ms), background var(--duration-quick, 150ms);\n}\n.tag-row .tag-add:hover, .tag-row .tag-add:focus-visible { color: #eee; background: #ffffff0d; }\n.tag-row .tag-input {\n width: 96px; height: 24px; padding: 0 8px; border: 0; border-radius: 6px; outline: none;\n font-family: inherit; font-size: 12px; color: #eee; background: #ffffff07; box-shadow: inset 0 0 0 1px #ffffff05;\n}\n.tag-row .tag-input::placeholder { color: #858585; }\n.tag-row .tag-input:focus { box-shadow: inset 0 0 0 1px #ffffff20; }\n.tag-row .tag-input[aria-invalid="true"] { box-shadow: inset 0 0 0 1px #f28b8299; }\n.tag-row .tag-options {\n flex-basis: 100%; display: grid; gap: 1px; max-height: 172px; overflow: auto; padding: 4px; border-radius: 8px;\n background: #ffffff07; box-shadow: inset 0 0 0 1px #ffffff0a;\n}\n.tag-row .tag-options:empty { display: none; }\n.tag-row .tag-option {\n display: flex; align-items: center; gap: 6px; min-height: 26px; padding: 0 8px; border-radius: 5px;\n color: #ddd; cursor: pointer; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;\n}\n.tag-row .tag-option[aria-selected="true"] { background: #ffffff12; color: #fff; }\n.tag-row .tag-option-hint, .tag-row .tag-option-empty { color: #858585; }\n.tag-row .tag-option-empty { padding: 5px 8px; }\n.new-comment-composer .draft-body .tag-slot { grid-row: 2; grid-column: 2; align-self: center; min-width: 0; }\n.new-comment-composer .draft-body:not(:has(.avatar)) .tag-slot { grid-column: 1; }\n/* Line up with message text: 16px gutter plus the 24px avatar and 8px gap. */\n.thread-tags { padding: 0 16px 12px 48px; margin-top: -6px; }\n';let s=document.createElement("div");s.className="tag-row",s.setAttribute("role","group"),s.setAttribute("aria-label","Tags");for(let a of n){let r=document.createElement("span");r.className="tag-chip";let o=document.createElement("span");o.textContent=`#${a}`;let i=document.createElement("button");i.type="button",i.textContent="×",i.setAttribute("aria-label",`Remove tag ${a}`),i.addEventListener("click",()=>{n=n.filter(t=>t!==a),e.onRemove(a),t()}),r.append(o,i),s.append(r)}if(r){let c=document.createElement("input");c.className="tag-input",c.placeholder="Add tag…",c.maxLength=33,c.autocomplete="off",c.setAttribute("role","combobox"),c.setAttribute("aria-label","Add tag"),c.setAttribute("aria-autocomplete","list"),c.setAttribute("aria-controls",l);let g=document.createElement("div");g.className="tag-options",g.id=l,g.setAttribute("role","listbox"),g.setAttribute("aria-label","Tags");let u=[],f=-1,m=a=>{!n.includes(a)&&n.length<12&&(n=[...n,a].sort(),e.onAdd(a)),r=n.length<12,t(r)},x=t=>{f=t,g.querySelectorAll("[role=option]").forEach((e,a)=>{e.setAttribute("aria-selected",String(a===t)),a===t&&e.scrollIntoView?.({block:"nearest"})}),t>=0?c.setAttribute("aria-activedescendant",`${l}-${t}`):c.removeAttribute("aria-activedescendant")},b=()=>{let t=c.value.trim().replace(/^#/,"").toLocaleLowerCase("en-US"),r=a(c.value),o=e.suggestions.filter(t=>!n.includes(t));u=(t?[...o.filter(e=>e.startsWith(t)),...o.filter(e=>!e.startsWith(t)&&e.includes(t))]:o).map(t=>({tag:t,create:!1})),r&&!n.includes(r)&&!o.includes(r)&&u.push({tag:r,create:!0}),g.replaceChildren(...u.map(({tag:t,create:e},a)=>{let n=document.createElement("div");return n.id=`${l}-${a}`,n.className="tag-option",n.setAttribute("role","option"),e&&n.append(Object.assign(document.createElement("span"),{className:"tag-option-hint",textContent:"Create"})),n.append(Object.assign(document.createElement("span"),{textContent:`#${t}`})),n.addEventListener("pointerdown",t=>t.preventDefault()),n.addEventListener("click",()=>m(t)),n.addEventListener("pointermove",()=>x(a)),n})),u.length||g.append(Object.assign(document.createElement("div"),{className:"tag-option-empty",textContent:t?n.includes(r??"")?"Already added":"Use letters, numbers, spaces, . _ or -":"Type to create a tag"})),c.setAttribute("aria-expanded",String(u.length>0)),x(t&&u.length?0:-1)};return c.addEventListener("input",b),c.addEventListener("keydown",o=>{if(o.stopPropagation(),!o.isComposing)if("ArrowDown"===o.key||"ArrowUp"===o.key){if(o.preventDefault(),!u.length)return;let t=u.length-1;x("ArrowDown"===o.key?f>=t?0:f+1:f<=0?t:f-1)}else if("Enter"===o.key||","===o.key)if(o.preventDefault(),f>=0&&u[f])m(u[f].tag);else if(c.value.trim()){let t=a(c.value);t?m(t):c.setAttribute("aria-invalid","true")}else r=!1,t();else if("Escape"===o.key)o.preventDefault(),r=!1,t();else if("Backspace"===o.key&&!c.value&&n.length){let a=n.at(-1);n=n.slice(0,-1),e.onRemove(a),t(!0)}}),c.addEventListener("input",()=>c.removeAttribute("aria-invalid")),c.addEventListener("blur",()=>{o||!c.isConnected||!r||(r=!1,t())}),s.append(c,g),i(p,s),b(),void((d||r)&&c.focus({preventScroll:!0}))}if(n.length<12){let e=document.createElement("button");e.type="button",e.className="tag-add",e.textContent=n.length?"+":"+ Add tag",e.setAttribute("aria-label","Add tag"),e.addEventListener("click",()=>{r=!0,t(!0)}),s.append(e)}i(p,s)}()}export{t as MAX_TAGS,a as normalizeTag,n as tagEditor};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const MAX_TAGS = 12;
|
|
2
|
+
export interface TagEditorOptions {
|
|
3
|
+
tags: readonly string[];
|
|
4
|
+
/** Tags already used in this project, offered while typing. */
|
|
5
|
+
suggestions: readonly string[];
|
|
6
|
+
onAdd(tag: string): void;
|
|
7
|
+
onRemove(tag: string): void;
|
|
8
|
+
}
|
|
9
|
+
/** Lowercase and validate a typed tag; returns null when it isn't a valid tag. */
|
|
10
|
+
export declare function normalizeTag(value: string): string | null;
|
|
11
|
+
/** Render removable tag chips and an inline "Tag" input into `slot`. */
|
|
12
|
+
export declare function tagEditor(slot: HTMLElement, options: TagEditorOptions): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as t,A as e,B as n,C as r,D as i,E as o,F as s,G as a,H as l,I as u,J as c,K as d,L as h,M as m,N as p,O as f,P as g,Q as v,R as y,S as w,T as x,U as E,V as P,W as S,X as b,Y as C,Z as M,_ as k,a as T,aa as A,b as L,ba as D,c as I,ca as j,da as V,ea as _,fa as O,ga as R,ha as W,ia as N,j as B,ja as F,k as H,ka as U,l as G,m as $,n as Y,o as X,p as z,q,r as J,s as K,t as Q,u as Z,v as tt,w as et,x as nt,y as rt,z as it}from"./chunk-7YUXYVQA.js";import{c as ot,g as st}from"./chunk-E5X4IQJ2.js";import{a as at}from"./chunk-I7AUKTXE.js";import{createRoot as lt}from"react-dom/client";import{flushSync as ut}from"react-dom";import{createElement as ct}from"react";import*as dt from"react";var ht=({size:t=24,color:e="currentColor",...n})=>dt.createElement("svg",{viewBox:"0 0 24 24",width:t,height:t,stroke:e,strokeWidth:"2",fill:"none",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",...n},dt.createElement("path",{d:"m7 15 5 5 5-5M7 9l5-5 5 5"}));ht.displayName="ChevronSelectorVertical";import*as mt from"react";var pt=({size:t=24,color:e="currentColor",...n})=>mt.createElement("svg",{viewBox:"0 0 24 24",width:t,height:t,stroke:e,strokeWidth:"2",fill:"none",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",...n},mt.createElement("path",{d:"M18 6 6 18M6 6l12 12"}));pt.displayName="XClose";import*as ft from"react";var gt=({size:t=24,color:e="currentColor",...n})=>ft.createElement("svg",{viewBox:"0 0 24 24",width:t,height:t,stroke:e,strokeWidth:"2",fill:"none",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",...n},ft.createElement("path",{d:"M19 12H5m0 0 7 7m-7-7 7-7"}));gt.displayName="ArrowLeft";import*as vt from"react";var yt=({size:t=24,color:e="currentColor",...n})=>vt.createElement("svg",{viewBox:"0 0 24 24",width:t,height:t,stroke:e,strokeWidth:"2",fill:"none",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",...n},vt.createElement("path",{d:"m9 18 6-6-6-6"}));yt.displayName="ChevronRight";import{useEffect as wt,useId as xt,useLayoutEffect as Et,useRef as Pt,useState as St}from"react";import{createContext as bt}from"react";var Ct=bt({});import{useRef as Mt}from"react";import{useLayoutEffect as kt,useEffect as Tt}from"react";var At=typeof window<"u"?kt:Tt;import{createContext as Lt}from"react";var Dt=Lt(null);import{createContext as It}from"react";var jt=It({transformPagePoint:t=>t,isStatic:!1,reducedMotion:"never"});import{useContext as Vt,useId as _t,useEffect as Ot,useCallback as Rt}from"react";import{createContext as Wt}from"react";var Nt=Wt({strict:!1}),Bt={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},Ft=!1;function Ht(){return function(){if(Ft)return;let t={};for(let e in Bt)t[e]={isEnabled:t=>Bt[e].some(e=>!!t[e])};v(t),Ft=!0}(),y()}var Ut=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","propagate","ignoreStrict","viewport"]);function Gt(t){return t.startsWith("while")||t.startsWith("drag")&&"draggable"!==t||t.startsWith("layout")||t.startsWith("onTap")||t.startsWith("onPan")||t.startsWith("onLayout")||Ut.has(t)}var $t,Yt=t=>!Gt(t);try{"function"==typeof($t=at("@emotion/is-prop-valid").default)&&(Yt=t=>t.startsWith("on")?!Gt(t):$t(t))}catch{}import{jsxs as Xt,jsx as zt}from"react/jsx-runtime";import{forwardRef as qt,useContext as Jt}from"react";import{createContext as Kt}from"react";var Qt=Kt({});import{useContext as Zt,useMemo as te}from"react";function ee(t){let{initial:e,animate:n}=function(t,e){if(h(t)){let{initial:e,animate:n}=t;return{initial:!1===e||d(e)?e:void 0,animate:d(n)?n:void 0}}return!1!==t.inherit?e:{}}(t,Zt(Qt));return te(()=>({initial:e,animate:n}),[ne(e),ne(n)])}function ne(t){return Array.isArray(t)?t.join(" "):t}import{Fragment as re,useMemo as ie,createElement as oe}from"react";import{useMemo as se}from"react";var ae=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function le(t,e,n){for(let r in e)!rt(e[r])&&!b(r,n)&&(t[r]=e[r])}function ue(t,e){let n={};return le(n,t.style||{},t),Object.assign(n,function({transformTemplate:t},e){return se(()=>{let n={style:{},transform:{},transformOrigin:{},vars:{}};return S(n,e,t),Object.assign({},n.vars,n.style)},[e])}(t,e)),n}function ce(t,e){let n={},r=ue(t,e);return t.drag&&!1!==t.dragListener&&(n.draggable=!1,r.userSelect=r.WebkitUserSelect=r.WebkitTouchCallout="none",r.touchAction=!0===t.drag?"none":"pan-"+("x"===t.drag?"y":"x")),void 0===t.tabIndex&&(t.onTap||t.onTapStart||t.whileTap)&&(n.tabIndex=0),n.style=r,n}import{useMemo as de}from"react";var he=()=>({style:{},transform:{},transformOrigin:{},vars:{},attrs:{}});function me(e,n,r,i){let o=de(()=>{let r={style:{},transform:{},transformOrigin:{},vars:{},attrs:{}};return k(r,n,t(i),e.transformTemplate,e.style),{...r.attrs,style:{...r.style}}},[n]);if(e.style){let t={};le(t,e.style,e),o.style={...t,...o.style}}return o}var pe=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function fe(t){return"string"==typeof t&&!t.includes("-")&&!!(pe.indexOf(t)>-1||/[A-Z]/u.test(t))}function ge(t,e,n,{latestValues:r},i,o=!1,s){let a=(s??fe(t)?me:ce)(e,r,i,t),l=function(t,e,n){let r={};for(let i in t)"values"===i&&"object"==typeof t.values||rt(t[i])||(Yt(i)||!0===n&&Gt(i)||!e&&!Gt(i)||t.draggable&&i.startsWith("onDrag"))&&(r[i]=t[i]);return r}(e,"string"==typeof t,o),u=t!==re?{...l,...a,ref:n}:{},{children:c}=e,d=ie(()=>rt(c)?c.get():c,[c]);return oe(t,{...u,children:d})}import{useContext as ve}from"react";function ye(t,e,n,r){let i={},o=r(t,{});for(let t in o)i[t]=R(o[t]);let{initial:s,animate:a}=t,l=h(t),u=m(t);e&&u&&!l&&!1!==t.inherit&&(void 0===s&&(s=e.initial),void 0===a&&(a=e.animate));let d=!!n&&!1===n.initial;d=d||!1===s;let p=d?a:s;if(p&&"boolean"!=typeof p&&!c(p)){let e=Array.isArray(p)?p:[p];for(let n=0;n<e.length;n++){let r=et(t,e[n]);if(r){let{transitionEnd:t,transition:e,...n}=r;for(let t in n){let e=n[t];if(Array.isArray(e)){e=e[d?e.length-1:0]}null!==e&&(i[t]=e)}for(let e in t)i[e]=t[e]}}}return i}var we=t=>(e,n)=>{let r=ve(Qt),i=ve(Dt),o=()=>function({scrapeMotionValuesFromProps:t,createRenderState:e},n,r,i){return{latestValues:ye(n,r,i,t),renderState:e()}}(t,e,r,i);return n?o():function(t){let e=Mt(null);return null===e.current&&(e.current=t()),e.current}(o)},xe=we({scrapeMotionValuesFromProps:C,createRenderState:ae}),Ee=we({scrapeMotionValuesFromProps:A,createRenderState:he}),Pe=Symbol.for("motionComponentSymbol");import{useRef as Se,useInsertionEffect as be,useCallback as Ce}from"react";function Me(t,e,n){let r=Se(n);be(()=>{r.current=n});let i=Se(null);return Ce(n=>{n&&t.onMount?.(n),e&&(n?e.mount(n):e.unmount());let o=r.current;if("function"==typeof o)if(n){let t=o(n);"function"==typeof t&&(i.current=t)}else i.current?(i.current(),i.current=null):o(n);else o&&(o.current=n)},[e])}import{useContext as ke,useRef as Te,useInsertionEffect as Ae,useEffect as Le}from"react";import{createContext as De}from"react";var Ie=De({});function je(t){return t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"current")}function Ve(t,n,r,i,o,s){let{visualElement:a}=ke(Qt),l=ke(Nt),u=ke(Dt),c=ke(jt),d=c.reducedMotion,h=c.skipAnimations,m=Te(null),p=Te(!1);i=i||l.renderer,!m.current&&i&&(m.current=i(t,{visualState:n,parent:a,props:r,presenceContext:u,blockInitialAnimation:!!u&&!1===u.initial,reducedMotionConfig:d,skipAnimations:h,isSVG:s}),p.current&&m.current&&(m.current.manuallyAnimateOnMount=!0));let f=m.current,g=ke(Ie);f&&!f.projection&&o&&("html"===f.type||"svg"===f.type)&&function(t,e,n,r){let{layoutId:i,layout:o,drag:s,dragConstraints:a,layoutScroll:l,layoutRoot:u,layoutAnchor:c,layoutCrossfade:d}=e;t.projection=new n(t.latestValues,e["data-framer-portal-id"]?void 0:_e(t.parent)),t.projection.setOptions({layoutId:i,layout:o,alwaysMeasureLayout:!!s||a&&je(a),visualElement:t,animationType:"string"==typeof o?o:"both",initialPromotionConfig:r,crossfade:d,layoutScroll:l,layoutRoot:u,layoutAnchor:c})}(m.current,r,o,g);let v=Te(!1);Ae(()=>{f&&v.current&&f.update(r,u)});let y=r[e],w=Te(!!y&&typeof window<"u"&&!window.MotionHandoffIsComplete?.(y)&&window.MotionHasOptimisedAnimation?.(y));return At(()=>{p.current=!0,f&&(v.current=!0,window.MotionIsMounted=!0,f.updateFeatures(),f.scheduleRenderMicrotask(),w.current&&f.animationState&&f.animationState.animateChanges())}),Le(()=>{f&&(!w.current&&f.animationState&&f.animationState.animateChanges(),w.current&&(queueMicrotask(()=>{window.MotionHandoffMarkAsComplete?.(y)}),w.current=!1),f.enteringChildren=void 0)}),f}function _e(t){if(t)return!1!==t.options.allowProjection?t.projection:_e(t.parent)}function Oe(t,{forwardMotionProps:e=!1,type:n}={},r,i){r&&function(t){let e=Ht();for(let n in t)e[n]={...e[n],...t[n]};v(e)}(r);let o=n?"svg"===n:fe(t),s=o?Ee:xe;function a(n,r){let a,l={...Jt(jt),...n,layoutId:Re(n)},{isStatic:u}=l,c=ee(n),d=s(n,u);if(!u&&typeof window<"u"){Jt(Nt).strict;let e=function(t){let e=Ht(),{drag:n,layout:r}=e;if(!n&&!r)return{};let i={...n,...r};return{MeasureLayout:n?.isEnabled(t)||r?.isEnabled(t)?i.MeasureLayout:void 0,ProjectionNode:i.ProjectionNode}}(l);a=e.MeasureLayout,c.visualElement=Ve(t,d,l,i,e.ProjectionNode,o)}return Xt(Qt.Provider,{value:c,children:[a&&c.visualElement?zt(a,{visualElement:c.visualElement,...l}):null,ge(t,n,Me(d,c.visualElement,r),d,u,e,o)]})}a.displayName=`motion.${"string"==typeof t?t:`create(${t.displayName??t.name??""})`}`;let l=qt(a);return l[Pe]=t,l}function Re({layoutId:t}){let e=Jt(Ct).id;return e&&void 0!==t?e+"-"+t:t}import{Fragment as We}from"react";var Ne=0,Be={animation:{Feature:class extends w{constructor(t){super(t),t.animationState||(t.animationState=j(t))}updateAnimationControlsSubscription(){let{animate:t}=this.node.getProps();c(t)&&(this.unmountControls=t.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){let{animate:t}=this.node.getProps(),{animate:e}=this.node.prevProps||{};t!==e&&this.updateAnimationControlsSubscription()}unmount(){this.node.animationState.reset(),this.unmountControls?.()}}},exit:{Feature:class extends w{constructor(){super(...arguments),this.id=Ne++,this.isExitComplete=!1}update(){if(!this.node.presenceContext)return;let{isPresent:t,onExitComplete:e}=this.node.presenceContext,{isPresent:n}=this.node.prevPresenceContext||{};if(!this.node.animationState||t===n)return;if(t&&!1===n){if(this.isExitComplete){let{initial:t,custom:e}=this.node.getProps();if("string"==typeof t||"object"==typeof t&&null!==t&&!Array.isArray(t)){let n=nt(this.node,t,e);if(n){let{transition:t,transitionEnd:e,...r}=n;for(let t in r)this.node.getValue(t)?.jump(r[t])}}this.node.animationState.reset(),this.node.animationState.animateChanges()}else this.node.animationState.setActive("exit",!1);return void(this.isExitComplete=!1)}let r=this.node.animationState.setActive("exit",!t);e&&!t&&r.then(()=>{this.isExitComplete=!0,e(this.id)})}mount(){let{register:t,onExitComplete:e}=this.node.presenceContext||{};e&&e(this.id),t&&(this.unmount=t(this.id))}unmount(){}}}};function Fe(t){return{point:{x:t.pageX,y:t.pageY}}}function He(t,e,n,r){return O(t,e,(t=>e=>o(e)&&t(e,Fe(e)))(n),r)}var Ue=({current:t})=>t?t.ownerDocument.defaultView:null,Ge=new Set(["auto","scroll"]),$e=class{constructor(t,e,{transformPagePoint:n,contextWindow:r=window,dragSnapToOrigin:i=!1,distanceThreshold:s=3,element:a}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.lastRawMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.scrollPositions=new Map,this.removeScrollListeners=null,this.onElementScroll=t=>{this.handleScroll(t.target)},this.onWindowScroll=()=>{this.handleScroll(window)},this.updatePoint=()=>{if(!this.lastMoveEvent||!this.lastMoveEventInfo)return;this.lastRawMoveEventInfo&&(this.lastMoveEventInfo=Ye(this.lastRawMoveEventInfo,this.transformPagePoint));let t=ze(this.lastMoveEventInfo,this.history),e=null!==this.startEvent,n=U(t.offset,{x:0,y:0})>=this.distanceThreshold;if(!e&&!n)return;let{point:r}=t,{timestamp:i}=K;this.history.push({...r,timestamp:i});let{onStart:o,onMove:s}=this.handlers;e||(o&&o(this.lastMoveEvent,t),this.startEvent=this.lastMoveEvent),s&&s(this.lastMoveEvent,t)},this.handlePointerMove=(t,e)=>{this.lastMoveEvent=t,this.lastRawMoveEventInfo=e,this.lastMoveEventInfo=Ye(e,this.transformPagePoint),q.update(this.updatePoint,!0)},this.handlePointerUp=(t,e)=>{this.end();let{onEnd:n,onSessionEnd:r,resumeAnimation:i}=this.handlers;if((this.dragSnapToOrigin||!this.startEvent)&&i&&i(),!this.lastMoveEvent||!this.lastMoveEventInfo)return;let o=ze("pointercancel"===t.type?this.lastMoveEventInfo:Ye(e,this.transformPagePoint),this.history);this.startEvent&&n&&n(t,o),r&&r(t,o)},!o(t))return;this.dragSnapToOrigin=i,this.handlers=e,this.transformPagePoint=n,this.distanceThreshold=s,this.contextWindow=r||window;let l=Ye(Fe(t),this.transformPagePoint),{point:u}=l,{timestamp:c}=K;this.history=[{...u,timestamp:c}];let{onSessionStart:d}=e;d&&d(t,ze(l,this.history));let h={passive:!0,capture:!0};this.removeListeners=$(He(this.contextWindow,"pointermove",this.handlePointerMove,h),He(this.contextWindow,"pointerup",this.handlePointerUp,h),He(this.contextWindow,"pointercancel",this.handlePointerUp,h)),a&&this.startScrollTracking(a)}startScrollTracking(t){let e=t.parentElement;for(;e;){let t=getComputedStyle(e);(Ge.has(t.overflowX)||Ge.has(t.overflowY))&&this.scrollPositions.set(e,{x:e.scrollLeft,y:e.scrollTop}),e=e.parentElement}this.scrollPositions.set(window,{x:window.scrollX,y:window.scrollY}),window.addEventListener("scroll",this.onElementScroll,{capture:!0}),window.addEventListener("scroll",this.onWindowScroll),this.removeScrollListeners=()=>{window.removeEventListener("scroll",this.onElementScroll,{capture:!0}),window.removeEventListener("scroll",this.onWindowScroll)}}handleScroll(t){let e=this.scrollPositions.get(t);if(!e)return;let n=t===window,r=n?{x:window.scrollX,y:window.scrollY}:{x:t.scrollLeft,y:t.scrollTop},i=r.x-e.x,o=r.y-e.y;0===i&&0===o||(n?this.lastMoveEventInfo&&(this.lastMoveEventInfo.point.x+=i,this.lastMoveEventInfo.point.y+=o):this.history.length>0&&(this.history[0].x-=i,this.history[0].y-=o),this.scrollPositions.set(t,r),q.update(this.updatePoint,!0))}updateHandlers(t){this.handlers=t}end(){this.removeListeners&&this.removeListeners(),this.removeScrollListeners&&this.removeScrollListeners(),this.scrollPositions.clear(),J(this.updatePoint)}};function Ye(t,e){return e?{point:e(t.point)}:t}function Xe(t,e){return{x:t.x-e.x,y:t.y-e.y}}function ze({point:t},e){return{point:t,delta:Xe(t,Je(e)),offset:Xe(t,qe(e)),velocity:Ke(e,.1)}}function qe(t){return t[0]}function Je(t){return t[t.length-1]}function Ke(t,e){if(t.length<2)return{x:0,y:0};let n=t.length-1,r=null,i=Je(t);for(;n>=0&&(r=t[n],!(i.timestamp-r.timestamp>X(e)));)n--;if(!r)return{x:0,y:0};r===t[0]&&t.length>2&&i.timestamp-r.timestamp>2*X(e)&&(r=t[1]);let o=z(i.timestamp-r.timestamp);if(0===o)return{x:0,y:0};let s={x:(i.x-r.x)/o,y:(i.y-r.y)/o};return s.x===1/0&&(s.x=0),s.y===1/0&&(s.y=0),s}function Qe(t,e,n){return{min:void 0!==e?t.min+e:void 0,max:void 0!==n?t.max+n-(t.max-t.min):void 0}}function Ze(t,e){let n=e.min-t.min,r=e.max-t.max;return e.max-e.min<t.max-t.min&&([n,r]=[r,n]),{min:n,max:r}}var tn=.35;function en(t,e,n){return{min:nn(t,e),max:nn(t,n)}}function nn(t,e){return"number"==typeof t?t:t[e]||0}var rn=new WeakMap,on=class{constructor(t){this.openDragLock=null,this.isDragging=!1,this.currentDirection=null,this.originPoint={x:0,y:0},this.constraints=!1,this.hasMutatedConstraints=!1,this.elastic=u(),this.latestPointerEvent=null,this.latestPanInfo=null,this.visualElement=t}start(t,{snapToCursor:e=!1,distanceThreshold:n}={}){let{presenceContext:i}=this.visualElement;if(i&&!1===i.isPresent)return;let{dragSnapToOrigin:o}=this.getProps();this.panSession=new $e(t,{onSessionStart:t=>{e&&this.snapToCursor(Fe(t).point),this.stopAnimation()},onStart:(t,e)=>{let{drag:n,dragPropagation:i,onDragStart:o}=this.getProps();if(n&&!i&&(this.openDragLock&&this.openDragLock(),this.openDragLock=r(n),!this.openDragLock))return;this.latestPointerEvent=t,this.latestPanInfo=e,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),_(t=>{let e=this.getAxisMotionValue(t).get()||0;if(Q.test(e)){let{projection:n}=this.visualElement;if(n&&n.layout){let r=n.layout.layoutBox[t];r&&(e=V(r)*(parseFloat(e)/100))}}this.originPoint[t]=e}),o&&q.update(()=>o(t,e),!1,!0),it(this.visualElement,"transform");let{animationState:s}=this.visualElement;s&&s.setActive("whileDrag",!0)},onMove:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e;let{dragPropagation:n,dragDirectionLock:r,onDirectionLock:i,onDrag:o}=this.getProps();if(!n&&!this.openDragLock)return;let{offset:s}=e;if(r&&null===this.currentDirection)return this.currentDirection=function(t,e=10){let n=null;return Math.abs(t.y)>e?n="y":Math.abs(t.x)>e&&(n="x"),n}(s),void(null!==this.currentDirection&&i&&i(this.currentDirection));this.updateAxis("x",e.point,s),this.updateAxis("y",e.point,s),this.visualElement.render(),o&&q.update(()=>o(t,e),!1,!0)},onSessionEnd:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e,this.stop(t,e),this.latestPointerEvent=null,this.latestPanInfo=null},resumeAnimation:()=>{let{dragSnapToOrigin:t}=this.getProps();(t||this.constraints)&&this.startAnimation({x:0,y:0})}},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:o,distanceThreshold:n,contextWindow:Ue(this.visualElement),element:this.visualElement.current})}stop(t,e){let n=t||this.latestPointerEvent,r=e||this.latestPanInfo,i=this.isDragging;if(this.cancel(),!i||!r||!n)return;let{velocity:o}=r;this.startAnimation(o);let{onDragEnd:s}=this.getProps();s&&q.postRender(()=>s(n,r))}cancel(){this.isDragging=!1;let{projection:t,animationState:e}=this.visualElement;t&&(t.isAnimationBlocked=!1),this.endPanSession();let{dragPropagation:n}=this.getProps();!n&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),e&&e.setActive("whileDrag",!1)}endPanSession(){this.panSession&&this.panSession.end(),this.panSession=void 0}updateAxis(t,e,n){let{drag:r}=this.getProps();if(!n||!an(t,r,this.currentDirection))return;let i=this.getAxisMotionValue(t),o=this.originPoint[t]+n[t];this.constraints&&this.constraints[t]&&(o=function(t,{min:e,max:n},r){return void 0!==e&&t<e?t=r?Z(e,t,r.min):Math.max(t,e):void 0!==n&&t>n&&(t=r?Z(n,t,r.max):Math.min(t,n)),t}(o,this.constraints[t],this.elastic[t])),i.set(o)}resolveConstraints(){let{dragConstraints:t,dragElastic:e}=this.getProps(),n=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):this.visualElement.projection?.layout,r=this.constraints;t&&je(t)?this.constraints||(this.constraints=this.resolveRefConstraints()):this.constraints=!(!t||!n)&&function(t,{top:e,left:n,bottom:r,right:i}){return{x:Qe(t.x,n,i),y:Qe(t.y,e,r)}}(n.layoutBox,t),this.elastic=function(t=tn){return!1===t?t=0:!0===t&&(t=tn),{x:en(t,"left","right"),y:en(t,"top","bottom")}}(e),r!==this.constraints&&!je(t)&&n&&this.constraints&&!this.hasMutatedConstraints&&_(t=>{!1!==this.constraints&&this.getAxisMotionValue(t)&&(this.constraints[t]=function(t,e){let n={};return void 0!==e.min&&(n.min=e.min-t.min),void 0!==e.max&&(n.max=e.max-t.min),n}(n.layoutBox[t],this.constraints[t]))})}resolveRefConstraints(){let{dragConstraints:t,onMeasureDragConstraints:e}=this.getProps();if(!t||!je(t))return!1;let n=t.current;H(null!==n,"If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop.","drag-constraints-ref");let{projection:r}=this.visualElement;if(!r||!r.layout)return!1;r.root&&(r.root.scroll=void 0,r.root.updateScroll());let i=P(n,r.root,this.visualElement.getTransformPagePoint()),o=function(t,e){return{x:Ze(t.x,e.x),y:Ze(t.y,e.y)}}(r.layout.layoutBox,i);if(e){let t=e(E(o));this.hasMutatedConstraints=!!t,t&&(o=x(t))}return o}startAnimation(t){let{drag:e,dragMomentum:n,dragElastic:r,dragTransition:i,dragSnapToOrigin:o,onDragTransitionEnd:s}=this.getProps(),a=this.constraints||{},l=_(s=>{if(!an(s,e,this.currentDirection))return;let l=a&&a[s]||{};(!0===o||o===s)&&(l={min:0,max:0});let u=r?200:1e6,c=r?40:1e7,d={type:"inertia",velocity:n?t[s]:0,bounceStiffness:u,bounceDamping:c,timeConstant:750,restDelta:1,restSpeed:10,...i,...l};return this.startAxisValueAnimation(s,d)});return Promise.all(l).then(s)}startAxisValueAnimation(t,e){let n=this.getAxisMotionValue(t);return it(this.visualElement,t),n.start(tt(t,n,0,e,this.visualElement,!1))}stopAnimation(){_(t=>this.getAxisMotionValue(t).stop())}getAxisMotionValue(t){let e=`_drag${t.toUpperCase()}`;return this.visualElement.getProps()[e]||this.visualElement.getValue(t,this.visualElement.latestValues[t]??0)}snapToCursor(t){_(e=>{let{drag:n}=this.getProps();if(!an(e,n,this.currentDirection))return;let{projection:r}=this.visualElement,i=this.getAxisMotionValue(e);if(r&&r.layout){let{min:n,max:o}=r.layout.layoutBox[e],s=i.get()||0;i.set(t[e]-Z(n,o,.5)+s)}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;let{drag:t,dragConstraints:e}=this.getProps(),{projection:n}=this.visualElement;if(!je(e)||!n||!this.constraints)return;this.stopAnimation();let r={x:0,y:0};_(t=>{let e=this.getAxisMotionValue(t);if(e&&!1!==this.constraints){let n=e.get();r[t]=function(t,e){let n=.5,r=V(t),i=V(e);return i>r?n=Y(e.min,e.max-r,t.min):r>i&&(n=Y(t.min,t.max-i,e.min)),B(0,1,n)}({min:n,max:n},this.constraints[t])}});let{transformTemplate:i}=this.visualElement.getProps();this.visualElement.current.style.transform=i?i({},""):"none",n.root&&n.root.updateScroll(),n.updateLayout(),this.constraints=!1,this.resolveConstraints(),_(e=>{if(!an(e,t,null))return;let n=this.getAxisMotionValue(e),{min:i,max:o}=this.constraints[e];n.set(Z(i,o,r[e]))}),this.visualElement.render()}addListeners(){if(!this.visualElement.current)return;rn.set(this.visualElement,this);let t,e=this.visualElement.current,n=He(e,"pointerdown",t=>{let{drag:n,dragListener:r=!0}=this.getProps(),i=t.target,o=i!==e&&s(i);n&&r&&!o&&this.start(t)}),r=()=>{let{dragConstraints:n}=this.getProps();je(n)&&n.current&&(this.constraints=this.resolveRefConstraints(),t||(t=function(t,e,n){let r=l(t,sn(n)),i=l(e,sn(n));return()=>{r(),i()}}(e,n.current,()=>this.scalePositionWithinConstraints())))},{projection:i}=this.visualElement,o=i.addEventListener("measure",r);i&&!i.layout&&(i.root&&i.root.updateScroll(),i.updateLayout()),q.read(r);let a=O(window,"resize",()=>this.scalePositionWithinConstraints()),u=i.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e})=>{this.isDragging&&e&&(_(e=>{let n=this.getAxisMotionValue(e);n&&(this.originPoint[e]+=t[e].translate,n.set(n.get()+t[e].translate))}),this.visualElement.render())});return()=>{a(),n(),o(),u&&u(),t&&t()}}getProps(){let t=this.visualElement.getProps(),{drag:e=!1,dragDirectionLock:n=!1,dragPropagation:r=!1,dragConstraints:i=!1,dragElastic:o=tn,dragMomentum:s=!0}=t;return{...t,drag:e,dragDirectionLock:n,dragPropagation:r,dragConstraints:i,dragElastic:o,dragMomentum:s}}};function sn(t){let e=!0;return()=>{e?e=!1:t()}}function an(t,e,n){return!(!0!==e&&e!==t||null!==n&&n!==t)}var ln=t=>(e,n)=>{t&&q.update(()=>t(e,n),!1,!0)};import{jsx as un}from"react/jsx-runtime";import{useContext as cn,Component as dn}from"react";var hn=!1,mn=class extends dn{componentDidMount(){let{visualElement:t,layoutGroup:e,switchLayoutGroup:n,layoutId:r}=this.props,{projection:i}=t;i&&(e.group&&e.group.add(i),n&&n.register&&r&&n.register(i),hn&&i.root.didUpdate(),i.addEventListener("animationComplete",()=>{this.safeToRemove()}),i.setOptions({...i.options,layoutDependency:this.props.layoutDependency,onExitComplete:()=>this.safeToRemove()})),W.hasEverUpdated=!0}getSnapshotBeforeUpdate(t){let{layoutDependency:e,visualElement:n,drag:r,isPresent:i}=this.props,{projection:o}=n;return o&&(o.isPresent=i,t.layoutDependency!==e&&o.setOptions({...o.options,layoutDependency:e}),hn=!0,r||t.layoutDependency!==e||void 0===e||t.isPresent!==i?o.willUpdate():this.safeToRemove(),t.isPresent!==i&&(i?o.promote():o.relegate()||q.postRender(()=>{let t=o.getStack();(!t||!t.members.length)&&this.safeToRemove()}))),null}componentDidUpdate(){let{visualElement:t,layoutAnchor:e}=this.props,{projection:r}=t;r&&(r.options.layoutAnchor=e,r.root.didUpdate(),n.postRender(()=>{!r.currentAnimation&&r.isLead()&&this.safeToRemove()}))}componentWillUnmount(){let{visualElement:t,layoutGroup:e,switchLayoutGroup:n}=this.props,{projection:r}=t;hn=!0,r&&(r.scheduleCheckAfterUnmount(),e&&e.group&&e.group.remove(r),n&&n.deregister&&n.deregister(r))}safeToRemove(){let{safeToRemove:t}=this.props;t&&t()}render(){return null}};function pn(t){let[e,n]=function(t=!0){let e=Vt(Dt);if(null===e)return[!0,null];let{isPresent:n,onExitComplete:r,register:i}=e,o=_t();Ot(()=>{if(t)return i(o)},[t]);let s=Rt(()=>t&&r&&r(o),[o,r,t]);return!n&&r?[!1,s]:[!0]}(),r=cn(Ct);return un(mn,{...t,layoutGroup:r,switchLayoutGroup:cn(Ie),isPresent:e,safeToRemove:n})}var fn={pan:{Feature:class extends w{constructor(){super(...arguments),this.removePointerDownListener=G}onPointerDown(t){this.session=new $e(t,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:Ue(this.node)})}createPanHandlers(){let{onPanSessionStart:t,onPanStart:e,onPan:n,onPanEnd:r}=this.node.getProps();return{onSessionStart:ln(t),onStart:ln(e),onMove:ln(n),onEnd:(t,e)=>{delete this.session,r&&q.postRender(()=>r(t,e))}}}mount(){this.removePointerDownListener=He(this.node.current,"pointerdown",t=>this.onPointerDown(t))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}},drag:{Feature:class extends w{constructor(t){super(t),this.removeGroupControls=G,this.removeListeners=G,this.controls=new on(t)}mount(){let{dragControls:t}=this.node.getProps();t&&(this.removeGroupControls=t.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||G}update(){let{dragControls:t}=this.node.getProps(),{dragControls:e}=this.node.prevProps||{};t!==e&&(this.removeGroupControls(),t&&(this.removeGroupControls=t.subscribe(this.controls)))}unmount(){this.removeGroupControls(),this.removeListeners(),this.controls.isDragging||this.controls.endPanSession()}},ProjectionNode:N,MeasureLayout:pn}};function gn(t,e,n){let{props:r}=t;t.animationState&&r.whileHover&&t.animationState.setActive("whileHover","Start"===n);let i=r["onHover"+n];i&&q.postRender(()=>i(e,Fe(e)))}function vn(t,e,n){let{props:r}=t;if(t.current instanceof HTMLButtonElement&&t.current.disabled)return;t.animationState&&r.whileTap&&t.animationState.setActive("whileTap","Start"===n);let i=r["onTap"+("End"===n?"":n)];i&&q.postRender(()=>i(e,Fe(e)))}var yn=new WeakMap,wn=new WeakMap,xn=t=>{let e=yn.get(t.target);e&&e(t)},En=t=>{t.forEach(xn)};function Pn(t,e,n){let r=function({root:t,...e}){let n=t||document;wn.has(n)||wn.set(n,{});let r=wn.get(n),i=JSON.stringify(e);return r[i]||(r[i]=new IntersectionObserver(En,{root:t,...e})),r[i]}(e);return yn.set(t,n),r.observe(t),()=>{yn.delete(t),r.unobserve(t)}}var Sn={some:0,all:1};var bn={inView:{Feature:class extends w{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){this.stopObserver?.();let{viewport:t={}}=this.node.getProps(),{root:e,margin:n,amount:r="some",once:i}=t,o={root:e?e.current:void 0,rootMargin:n,threshold:"number"==typeof r?r:Sn[r]};this.stopObserver=Pn(this.node.current,o,t=>{let{isIntersecting:e}=t;if(this.isInView===e||(this.isInView=e,i&&!e&&this.hasEnteredView))return;e&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",e);let{onViewportEnter:n,onViewportLeave:r}=this.node.getProps(),o=e?n:r;o&&o(t)})}mount(){this.startObserver()}update(){if(typeof IntersectionObserver>"u")return;let{props:t,prevProps:e}=this.node;["amount","margin","root"].some(function({viewport:t={}},{viewport:e={}}={}){return n=>t[n]!==e[n]}(t,e))&&this.startObserver()}unmount(){this.stopObserver?.(),this.hasEnteredView=!1,this.isInView=!1}}},tap:{Feature:class extends w{mount(){let{current:t}=this.node;if(!t)return;let{globalTapTarget:e,propagate:n}=this.node.props;this.unmount=a(t,(t,e)=>(vn(this.node,e,"Start"),(t,{success:e})=>vn(this.node,t,e?"End":"Cancel")),{useGlobalTarget:e,stopPropagation:!1===n?.tap})}unmount(){}}},focus:{Feature:class extends w{constructor(){super(...arguments),this.isActive=!1}onFocus(){let t=!1;try{t=this.node.current.matches(":focus-visible")}catch{t=!0}!t||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){!this.isActive||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=$(O(this.node.current,"focus",()=>this.onFocus()),O(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}},hover:{Feature:class extends w{mount(){let{current:t}=this.node;t&&(this.unmount=i(t,(t,e)=>(gn(this.node,e,"Start"),t=>gn(this.node,t,"End"))))}unmount(){}}}},Cn=function(t,e){if(typeof Proxy>"u")return Oe;let n=new Map,r=(n,r)=>Oe(n,r,t,e);return new Proxy((t,e)=>r(t,e),{get:(i,o)=>"create"===o?r:(n.has(o)||n.set(o,Oe(o,void 0,t,e)),n.get(o))})}({...Be,...bn,...fn,...{layout:{ProjectionNode:N,MeasureLayout:pn}}},(t,e)=>e.isSVG??fe(t)?new D(e):new M(e,{allowProjection:t!==We}));import{useState as Mn}from"react";var kn=Cn;import{Fragment as Tn,jsx as An,jsxs as Ln}from"react/jsx-runtime";function Dn({items:t,defaultActiveId:e,activeId:n,edge:r="bottom",alignEnd:i=!1,onActiveChange:o,label:s="App navigation",moreLabel:a="More",backLabel:l="Back",className:u="",style:c}){let d=xt(),[h,m]=St({kind:"collapsed"}),[v,y]=St(e??t[0]?.id),[w,x]=St(null),E=Pt(null),P=Pt(null),S=Pt(r),b="left"===r||"right"===r;Et(()=>{let t=P.current;if(!t||!w)return;let e=t.getBoundingClientRect(),n=Math.max(8-e.left,Math.min(0,window.innerWidth-8-e.right)),r=Math.max(8-e.top,Math.min(0,window.innerHeight-8-e.bottom));t.style.left=`${w.x+n}px`,t.style.top=`${w.y+r}px`},[w]);let C=Pt(null),M=Pt(null),k=Pt(h),A=Pt("more"),D=Pt(!1),j=Pt(!1),V=Pt(!1),_=Pt(null),O=Pt(void 0),R=Pt(void 0),W=Pt(!1),N=Pt(!1),B=function(){!f.current&&g();let[t]=Mn(p.current);return t}(),H=n??v,U="collapsed"!==h.kind,G=t.filter(t=>!1!==t.showInBar),$=G.find(t=>"dismiss"===t.id),Y=G.filter(t=>"dismiss"!==t.id),X=t.filter(t=>t.children?.length);function z(){clearTimeout(O.current),x(null)}function q(){z(),clearTimeout(R.current),W.current=!1}function J(t,e,n=!1){clearTimeout(O.current);let i=E.current,o=M.current;if(!i||!o||U||j.current||V.current&&!n)return;let s=i.getBoundingClientRect(),a=t.getBoundingClientRect(),l={label:e,x:"left"===r?s.width+8:"right"===r?-8:a.left-s.left+a.width/2,y:"top"===r?s.height+8:"bottom"===r?-8:a.top-s.top+a.height/2};clearTimeout(R.current);let u=()=>{W.current=!0,x(l)};n||W.current?u():O.current=setTimeout(u,400)}function K(t,e,n){q(),U||(A.current=e,D.current=n),_.current="first",m(t)}function Q(t=!0,e=A.current){q(),V.current=!0,_.current=t&&D.current?e:null,m({kind:"collapsed"})}function Z(){"group"===h.kind&&(_.current=h.id,m({kind:"main"}))}function tt(t,e){let n=!!t.children?.length,r=H===t.id||!!t.children?.some(t=>t.id===H),i=!e&&t.keepOpenOnSelect,s=(e,n=!1)=>An(jn,{label:t.label,icon:n,reduced:!!B,children:e}),a=r&&t.activeIcon?t.activeIcon:t.icon,l=Ln(Tn,{children:[An("span",{className:"morphing-menu__icon","aria-hidden":"true",style:i?{display:"grid"}:void 0,children:i?s(a,!0):a}),!e&&An("span",{className:"morphing-menu__label",style:i?{display:"grid"}:void 0,children:i?s(t.label):t.label}),!e&&n&&An(In,{})]}),u={className:e?"morphing-menu__shortcut":"morphing-menu__row","data-menu-item":t.id,"aria-label":t.label,"aria-current":r?n?"true":"page":void 0,"aria-expanded":n?"group"===h.kind&&h.id===t.id:void 0,"aria-controls":n?`${d}-group-${t.id}`:void 0,onClick:e=>n?K({kind:"group",id:t.id},t.id,0===e.detail):function(t){y(t.id),o?.(t.id),U&&!t.keepOpenOnSelect?Q(!0,"group"===h.kind?h.id:t.id):q(),t.onSelect?.()}(t),onPointerEnter:n=>{e&&"mouse"===n.pointerType&&J(n.currentTarget,t.label)},onPointerLeave:z,onPointerDown:q,onFocus:n=>{e&&n.currentTarget.matches(":focus-visible")&&J(n.currentTarget,t.label,!0)},onBlur:z};return t.href&&!n?An("a",{href:t.href,...u,children:l},t.id):An(kn.button,{layout:!!e&&"position",transition:B?{duration:0}:T,type:"button",...u,children:l},t.id)}Et(()=>{let t=E.current,e=C.current,n=M.current;if(!t||!e||!n)return;let i=S.current!==r;S.current=r;let o=k.current;k.current=h;let s=[...t.querySelectorAll(".morphing-menu__panel")],a=s.find(t=>"false"===t.getAttribute("aria-hidden")),l=[],u=!1,c=t=>(l.push(t),t),d=()=>({width:a?.offsetWidth??n.offsetWidth,height:a?.offsetHeight??n.offsetHeight}),p=i||o.kind!==h.kind||"group"===o.kind&&"group"===h.kind&&o.id!==h.id,f="collapsed"===o.kind!=("collapsed"===h.kind),g=B||!p;if(g)(()=>{let t=d();e.style.width=`${t.width}px`,e.style.height=`${t.height}px`})();else if(f&&!U)c(F(e,d(),I));else if(f){let t=Math.min(n.offsetWidth,n.offsetWidth>250?280:200);c(F(e,{width:t,height:n.offsetHeight>52?32:28},L)).finished.then(()=>{u||c(F(e,d(),{...T,bounce:U?.24:.15}))}).catch(()=>{})}else c(F(e,d(),{...T,duration:.25,bounce:.1}));c(F(n,{opacity:U?0:1,scale:U&&!B?.8:1,filter:U&&!B?"blur(8px)":"blur(0px)"},{duration:g?0:U?.15:.22,delay:g||U?0:.08,...U?{}:{ease:"easeOut"}}));for(let t of s){let e=t===a;for(let[n,r]of[...t.querySelectorAll(".morphing-menu__row")].entries())e&&f&&!g&&Object.assign(r.style,{opacity:"0",transform:"translateY(48px)",filter:"blur(4px)"}),c(F(r,{opacity:e?1:0,y:e||B?0:16,filter:e||B?"blur(0px)":"blur(2px)"},{...T,duration:g?0:e?f?.4:.25:.12,bounce:f?.3:0,delay:!g&&e?(f?.2:0)+.02*n:0}))}let v=[...(U?a:n)?.querySelectorAll("[data-menu-item]")??[]],y=_.current;_.current=null,y&&(j.current=!0,(("first"===y?v[0]:v.find(t=>t.dataset.menuItem===y))??(U?void 0:v.find(t=>"more"===t.dataset.menuItem)))?.focus({preventScroll:!0}),j.current=!1);let w=()=>m(t=>({...t}));return window.addEventListener("resize",w),()=>{u=!0,l.forEach(t=>t.stop()),window.removeEventListener("resize",w)}},[h,B,r,t.length,G.length]),wt(()=>{if(!U)return;let t=t=>{t.target instanceof Node&&!t.composedPath().includes(E.current)&&Q(!1)};return document.addEventListener("pointerdown",t),()=>document.removeEventListener("pointerdown",t)},[U]),wt(()=>()=>{clearTimeout(O.current),clearTimeout(R.current)},[]);let et=Ln("button",{type:"button",className:"morphing-menu__row","data-menu-item":"close",onClick:()=>Q(!0,"more"),children:[An(pt,{"aria-hidden":"true"}),An("span",{className:"morphing-menu__label",children:"Close drawer"})]});return Ln("nav",{ref:E,"aria-label":s,className:["morphing-menu",u].filter(Boolean).join(" "),"data-view":h.kind,"data-edge":r,"data-vertical":b,"data-align-end":i,style:{"--mm-count":G.length+1,...c},onPointerMove:t=>{if(!V.current||"mouse"!==t.pointerType||!t.movementX&&!t.movementY)return;V.current=!1;let e=t.target instanceof Element?t.target.closest(".morphing-menu__shortcut"):null;e&&J(e,e.getAttribute("aria-label")??"")},onKeyDown:t=>{"Escape"===t.key&&(V.current=!0,q(),U&&(t.preventDefault(),t.stopPropagation(),"group"===h.kind?Z():Q()))},onBlur:t=>{t.relatedTarget instanceof Node&&t.currentTarget.contains(t.relatedTarget)||(U?t.relatedTarget instanceof Node&&Q(!1):N.current||q())},children:[Ln("div",{ref:C,className:"morphing-menu__shell",children:[Ln("div",{ref:M,className:"morphing-menu__bar","aria-hidden":U,inert:U,onPointerEnter:t=>{"mouse"===t.pointerType&&(N.current=!0,clearTimeout(R.current))},onPointerLeave:t=>{"mouse"===t.pointerType&&(N.current=!1,z(),clearTimeout(R.current),R.current=setTimeout(()=>{W.current=!1},300))},children:[Y.map(t=>tt(t,!0)),An(kn.button,{layout:"position",transition:B?{duration:0}:T,type:"button",className:"morphing-menu__shortcut","data-menu-item":"more","aria-label":a,"aria-expanded":U,"aria-controls":`${d}-main`,onClick:t=>K({kind:"main"},"more",0===t.detail),onPointerEnter:t=>{"mouse"===t.pointerType&&J(t.currentTarget,a)},onPointerLeave:z,onPointerDown:q,onBlur:z,onFocus:t=>{t.currentTarget.matches(":focus-visible")&&J(t.currentTarget,a,!0)},children:An(ht,{"aria-hidden":"true"})}),$&&tt($,!0)]}),Ln("div",{id:`${d}-main`,className:"morphing-menu__panel","aria-hidden":"main"!==h.kind,inert:"main"!==h.kind,children:[[...t].sort((t,e)=>(t.expandedOrder??0)-(e.expandedOrder??0)).map(t=>tt(t,!1)),et]}),X.map(t=>{let e="group"===h.kind&&h.id===t.id;return Ln("div",{id:`${d}-group-${t.id}`,className:"morphing-menu__panel","aria-label":t.label,"aria-hidden":!e,inert:!e,children:[Ln("button",{type:"button",className:"morphing-menu__row","data-menu-item":"back",onClick:Z,children:[An(gt,{"aria-hidden":"true"}),An("span",{children:l})]}),t.children.map(t=>tt(t,!1)),et]},t.id)})]}),w&&!U&&An("div",{ref:P,className:"morphing-menu__tooltip","aria-hidden":"true",style:{left:w.x,top:w.y},children:w.label})]})}function In(){return An(yt,{className:"morphing-menu__chevron","aria-hidden":"true"})}function jn({label:t,icon:e,reduced:n,children:r}){let i=Pt(null),o=Pt(null);return Et(()=>{let r=i.current,s=o.current;if(o.current={label:t,node:r.cloneNode(!0)},!s||s.label===t||n)return;let a=s.node;a.setAttribute("aria-hidden","true"),r.parentElement.append(a);let l={duration:200,easing:"cubic-bezier(.22,1,.36,1)"},u=r.animate([{opacity:0,transform:e?"scale(.7)":"translateY(4px)",filter:"blur(2px)"},{opacity:1,transform:"none",filter:"blur(0px)"}],l),c=a.animate([{opacity:1,transform:"none",filter:"blur(0px)"},{opacity:0,transform:e?"scale(.7)":"translateY(-4px)",filter:"blur(2px)"}],l);return c.finished.then(()=>a.remove()).catch(()=>{}),()=>{u.cancel(),c.cancel(),a.remove()}},[t,e,n]),An("span",{ref:i,style:{gridArea:"1 / 1",display:"flex",alignItems:"center"},children:r})}function Vn(t){return"glyph"in t?ct("span",{style:{display:"contents"},dangerouslySetInnerHTML:{__html:ot[t.glyph]}}):ct("span",{className:"review-avatar"},t.user?.avatarUrl?ct("img",{src:t.user.avatarUrl,alt:"",referrerPolicy:"no-referrer"}):t.user?st(t.user.name):Vn({glyph:"person"}))}var _n=t=>ct(Dn,{...t,items:t.items.map(t=>({...t,icon:Vn(t.icon)}))});function On(t,e){t.replaceChildren();let n=lt(t);return ut(()=>n.render(_n(e))),{render:t=>n.render(_n(t)),unmount:()=>n.unmount()}}export{On as mountToolbar};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export interface Identity {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
verified: boolean;
|
|
5
|
+
avatarUrl?: string;
|
|
6
|
+
accentColor?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Anchor {
|
|
9
|
+
/** A manually moved indicator keeps its own position instead of joining a stack. */
|
|
10
|
+
unstacked?: boolean;
|
|
11
|
+
selector: string;
|
|
12
|
+
text: string;
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
pageX: number;
|
|
18
|
+
pageY: number;
|
|
19
|
+
viewportWidth: number;
|
|
20
|
+
source?: string;
|
|
21
|
+
/** Optional public target details; old anchors remain valid without them. */
|
|
22
|
+
context?: {
|
|
23
|
+
tag?: string;
|
|
24
|
+
role?: string;
|
|
25
|
+
label?: string;
|
|
26
|
+
nearby?: string;
|
|
27
|
+
classes?: string;
|
|
28
|
+
selectedText?: string;
|
|
29
|
+
styles?: string;
|
|
30
|
+
scope?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface Comment {
|
|
34
|
+
id: string;
|
|
35
|
+
body: string;
|
|
36
|
+
author: Identity;
|
|
37
|
+
createdAt: number;
|
|
38
|
+
editedAt: number | null;
|
|
39
|
+
reactions: Record<string, string[]>;
|
|
40
|
+
}
|
|
41
|
+
export interface Thread {
|
|
42
|
+
id: string;
|
|
43
|
+
page: string;
|
|
44
|
+
anchor: Anchor;
|
|
45
|
+
resolved: boolean;
|
|
46
|
+
resolvedBy: Identity | null;
|
|
47
|
+
createdAt: number;
|
|
48
|
+
updatedAt: number;
|
|
49
|
+
/** Reviewer-defined labels used to group related feedback. */
|
|
50
|
+
tags?: string[];
|
|
51
|
+
comments: Comment[];
|
|
52
|
+
}
|
|
53
|
+
export interface OnboardingOptions {
|
|
54
|
+
/** Temporary first-run setup inside the consuming site. Generated by the CLI. */
|
|
55
|
+
inProject?: boolean;
|
|
56
|
+
/** Suggested deployed sites shown for owner approval during first-run setup. */
|
|
57
|
+
sites?: string[];
|
|
58
|
+
invite?: string;
|
|
59
|
+
code?: string;
|
|
60
|
+
workspace?: string;
|
|
61
|
+
claimKey?: string;
|
|
62
|
+
site?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Keyboard shortcuts as a key with optional Ctrl, Alt, Shift, or Meta
|
|
66
|
+
* modifiers, e.g. "M" or "Shift+J". Set false to disable one. Escape and Tab
|
|
67
|
+
* are reserved.
|
|
68
|
+
*/
|
|
69
|
+
export interface KomoShortcuts {
|
|
70
|
+
/** Toggle comment mode. Defaults to "C". */
|
|
71
|
+
comment?: string | false;
|
|
72
|
+
/** Return to browsing. Defaults to "V". */
|
|
73
|
+
browse?: string | false;
|
|
74
|
+
/** Toggle Design mode. Defaults to "D". Has no effect when features.designMode is false. */
|
|
75
|
+
design?: string | false;
|
|
76
|
+
/** Restore a dismissed toolbar. Defaults to "Shift+K". */
|
|
77
|
+
restore?: string | false;
|
|
78
|
+
}
|
|
79
|
+
/** Optional review features. Each defaults to true; false removes it from the UI. */
|
|
80
|
+
export interface KomoFeatures {
|
|
81
|
+
/** Design mode inspector, style previews, and design proposals. */
|
|
82
|
+
designMode?: boolean;
|
|
83
|
+
/** Thread group tags: add them while writing a comment or on an open thread; shown in the sidebar. */
|
|
84
|
+
tags?: boolean;
|
|
85
|
+
}
|
|
86
|
+
export interface CommentsOptions {
|
|
87
|
+
/** Used by the hosted setup screen to reuse the sidebar account UI. */
|
|
88
|
+
onboarding?: OnboardingOptions;
|
|
89
|
+
/** Shared API URL. Never put a private credential here. */
|
|
90
|
+
endpoint: string;
|
|
91
|
+
/** Repository identifier, normally owner/repository. */
|
|
92
|
+
repo: string;
|
|
93
|
+
branch: string;
|
|
94
|
+
/** Public project key registered in the API configuration. */
|
|
95
|
+
project: string;
|
|
96
|
+
enabled?: boolean;
|
|
97
|
+
/** Hide the toolbar until controller.open() or the restore shortcut is used. Defaults to true. */
|
|
98
|
+
showToolbar?: boolean;
|
|
99
|
+
/** Override or disable the review keyboard shortcuts. */
|
|
100
|
+
shortcuts?: KomoShortcuts;
|
|
101
|
+
/** Turn optional features off, e.g. { designMode: false, tags: false }. */
|
|
102
|
+
features?: KomoFeatures;
|
|
103
|
+
/**
|
|
104
|
+
* Sidebar presentation when the review opens. "edge" (default) is a floating,
|
|
105
|
+
* draggable sidebar that parks off and peeks from the viewport edge while
|
|
106
|
+
* closed. "background" frames the site and shows the sidebar in the scaled
|
|
107
|
+
* review sheet. Users can switch Floating and Frame from the Account panel;
|
|
108
|
+
* that choice is remembered per project. Compact viewports always use a bottom
|
|
109
|
+
* Drawer, retaining the desktop preference when the viewport grows.
|
|
110
|
+
*/
|
|
111
|
+
sidebar?: "background" | "edge";
|
|
112
|
+
/** Hide the drawer away from the pointer. Defaults to true. */
|
|
113
|
+
autoHideDrawer?: boolean;
|
|
114
|
+
/** Center the default drawer position within this element. */
|
|
115
|
+
drawerContainer?: HTMLElement;
|
|
116
|
+
/** Mounted content root for Frame. Defaults to a sole body content element; otherwise Floating only. */
|
|
117
|
+
pageRoot?: HTMLElement;
|
|
118
|
+
/** Resolve an anchor to a repository-relative source path. */
|
|
119
|
+
source?: (element: Element) => string | undefined;
|
|
120
|
+
/** Defaults to GitHub's source viewer. */
|
|
121
|
+
sourceUrl?: (source: string, branch: string) => string;
|
|
122
|
+
/** Defaults to location.pathname; query strings are not stored. */
|
|
123
|
+
page?: () => string;
|
|
124
|
+
pollInterval?: number;
|
|
125
|
+
/** Full emoji JSON URL; fetched only when choosing another emoji. */
|
|
126
|
+
emojiDataSource?: string;
|
|
127
|
+
/** Opt-in trusted parent domain. Every sibling host can receive the session. */
|
|
128
|
+
sessionDomain?: string;
|
|
129
|
+
/** Canonical API identity for trusted gateways to the same service. Defaults to endpoint. */
|
|
130
|
+
sessionEndpoint?: string;
|
|
131
|
+
}
|
|
132
|
+
export interface CommentsController {
|
|
133
|
+
/** Open a new comment anchored to an element. */
|
|
134
|
+
comment(element: Element): void;
|
|
135
|
+
destroy(): void;
|
|
136
|
+
open(): void;
|
|
137
|
+
close(): void;
|
|
138
|
+
refresh(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
export interface AccountUsage {
|
|
141
|
+
hosted: boolean;
|
|
142
|
+
projects: {
|
|
143
|
+
used: number;
|
|
144
|
+
limit: number | null;
|
|
145
|
+
} | null;
|
|
146
|
+
comments: {
|
|
147
|
+
used: number;
|
|
148
|
+
limit: number | null;
|
|
149
|
+
};
|
|
150
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,116 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hazem3500/komo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Point, area, and threaded comments for website branches.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"server/index.ts",
|
|
10
|
+
"server/validation.ts",
|
|
11
|
+
"server/migrations",
|
|
12
|
+
"server/tsconfig.json",
|
|
13
|
+
"server/secrets.d.ts",
|
|
14
|
+
"src/types.ts",
|
|
15
|
+
"src/emoji.ts",
|
|
16
|
+
"README.md",
|
|
17
|
+
"NODE.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"src/page.ts",
|
|
20
|
+
"NOTICE.md",
|
|
21
|
+
"cli",
|
|
22
|
+
"server/workspaces.ts",
|
|
23
|
+
"server/setup-page.ts",
|
|
24
|
+
"server/setup-client.txt",
|
|
25
|
+
"server/project-management.ts",
|
|
26
|
+
"server/webhooks.ts",
|
|
27
|
+
"server/node-webhooks.ts",
|
|
28
|
+
"server/import-project.ts",
|
|
29
|
+
"server/digest.ts",
|
|
30
|
+
"server/project-sites.ts"
|
|
31
|
+
],
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./setup": {
|
|
40
|
+
"types": "./dist/setup.d.ts",
|
|
41
|
+
"import": "./dist/setup.js"
|
|
42
|
+
},
|
|
43
|
+
"./server": "./server/index.ts",
|
|
44
|
+
"./react": {
|
|
45
|
+
"types": "./dist/react.d.ts",
|
|
46
|
+
"import": "./dist/react.js"
|
|
47
|
+
},
|
|
48
|
+
"./node": {
|
|
49
|
+
"import": "./dist/node.mjs"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": [
|
|
53
|
+
"./dist/chunk-*.js",
|
|
54
|
+
"./dist/picker-*.js"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "node scripts/build.mjs && pnpm run build:setup && pnpm run build:node",
|
|
58
|
+
"prepack": "pnpm run build",
|
|
59
|
+
"typecheck": "wrangler types --config server/wrangler.jsonc server/worker-configuration.d.ts && tsc -p tsconfig.json --noEmit && tsc -p server/tsconfig.json",
|
|
60
|
+
"test": "pnpm run build:setup && vitest run --config vitest.config.ts",
|
|
61
|
+
"dev:api": "wrangler dev --config server/wrangler.jsonc --port 8788",
|
|
62
|
+
"migrate:local": "wrangler d1 migrations apply DB --local --config server/wrangler.jsonc",
|
|
63
|
+
"migrate:remote": "wrangler d1 migrations apply DB --remote --config server/wrangler.jsonc",
|
|
64
|
+
"deploy:api": "pnpm run build:setup && wrangler deploy --config server/wrangler.jsonc",
|
|
65
|
+
"build:setup": "esbuild src/setup-browser.ts --bundle --format=esm --minify --outfile=server/setup-client.txt",
|
|
66
|
+
"size": "node scripts/measure.mjs",
|
|
67
|
+
"bench": "node scripts/bench.mjs",
|
|
68
|
+
"demo:react-props": "node examples/react-props/build.mjs",
|
|
69
|
+
"test:package": "pnpm run build && node scripts/check-package.mjs",
|
|
70
|
+
"build:node": "esbuild server/node.ts --bundle --platform=node --format=esm --packages=external --loader:.txt=text --outfile=dist/node.mjs && mkdir -p dist/postgres && cp server/postgres/*.sql dist/postgres/"
|
|
71
|
+
},
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "git+https://github.com/hazem3500/komo.git",
|
|
75
|
+
"directory": "packages/komo"
|
|
76
|
+
},
|
|
77
|
+
"publishConfig": {
|
|
78
|
+
"access": "public"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/node": "^26",
|
|
82
|
+
"@types/pg": "^8.23.1",
|
|
83
|
+
"@types/react": "^19",
|
|
84
|
+
"@types/react-dom": "^19",
|
|
85
|
+
"@untitledui/icons": "0.0.22",
|
|
86
|
+
"emoji-picker-element": "^1.29.1",
|
|
87
|
+
"esbuild": "^0.25.0",
|
|
88
|
+
"jsdom": "^30.1.0",
|
|
89
|
+
"motion": "^12.43.0",
|
|
90
|
+
"react": "19.2.7",
|
|
91
|
+
"react-colorful": "^5.8.1",
|
|
92
|
+
"react-dom": "19.2.7",
|
|
93
|
+
"terser": "5.44.0",
|
|
94
|
+
"typescript": "^6.0.0",
|
|
95
|
+
"vitest": "4.1.11",
|
|
96
|
+
"wrangler": "4.136.3"
|
|
97
|
+
},
|
|
98
|
+
"dependencies": {
|
|
99
|
+
"emoji-regex": "^10.6.0",
|
|
100
|
+
"pg": "^8.23.0"
|
|
101
|
+
},
|
|
102
|
+
"bin": {
|
|
103
|
+
"komo": "./cli/index.mjs"
|
|
104
|
+
},
|
|
105
|
+
"engines": {
|
|
106
|
+
"node": ">=22.12"
|
|
107
|
+
},
|
|
108
|
+
"homepage": "https://github.com/hazem3500/komo#readme",
|
|
109
|
+
"bugs": {
|
|
110
|
+
"url": "https://github.com/hazem3500/komo/issues"
|
|
111
|
+
},
|
|
112
|
+
"peerDependencies": {
|
|
113
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
114
|
+
"react-dom": "^18.2.0 || ^19.0.0"
|
|
115
|
+
}
|
|
116
|
+
}
|
package/server/digest.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { check, string, pagePath, anchorValue } from "./validation";
|
|
2
|
+
import { digest } from "./digest";
|
|
3
|
+
import { isEmoji } from "../src/emoji";
|
|
4
|
+
|
|
5
|
+
export async function importProject(
|
|
6
|
+
env: Env,
|
|
7
|
+
project: string,
|
|
8
|
+
data: Record<string, unknown>
|
|
9
|
+
) {
|
|
10
|
+
const source = string(data.source, 100, "source project");
|
|
11
|
+
const kind = string(data.kind, 20, "table");
|
|
12
|
+
check(
|
|
13
|
+
Array.isArray(data.records) &&
|
|
14
|
+
data.records.length > 0 &&
|
|
15
|
+
data.records.length <= 50,
|
|
16
|
+
400,
|
|
17
|
+
"Import 1–50 records at a time."
|
|
18
|
+
);
|
|
19
|
+
const repo =
|
|
20
|
+
(
|
|
21
|
+
await env.DB.prepare("SELECT repo FROM workspaces WHERE id=?")
|
|
22
|
+
.bind(project)
|
|
23
|
+
.first<{ repo: string }>()
|
|
24
|
+
)?.repo ?? JSON.parse(env.PROJECTS)[project]?.repo;
|
|
25
|
+
check(repo, 404, "Unknown destination project.");
|
|
26
|
+
const id = async (value: unknown, identity = false) =>
|
|
27
|
+
(identity ? "guest:import:" : "import:") +
|
|
28
|
+
(await digest(`${project}\n${source}\n${string(value, 200, "source ID")}`));
|
|
29
|
+
const time = (value: unknown) => {
|
|
30
|
+
check(
|
|
31
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 0,
|
|
32
|
+
400,
|
|
33
|
+
"Invalid timestamp."
|
|
34
|
+
);
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
37
|
+
const statements: D1PreparedStatement[] = [];
|
|
38
|
+
for (const value of data.records) {
|
|
39
|
+
check(
|
|
40
|
+
value && typeof value === "object" && !Array.isArray(value),
|
|
41
|
+
400,
|
|
42
|
+
"Invalid record."
|
|
43
|
+
);
|
|
44
|
+
const row = value as Record<string, unknown>;
|
|
45
|
+
if (kind === "users") {
|
|
46
|
+
const accent = row.accent_color;
|
|
47
|
+
check(
|
|
48
|
+
accent === undefined ||
|
|
49
|
+
accent === null ||
|
|
50
|
+
(typeof accent === "string" && /^#[a-f0-9]{6}$/i.test(accent)),
|
|
51
|
+
400,
|
|
52
|
+
"Invalid accent color."
|
|
53
|
+
);
|
|
54
|
+
const photo = row.avatar_url;
|
|
55
|
+
check(
|
|
56
|
+
photo === undefined ||
|
|
57
|
+
photo === null ||
|
|
58
|
+
(typeof photo === "string" &&
|
|
59
|
+
photo.length <= 12000 &&
|
|
60
|
+
(/^(https:\/\/|data:image\/jpeg;base64,)/.test(photo) ||
|
|
61
|
+
photo === "")),
|
|
62
|
+
400,
|
|
63
|
+
"Invalid avatar."
|
|
64
|
+
);
|
|
65
|
+
const authorId = await id(row.id, true);
|
|
66
|
+
statements.push(
|
|
67
|
+
env.DB.prepare(
|
|
68
|
+
"INSERT OR IGNORE INTO users(id,name,verified,avatar_url,accent_color) VALUES(?,?,0,?,?) RETURNING id"
|
|
69
|
+
).bind(
|
|
70
|
+
authorId,
|
|
71
|
+
string(row.name, 60, "author name"),
|
|
72
|
+
photo ?? null,
|
|
73
|
+
accent ?? null
|
|
74
|
+
),
|
|
75
|
+
env.DB.prepare(
|
|
76
|
+
"INSERT OR IGNORE INTO project_members(project,user_id) VALUES(?,?)"
|
|
77
|
+
).bind(project, authorId)
|
|
78
|
+
);
|
|
79
|
+
} else if (kind === "threads") {
|
|
80
|
+
let rawAnchor = row.anchor;
|
|
81
|
+
if (typeof rawAnchor === "string") {
|
|
82
|
+
try {
|
|
83
|
+
rawAnchor = JSON.parse(rawAnchor);
|
|
84
|
+
} catch {
|
|
85
|
+
check(false, 400, "Invalid imported anchor.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const anchor = anchorValue(rawAnchor);
|
|
89
|
+
check(
|
|
90
|
+
row.resolved === 0 || row.resolved === 1,
|
|
91
|
+
400,
|
|
92
|
+
"Invalid resolved state."
|
|
93
|
+
);
|
|
94
|
+
statements.push(
|
|
95
|
+
env.DB.prepare(
|
|
96
|
+
"INSERT OR IGNORE INTO threads(id,project,repo,branch,page,anchor,resolved,resolved_by,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?) RETURNING id"
|
|
97
|
+
).bind(
|
|
98
|
+
await id(row.id),
|
|
99
|
+
project,
|
|
100
|
+
repo,
|
|
101
|
+
string(row.branch, 250, "branch"),
|
|
102
|
+
pagePath(row.page),
|
|
103
|
+
JSON.stringify(anchor),
|
|
104
|
+
row.resolved,
|
|
105
|
+
row.resolved_by ? await id(row.resolved_by, true) : null,
|
|
106
|
+
time(row.created_at),
|
|
107
|
+
time(row.updated_at)
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
} else if (kind === "comments") {
|
|
111
|
+
const thread = await id(row.thread_id),
|
|
112
|
+
author = await id(row.user_id, true);
|
|
113
|
+
check(
|
|
114
|
+
await env.DB.prepare("SELECT id FROM threads WHERE id=? AND project=?")
|
|
115
|
+
.bind(thread, project)
|
|
116
|
+
.first(),
|
|
117
|
+
400,
|
|
118
|
+
"Import the parent thread first."
|
|
119
|
+
);
|
|
120
|
+
statements.push(
|
|
121
|
+
env.DB.prepare(
|
|
122
|
+
"INSERT OR IGNORE INTO comments(id,thread_id,user_id,body,created_at,edited_at) VALUES(?,?,?,?,?,?) RETURNING id"
|
|
123
|
+
).bind(
|
|
124
|
+
await id(row.id),
|
|
125
|
+
thread,
|
|
126
|
+
author,
|
|
127
|
+
string(row.body, 4000, "comment"),
|
|
128
|
+
time(row.created_at),
|
|
129
|
+
row.edited_at == null ? null : time(row.edited_at)
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
} else if (kind === "reactions") {
|
|
133
|
+
const comment = await id(row.comment_id);
|
|
134
|
+
check(
|
|
135
|
+
await env.DB.prepare(
|
|
136
|
+
"SELECT c.id FROM comments c JOIN threads t ON t.id=c.thread_id WHERE c.id=? AND t.project=?"
|
|
137
|
+
)
|
|
138
|
+
.bind(comment, project)
|
|
139
|
+
.first(),
|
|
140
|
+
400,
|
|
141
|
+
"Import the comment first."
|
|
142
|
+
);
|
|
143
|
+
check(
|
|
144
|
+
typeof row.emoji === "string" && isEmoji(row.emoji),
|
|
145
|
+
400,
|
|
146
|
+
"Invalid reaction."
|
|
147
|
+
);
|
|
148
|
+
statements.push(
|
|
149
|
+
env.DB.prepare(
|
|
150
|
+
"INSERT OR IGNORE INTO reactions(comment_id,user_id,emoji) VALUES(?,?,?) RETURNING comment_id"
|
|
151
|
+
).bind(comment, await id(row.user_id, true), row.emoji)
|
|
152
|
+
);
|
|
153
|
+
} else check(false, 400, "Unknown import table.");
|
|
154
|
+
}
|
|
155
|
+
const results = await env.DB.batch(statements);
|
|
156
|
+
return Response.json({
|
|
157
|
+
ok: true,
|
|
158
|
+
imported: results.reduce(
|
|
159
|
+
(total, result, index) =>
|
|
160
|
+
total + (kind === "users" && index % 2 ? 0 : result.results.length),
|
|
161
|
+
0
|
|
162
|
+
),
|
|
163
|
+
});
|
|
164
|
+
}
|