@alwith/module-chat 0.1.3 → 0.1.6
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 +2 -2
- package/README.md +11 -1
- package/dist/approval/permission-view.js +1 -1
- package/dist/codex/markdown.css +1 -1
- package/dist/codex/styles.css +23 -23
- package/dist/composer/model-select.js +1 -1
- package/dist/lib/update-scheduler.d.ts +10 -0
- package/dist/lib/update-scheduler.js +1 -0
- package/package.json +6 -1
package/LICENSE
CHANGED
|
@@ -11,8 +11,8 @@ reduces rights granted by those licenses or by mandatory applicable law.
|
|
|
11
11
|
2. Permission to develop and distribute applications
|
|
12
12
|
Subject to this license, ALwith grants you a worldwide, non-exclusive, royalty-free
|
|
13
13
|
copyright license to use, reproduce, integrate and modify the supplied Software
|
|
14
|
-
to develop, test, host and operate your own applications,
|
|
15
|
-
|
|
14
|
+
to develop, test, host and operate your own applications, for personal,
|
|
15
|
+
internal business or commercial use. You may brand,
|
|
16
16
|
sell and distribute those applications, including the Software embedded in them,
|
|
17
17
|
to your customers and end users. No per-application registration, approval or
|
|
18
18
|
royalty is required by this software license.
|
package/README.md
CHANGED
|
@@ -21,6 +21,16 @@ Run bun install at the workspace root, then bun run --cwd packages/chat typechec
|
|
|
21
21
|
|
|
22
22
|
ACP session folding stays in @alwith/api. This package owns chat presentation and interaction, not Agent processes or a second session database.
|
|
23
23
|
|
|
24
|
+
`update-scheduler` exports the optional, framework-independent `ChatUpdateScheduler<Snapshot>`.
|
|
25
|
+
The host folds every protocol update immediately, then calls `enqueue(sessionId, snapshot)`
|
|
26
|
+
with the complete immutable snapshot. One 16ms window publishes the latest snapshot for each
|
|
27
|
+
pending session in a single callback; incoming chunks never reset the deadline. Call `flush()`
|
|
28
|
+
after enqueueing a lifecycle/configuration change or finishing history replay, `remove(sessionId)`
|
|
29
|
+
when dropping a session, and `cancel()` on disconnect/disposal. The callback should publish the
|
|
30
|
+
batch to the UI store once. This schedules store notifications, not protocol processing or
|
|
31
|
+
Markdown parsing. It retains only pending snapshot references, has no React/DOM/ACP dependency,
|
|
32
|
+
and does not change existing components or enable batching unless the host opts in.
|
|
33
|
+
|
|
24
34
|
`search` provides Desktop's complete find bar, DOM matching, highlights and navigation. The host supplies its viewport, labels and native-menu subscription; virtualized callers also supply source matches and an async turn-reveal callback. Key the component by session identity to discard the previous search on a session switch. Highlight ownership is per mounted search, and cancelled navigation cannot repaint after close. Source projection stays with the application's message model; the search component never reads a private store.
|
|
25
35
|
|
|
26
36
|
`search-source` shares Unicode-aware literal matching and original UTF-16 offsets with DOM matching. This does **not** make raw tool fields, Markdown source or folded content equivalent to rendered text: the current per-turn occurrence index still requires a complete source-to-rendered mapping. Do not treat the current search counter as proof that every indexed match can be revealed. Automatic expansion of folded matches has not been agreed.
|
|
@@ -47,7 +57,7 @@ The composer form comes from `@alwith/module-chat/composer`, with the controller
|
|
|
47
57
|
|
|
48
58
|
See [LICENSE](LICENSE) and [third-party notices](THIRD_PARTY_NOTICES.md).
|
|
49
59
|
ALwith-owned code is proprietary, not open source. You may build, customize, host,
|
|
50
|
-
charge for and distribute your own
|
|
60
|
+
charge for and distribute your own applications under the included
|
|
51
61
|
license, without per-application registration. This does not grant hosted-service
|
|
52
62
|
access or rights to separately distributed Rust libraries or Runtime binaries.
|
|
53
63
|
The npm artifact includes compiled/minified JS, declarations and necessary assets,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as r,jsxs as u}from"react/jsx-runtime";import{autoUpdate as
|
|
1
|
+
import{jsx as r,jsxs as u}from"react/jsx-runtime";import{autoUpdate as S}from"@floating-ui/dom";import{CheckCheckIcon as z,CheckIcon as _,ShieldXIcon as M,XIcon as O}from"lucide-react";import{useCallback as U,useEffect as v,useId as W,useLayoutEffect as I,useRef as X,useState as p}from"react";import{createPortal as B}from"react-dom";import{cn as N}from"cn";import{sortPermissionOptions as H}from"./permission-options.js";var L={allow_once:{icon:_},allow_always:{icon:z},reject_once:{icon:O},reject_always:{icon:M}};function R({requestId:f,options:j,title:D,children:E,total:w=1,alwaysLabel:A="always",autoFocus:y="always",anchorRef:n,onRespond:i}){let x=W(),[l,b]=p(0),[s,g]=p(null),[a,C]=p(null),k=X(null),c=H(j);v(()=>{b(0)},[f]),v(()=>{let e=document.activeElement;if(y==="always"||e===null||e===document.body)k.current?.focus()},[f,a,y]);let d=U(()=>{let e=n?.current;if(!e)return;let t=e.getBoundingClientRect();if(t.width===0){let o=Math.min(640,window.innerWidth*0.9);g({left:(window.innerWidth-o)/2,width:o,bottom:24});return}g({left:t.left,width:t.width,bottom:window.innerHeight-t.bottom+20})},[n]);if(I(()=>{if(!n)return;let e=0,t=()=>{if(n.current)d();else e=requestAnimationFrame(t)};return t(),()=>cancelAnimationFrame(e)},[f,n,d]),I(()=>{let e=n?.current;if(!e||!a)return;return S(e,a,d,{animationFrame:!0})},[n,a,d]),n&&!s)return null;let h=u("div",{ref:C,"data-overlay":"permission-request",className:N("bg-card border-border overflow-hidden rounded-lg border",n&&"fixed z-50"),style:n&&s?{left:s.left,width:s.width,bottom:s.bottom}:void 0,children:[u("div",{className:"border-border border-b px-3 py-2",children:[u("div",{className:"flex items-center justify-between gap-2",children:[r("div",{id:x,className:"text-foreground text-sm font-medium",children:D}),w>1&&r("span",{className:"text-muted-foreground shrink-0 text-xs",children:`1 / ${w}`})]}),E]}),r("div",{role:"listbox","aria-labelledby":x,tabIndex:0,ref:k,onKeyDown:(e)=>{let t=c.length;if(t===0&&e.key!=="Escape")return;if(e.key==="ArrowDown"||e.key==="ArrowUp")e.preventDefault(),b((o)=>e.key==="ArrowDown"?(o+1)%t:(o-1+t)%t);else if(e.key==="Enter")e.preventDefault(),i(c[l].optionId);else if(e.key==="Escape")e.preventDefault(),i(null);else if(/^[1-9]$/.test(e.key)&&Number(e.key)<=t)e.preventDefault(),i(c[Number(e.key)-1].optionId)},className:"outline-none",children:c.map((e,t)=>{let o=e.kind??"allow_once",P=L[o]?.icon??_,F=o==="allow_always"||o==="reject_always",K=o==="reject_once"||o==="reject_always";return u("div",{role:"option",tabIndex:-1,"aria-selected":t===l,title:e.description,onClick:()=>void i(e.optionId),onKeyDown:(m)=>{if(m.key==="Enter"||m.key===" ")m.preventDefault(),m.stopPropagation(),i(e.optionId)},className:N("flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm select-none",t===l?"bg-ring text-accent-foreground":"text-foreground",K&&t!==l&&"text-muted-foreground"),children:[r("span",{className:"min-w-3 text-xs opacity-70",children:t+1}),r(P,{className:"size-3.5 shrink-0 opacity-80"}),r("span",{className:"min-w-0 flex-1 truncate",children:e.name}),F&&r("span",{className:"bg-muted text-muted-foreground rounded px-1.5 py-0.5 text-[10px] tracking-wide uppercase",children:A})]},e.optionId)})})]});return n?B(h,document.body):h}export{R as PermissionView};
|
package/dist/codex/markdown.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Codex
|
|
1
|
+
/* Codex streaming word-by-word fade-in (_fadeIn_): new word opacity 0->1, 150ms cubic-bezier(.37,.55,.86,.88). */
|
|
2
2
|
.codex-markdown-fade {
|
|
3
3
|
opacity: 0;
|
|
4
4
|
animation: codex-markdown-fade-in 0.15s cubic-bezier(0.37, 0.55, 0.86, 0.88) forwards;
|
package/dist/codex/styles.css
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
@import "./markdown.css";
|
|
2
2
|
|
|
3
3
|
/* ── Codex desktop chat stream ──
|
|
4
|
-
*
|
|
5
|
-
* 16px conversation item
|
|
4
|
+
* From the ChatGPT.app Codex conversation components: host chat font size, 12px turn virtual spacing,
|
|
5
|
+
* 16px conversation item spacing, 4px grouped activity spacing, 14/16px icon, 77% user bubble, 12x8px bubble padding.
|
|
6
6
|
*/
|
|
7
7
|
[data-stream-style="compact"],
|
|
8
8
|
[data-stream-style="codexUI"] {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
--codex-checkbox-background: #ffffffb3;
|
|
23
23
|
--codex-checkbox-foreground: var(--codex-foreground);
|
|
24
24
|
--codex-link-foreground: #339cff;
|
|
25
|
-
/*
|
|
26
|
-
|
|
25
|
+
/* Color chain copied from Codex app.css: description-foreground(electron) = fg 70% srgb;
|
|
26
|
+
* conversation-header 30% / body 60% / summary-trailing 40% / reasoning descendants 50% are all oklab;
|
|
27
27
|
* summary-leading = description-foreground 90% oklab;text-secondary = fg 65% srgb。 */
|
|
28
28
|
--codex-description-foreground: color-mix(in srgb, var(--codex-foreground) 70%, transparent);
|
|
29
29
|
--codex-text-secondary: color-mix(in srgb, var(--codex-foreground) 65%, transparent);
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
--codex-border: rgb(255 255 255 / 16%);
|
|
46
46
|
--codex-hover: rgb(255 255 255 / 8%);
|
|
47
47
|
--codex-ring: rgb(255 255 255 / 36%);
|
|
48
|
-
/*
|
|
48
|
+
/* Dark 8% -- Codex original `--user-message-background-color: var(--alpha-08)`; light is 5% (alpha-05) */
|
|
49
49
|
--codex-user-background: rgb(255 255 255 / 8%);
|
|
50
50
|
--codex-checkbox-background: #212121f5;
|
|
51
51
|
--codex-link-foreground: #99ceff;
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
height: 20px;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
/*
|
|
156
|
+
/* While an answer is in progress, the scroll-to-bottom button becomes 3 bouncing dots (Codex _waveDot_: 1s ease-in-out infinite, staggered by 0.1s). */
|
|
157
157
|
.codex-scroll-to-bottom-dots {
|
|
158
158
|
display: flex;
|
|
159
159
|
align-items: center;
|
|
@@ -626,8 +626,8 @@
|
|
|
626
626
|
text-decoration: underline;
|
|
627
627
|
}
|
|
628
628
|
|
|
629
|
-
/* Codex `.loading-shimmer-pure-text`
|
|
630
|
-
|
|
629
|
+
/* Codex `.loading-shimmer-pure-text` copied value by value: base color = description-foreground 55% (dark 45%),
|
|
630
|
+
* highlight light #ffffffbf / dark #0009, 50%x200% background band, 2s steps(48, end) infinite loop. */
|
|
631
631
|
.codex-activity-running {
|
|
632
632
|
--codex-shimmer-base: color-mix(in srgb, var(--codex-description-foreground) 55%, transparent);
|
|
633
633
|
--codex-shimmer-contrast: #ffffffbf;
|
|
@@ -653,12 +653,12 @@
|
|
|
653
653
|
--codex-shimmer-contrast: #0009;
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
-
/* Codex
|
|
656
|
+
/* Codex applies `[&_.loading-shimmer-pure-text]:align-top` at the use site to fix the inline-block baseline. */
|
|
657
657
|
.codex-activity-label .codex-activity-running {
|
|
658
658
|
vertical-align: top;
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
-
/* Codex reasoning
|
|
661
|
+
/* Codex reasoning titles use conversation-header (30% fg); on hover the header brightens as a whole. */
|
|
662
662
|
.codex-reasoning-label {
|
|
663
663
|
color: var(--codex-conversation-header);
|
|
664
664
|
}
|
|
@@ -668,8 +668,8 @@
|
|
|
668
668
|
color: inherit;
|
|
669
669
|
}
|
|
670
670
|
|
|
671
|
-
/* Codex `.vertical-scroll-fade-mask
|
|
672
|
-
|
|
671
|
+
/* Codex `.vertical-scroll-fade-mask`: scroll-position-driven top/bottom fade (scroll-driven animation).
|
|
672
|
+
* In engines without animation-timeline both edge fades stay 0 and the mask degrades to fully visible -- same fallback as Codex. */
|
|
673
673
|
@property --codex-top-fade {
|
|
674
674
|
syntax: "<length-percentage>";
|
|
675
675
|
inherits: false;
|
|
@@ -695,8 +695,8 @@
|
|
|
695
695
|
);
|
|
696
696
|
}
|
|
697
697
|
|
|
698
|
-
/*
|
|
699
|
-
|
|
698
|
+
/* The animation must be wrapped in @supports: an engine without scroll timeline treats it as a zero-duration ordinary animation,
|
|
699
|
+
* and fill-mode both freezes it on the to frame -- the top fade stays on permanently. Wrapping it is what gives a true "degrade to fully visible". */
|
|
700
700
|
@supports (animation-timeline: scroll(self y)) {
|
|
701
701
|
.codex-scroll-fade-mask,
|
|
702
702
|
.codex-activity-group-body,
|
|
@@ -723,7 +723,7 @@
|
|
|
723
723
|
}
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
-
/*
|
|
726
|
+
/* Fade distance of each scroll box, matching Codex's --edge-fade-distance everywhere. */
|
|
727
727
|
.codex-activity-group-body {
|
|
728
728
|
--codex-edge-fade-distance: 1.5rem;
|
|
729
729
|
}
|
|
@@ -763,8 +763,8 @@
|
|
|
763
763
|
padding: 8px 0 4px;
|
|
764
764
|
}
|
|
765
765
|
|
|
766
|
-
/* Codex
|
|
767
|
-
|
|
766
|
+
/* Codex terminal block (bml/_ml): rounded-lg bordered card, command area with $ prefix + line-clamp-2,
|
|
767
|
+
* output area col-reverse to stick to the bottom automatically, footer right-aligned Success / Exit code / Stopped. */
|
|
768
768
|
.codex-command-card {
|
|
769
769
|
display: flex;
|
|
770
770
|
overflow: hidden;
|
|
@@ -947,8 +947,8 @@
|
|
|
947
947
|
align-items: flex-end;
|
|
948
948
|
}
|
|
949
949
|
|
|
950
|
-
/* Codex
|
|
951
|
-
|
|
950
|
+
/* Codex original (app.asar): the whole user message is `flex flex-col items-end gap-2` --
|
|
951
|
+
a uniform 8px between bubble / attachments / action row. */
|
|
952
952
|
.codex-user-message-group {
|
|
953
953
|
display: flex;
|
|
954
954
|
width: 100%;
|
|
@@ -972,8 +972,8 @@
|
|
|
972
972
|
gap: 8px;
|
|
973
973
|
}
|
|
974
974
|
|
|
975
|
-
/*
|
|
976
|
-
|
|
975
|
+
/* Fixed-size thumbnail + crop -- aligned with Codex: small squares in the message, the original image isn't spread out (2026-08-10).
|
|
976
|
+
The enlarged layer that opens uses this repo's full-form overlay (ImageLightbox), not Codex's own lightbox. */
|
|
977
977
|
.codex-user-image {
|
|
978
978
|
display: block;
|
|
979
979
|
overflow: hidden;
|
|
@@ -994,8 +994,8 @@
|
|
|
994
994
|
margin-bottom: 6px;
|
|
995
995
|
}
|
|
996
996
|
|
|
997
|
-
/* CDP
|
|
998
|
-
|
|
997
|
+
/* Codex runtime measured via CDP: action row `flex items-center gap-0.5` (2px), buttons 26x26 radius 10,
|
|
998
|
+
icon 16 -- the latter three match .codex-message-action; gap was written as 4px, now aligned. Row height aligned to the 26 button box. */
|
|
999
999
|
.codex-user-actions {
|
|
1000
1000
|
display: flex;
|
|
1001
1001
|
height: 26px;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as a,Fragment as
|
|
1
|
+
import{jsx as e,jsxs as a,Fragment as u}from"react/jsx-runtime";import{ArrowUpRight as se,ChevronUp as T,Loader2Icon as A}from"lucide-react";import{Fragment as te,useState as F}from"react";import{cn as g}from"cn";import{MenuRadioSelect as d}from"./menu-radio.js";import{EffortSlider as le}from"./effort-slider.js";import{Button as S}from"../ui/button.js";import{Switch as re}from"../ui/switch.js";import{Tooltip as I,TooltipContent as R,TooltipTrigger as U}from"../ui/tooltip.js";import{DropdownMenu as B,DropdownMenuContent as E,DropdownMenuGroup as y,DropdownMenuItem as z,DropdownMenuLabel as L,DropdownMenuSeparator as c,DropdownMenuSub as W,DropdownMenuSubContent as q,DropdownMenuSubTrigger as H,DropdownMenuTrigger as J}from"../ui/dropdown-menu.js";function Ne({value:h,onChange:K,models:p,modelGroups:b,providerMenu:r,header:w,className:_,condensed:C,stripLabelPrefix:D,showDescription:Q,showModelList:X=!0,loading:Y,switching:M,isLoading:Z,effortConfig:t,booleanOptions:k,onBooleanChange:G,selectOptions:P=[],t:l,...x}){let[ee,ne]=F(!1),[ae,ie]=F(!1),V=(n)=>D&&n.startsWith(D)?n.slice(D.length):n;if(Y)return e("div",{className:"inline-flex min-w-0 items-center gap-1.5",children:a(S,{variant:"ghost",disabled:!0,className:g("h-7 min-w-0 shrink gap-1.5 rounded-md px-2 text-xs font-normal",_),children:[e(A,{className:"size-3 animate-spin"}),e("span",{className:"text-muted-foreground truncate",children:l("model.loadingModels")})]})});return a("div",{className:"inline-flex min-w-0 items-center gap-1.5",children:[r&&a(B,{open:ee,onOpenChange:ne,children:[a(I,{children:[e(U,{render:e(J,{render:a(S,{"data-testid":"provider-trigger",variant:"ghost",disabled:x.disabled,className:g("h-7 shrink-0 gap-1 rounded-md px-1.5","hover:bg-accent"),children:[r.icon,e(T,{className:"size-2.5 shrink-0 opacity-60"})]})})}),e(R,{children:l("model.switchProvider")})]}),a(E,{side:"top",align:"end",sideOffset:4,className:"z-100 min-w-[10rem]",children:[r.groups.map((n,s)=>a(te,{children:[s>0&&n.length>0&&e(c,{}),e(d,{value:r.value,onValueChange:r.onChange,options:n})]},s)),r.moreAction&&a(u,{children:[e(c,{}),a(z,{onClick:r.moreAction,className:"whitespace-nowrap",children:[e("span",{className:"min-w-0 flex-1 truncate",children:l("model.moreModels")}),e(se,{className:"size-4"})]})]})]})]}),a(B,{open:ae,onOpenChange:ie,children:[a(I,{children:[e(U,{render:e(J,{render:a(S,{"data-testid":"model-trigger","aria-busy":M,variant:"ghost",className:g("h-7 gap-1.5 rounded-md px-2 text-xs font-normal","hover:bg-accent","max-w-full min-w-0 shrink",C&&"h-7 gap-1 px-1.5 text-[11px]",_),...x,children:[e("span",{className:g("min-w-0 flex-1 truncate text-start",C&&"text-[11px]"),children:V(h.label)||l("model.selectModel")}),t&&a("span",{className:"text-muted-foreground grid shrink-0 justify-items-center",children:[e("span",{className:"invisible col-start-1 row-start-1",children:t.sizer??t.levels.reduce((n,s)=>s.name.length>n.length?s.name:n,"")}),e("span",{className:"col-start-1 row-start-1",children:t.levels.find((n)=>n.value===t.value)?.name??t.value})]}),M?e(A,{"data-testid":"model-switch-loading",className:"size-3 shrink-0 animate-spin"}):e(T,{className:g("size-3 shrink-0",C&&"size-2.5")})]})})}),e(R,{children:l("model.switchModel")})]}),a(E,{side:"top",align:"end",sideOffset:4,className:"z-100 w-auto max-w-[min(24rem,calc(100vw-1rem))] min-w-[14rem]",children:[w&&a(u,{children:[a("div",{className:"flex items-center gap-2 px-2 py-2",children:[w.icon,e("span",{className:"min-w-0 flex-1 truncate text-sm font-medium",children:w.name}),w.action]}),e(c,{})]}),a(y,{children:[X&&(Z?e(z,{disabled:!0,className:"text-muted-foreground",children:l("model.loadingModels")}):p.length===0?e(z,{disabled:!0,className:"text-muted-foreground",children:l("model.noModelsAvailable")}):(()=>{let n=p.filter((i)=>!i.more),s=p.filter((i)=>i.more),O=n.length>0&&s.length>0,N=(i,m)=>({value:i.api_id,label:V(i.label),description:m?.noDescription?void 0:Q?i.description:void 0}),f=(i)=>{let m=p.find((v)=>v.api_id===i);if(m)K(m)};return a(u,{children:[b?b.map((i)=>{let m=i.models.filter((o)=>!o.more),v=i.models.filter((o)=>o.more),j=m.length>0&&v.length>0;return a(y,{children:[e(L,{className:"text-muted-foreground text-xs font-normal",children:i.name}),e(d,{value:h.api_id,onValueChange:f,itemClassName:"font-medium whitespace-nowrap",options:(j?m:i.models).map((o)=>N(o))}),j&&a(W,{children:[e(H,{className:"font-medium whitespace-nowrap",children:l("model.moreModels")}),e(q,{children:e(d,{value:h.api_id,onValueChange:f,itemClassName:"font-medium whitespace-nowrap",options:v.map((o)=>N(o,{noDescription:!0}))})})]})]},i.id)}):e(d,{value:h.api_id,onValueChange:f,itemClassName:"font-medium whitespace-nowrap",options:(O?n:p).map((i)=>N(i))}),!b&&O&&a(W,{children:[e(H,{className:"font-medium whitespace-nowrap",children:l("model.moreModels")}),e(q,{children:e(d,{value:h.api_id,onValueChange:f,itemClassName:"font-medium whitespace-nowrap",options:s.map((i)=>N(i,{noDescription:!0}))})})]})]})})()),t&&a(u,{children:[e(c,{}),e(le,{...t,disabled:x.disabled})]}),P.map((n)=>a(y,{children:[e(c,{}),e(L,{children:n.name}),e(d,{value:n.value,onValueChange:n.onChange,options:n.options,itemClassName:"whitespace-nowrap"})]},n.id)),k&&k.length>0&&a(u,{children:[!t&&e(c,{}),k.map((n)=>a("div",{className:"flex items-center justify-between gap-3 px-2 py-2.5",children:[e("span",{className:"text-sm font-medium",children:n.name}),e(re,{"aria-label":n.name,disabled:x.disabled,checked:n.currentValue,onCheckedChange:(s)=>G?.(n.id,s)})]},n.id))]})]})]})]})]})}export{Ne as ModelSelect};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class ChatUpdateScheduler<Snapshot> {
|
|
2
|
+
private readonly publish;
|
|
3
|
+
private readonly pending;
|
|
4
|
+
private timer;
|
|
5
|
+
constructor(publish: (snapshots: Readonly<Record<string, Snapshot>>) => void);
|
|
6
|
+
enqueue(sessionId: string, snapshot: Snapshot): void;
|
|
7
|
+
flush(): void;
|
|
8
|
+
remove(sessionId: string): void;
|
|
9
|
+
cancel(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class t{publish;pending=new Map;timer=null;constructor(i){this.publish=i}enqueue(i,e){this.pending.set(i,e),this.timer??=setTimeout(()=>this.flush(),16)}flush(){if(this.timer!==null)clearTimeout(this.timer);if(this.timer=null,this.pending.size===0)return;let i=Object.fromEntries(this.pending);this.pending.clear(),this.publish(i)}remove(i){if(this.pending.delete(i),this.pending.size===0)this.cancel()}cancel(){if(this.timer!==null)clearTimeout(this.timer);this.timer=null,this.pending.clear()}}export{t as ChatUpdateScheduler};
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwith/module-chat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
|
+
"./update-scheduler": {
|
|
8
|
+
"types": "./dist/lib/update-scheduler.d.ts",
|
|
9
|
+
"import": "./dist/lib/update-scheduler.js",
|
|
10
|
+
"default": "./dist/lib/update-scheduler.js"
|
|
11
|
+
},
|
|
7
12
|
"./markdown-parser.worker": {
|
|
8
13
|
"types": "./dist/lib/markdown-parser.worker.d.ts",
|
|
9
14
|
"import": "./dist/lib/markdown-parser.worker.js",
|