@deepseek-ai/dsh-client-ui-chat 0.1.7-alpha.1 → 0.1.7-alpha.2
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/README.i18n.yaml +2 -2
- package/README.md +24 -3
- package/README.zh.md +24 -3
- package/lib/client.js +481 -177
- package/lib/types/client/chat/use-chat-reading.d.ts +3 -2
- package/lib/types/client/chat/use-chat-viewport.d.ts +5 -8
- package/lib/types/client/chat/use-process-scroll.d.ts +23 -0
- package/lib/types/client/chat/use-scroll-follow.d.ts +105 -0
- package/package.json +39 -39
package/lib/client.js
CHANGED
|
@@ -177,7 +177,12 @@ window.__ModuleLoader__.load({
|
|
|
177
177
|
return {
|
|
178
178
|
code: {
|
|
179
179
|
copyLabel: t("copy"),
|
|
180
|
-
copiedLabel: t("copied")
|
|
180
|
+
copiedLabel: t("copied"),
|
|
181
|
+
toolbarLabels: {
|
|
182
|
+
codeLabel: t("codeBlock.title"),
|
|
183
|
+
wrapLabel: t("codeBlock.wrap"),
|
|
184
|
+
unwrapLabel: t("codeBlock.unwrap")
|
|
185
|
+
}
|
|
181
186
|
},
|
|
182
187
|
footnotes: t("markdown.footnotes")
|
|
183
188
|
};
|
|
@@ -1579,7 +1584,7 @@ window.__ModuleLoader__.load({
|
|
|
1579
1584
|
}
|
|
1580
1585
|
//#endregion
|
|
1581
1586
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/ChatView.module.css.mjs
|
|
1582
|
-
const css$13 = ".EvIC1a_root{flex-direction:column;flex:auto;min-height:0;display:flex;position:relative}.EvIC1a_scroll{min-height:0;padding:16px calc(var(--dsh-composer-side-clearance) + 16px);flex:auto;overflow-y:auto;container-type:inline-size}.EvIC1a_root[data-chat-following-tail] .EvIC1a_scroll,[data-conversation-scroll]:has(.EvIC1a_root[data-chat-following-tail]){overflow-anchor:none}[data-conversation-scroll] .EvIC1a_root{flex:none;height:auto;min-height:auto}[data-conversation-scroll] .EvIC1a_scroll{flex:none;min-height:auto;overflow:visible}.EvIC1a_column{max-width:var(--dsh-chat-content-width);flex-direction:column;width:100%;margin:0 auto;display:flex}.EvIC1a_column>:not([hidden]):not(.EvIC1a_flowItem:empty)~:not([hidden]):not(.EvIC1a_flowItem:empty){margin-top:var(--dsh-chat-flow-gap,16px)}.EvIC1a_flowItem{min-width:0}.EvIC1a_flowItem[data-turn-process-answer]{--dsh-chat-flow-gap:8px}.EvIC1a_flowItem:empty{height:0}.EvIC1a_callRow{border-radius:6px}.EvIC1a_hint{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}.EvIC1a_openError{color:var(--dsw-alias-state-error-primary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}.EvIC1a_older{justify-content:center;display:flex}.EvIC1a_older button{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover-solid);cursor:pointer;border:none;border-radius:14px;padding:4px 12px;font-size:12px}.EvIC1a_older button:disabled{cursor:default;opacity:.6}.EvIC1a_toBottomSlot{z-index:8;height:0;padding-right:max(
|
|
1587
|
+
const css$13 = ".EvIC1a_frame{flex-direction:column;flex:auto;min-height:0;display:flex;position:relative;container-type:inline-size}.EvIC1a_root{flex-direction:column;flex:auto;min-height:0;display:flex;position:relative;overflow:visible clip}.EvIC1a_scroll{min-height:0;padding:16px calc(var(--dsh-composer-side-clearance) + 16px);flex:auto;overflow-y:auto;container-type:inline-size}.EvIC1a_root[data-chat-following-tail] .EvIC1a_scroll,[data-conversation-scroll]:has(.EvIC1a_root[data-chat-following-tail]){overflow-anchor:none}[data-conversation-scroll] .EvIC1a_frame,[data-conversation-scroll] .EvIC1a_root{flex:none;height:auto;min-height:auto}[data-conversation-scroll] .EvIC1a_scroll{flex:none;min-height:auto;overflow:visible}.EvIC1a_column{max-width:var(--dsh-chat-content-width);flex-direction:column;width:100%;margin:0 auto;display:flex}.EvIC1a_column>:not([hidden]):not(.EvIC1a_flowItem:empty)~:not([hidden]):not(.EvIC1a_flowItem:empty){margin-top:var(--dsh-chat-flow-gap,16px)}.EvIC1a_flowItem{min-width:0}.EvIC1a_flowItem[data-turn-process-answer]{--dsh-chat-flow-gap:8px}.EvIC1a_flowItem:empty{height:0}.EvIC1a_callRow{border-radius:6px}.EvIC1a_hint{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}.EvIC1a_openError{color:var(--dsw-alias-state-error-primary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}.EvIC1a_older{justify-content:center;display:flex}.EvIC1a_older button{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover-solid);cursor:pointer;border:none;border-radius:14px;padding:4px 12px;font-size:12px}.EvIC1a_older button:disabled{cursor:default;opacity:.6}.EvIC1a_toBottomSlot{z-index:8;height:0;padding-right:max(calc(var(--dsh-composer-side-clearance) + 16px), calc((100% - var(--dsh-chat-content-width)) / 2));pointer-events:none;justify-content:flex-end;display:flex;position:absolute;bottom:16px;left:0;right:0}[data-conversation-scroll] .EvIC1a_toBottomSlot{bottom:calc(var(--dsh-composer-height,152px) + 16px);position:sticky}.EvIC1a_toBottom{--dsw-elevation-stroke-color:var(--dsw-alias-border-l3);corner-shape:round;width:34px;height:34px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-floating-fill);box-shadow:var(--dsw-elevation-panel);cursor:pointer;pointer-events:auto;border:0;border-radius:100px;justify-content:center;align-items:center;margin-top:-34px;padding:0;display:flex}.EvIC1a_toBottom:hover{background:var(--dsw-alias-button-floating-hover)}.EvIC1a_modalAction{min-width:72px}";
|
|
1583
1588
|
const tagId$13 = "@deepseek-ai/dsh-client-ui-chat/ChatView.module.css";
|
|
1584
1589
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
|
|
1585
1590
|
const tag = document.createElement("style");
|
|
@@ -1592,6 +1597,7 @@ window.__ModuleLoader__.load({
|
|
|
1592
1597
|
"callRow": "EvIC1a_callRow",
|
|
1593
1598
|
"column": "EvIC1a_column",
|
|
1594
1599
|
"flowItem": "EvIC1a_flowItem",
|
|
1600
|
+
"frame": "EvIC1a_frame",
|
|
1595
1601
|
"hint": "EvIC1a_hint",
|
|
1596
1602
|
"modalAction": "EvIC1a_modalAction",
|
|
1597
1603
|
"older": "EvIC1a_older",
|
|
@@ -1818,6 +1824,295 @@ window.__ModuleLoader__.load({
|
|
|
1818
1824
|
};
|
|
1819
1825
|
}
|
|
1820
1826
|
//#endregion
|
|
1827
|
+
//#region lib/types/client/chat/use-scroll-follow.js
|
|
1828
|
+
/** Independent bottom-follow intent and native scrolling, without paging or DOM observers. */
|
|
1829
|
+
/**
|
|
1830
|
+
* Read one scrollport without measuring its children.
|
|
1831
|
+
* @param element - scrolling element.
|
|
1832
|
+
* @returns current position and range.
|
|
1833
|
+
*/
|
|
1834
|
+
function scrollMetrics(element) {
|
|
1835
|
+
const height = element.clientHeight;
|
|
1836
|
+
return {
|
|
1837
|
+
top: element.scrollTop,
|
|
1838
|
+
height,
|
|
1839
|
+
floor: Math.max(0, element.scrollHeight - height)
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
/** One scrollport's follow intent; native animation progress does not count as reader movement. */
|
|
1843
|
+
var ScrollFollow = class ScrollFollow {
|
|
1844
|
+
following;
|
|
1845
|
+
threshold;
|
|
1846
|
+
static owners = /* @__PURE__ */ new WeakMap();
|
|
1847
|
+
target = null;
|
|
1848
|
+
sampledTop;
|
|
1849
|
+
/**
|
|
1850
|
+
* @param following - initial follow intent.
|
|
1851
|
+
* @param threshold - accepted distance from the floor, in pixels.
|
|
1852
|
+
*/
|
|
1853
|
+
constructor(following, threshold) {
|
|
1854
|
+
this.following = following;
|
|
1855
|
+
this.threshold = threshold;
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Find the mounted controller for reading-position compensation.
|
|
1859
|
+
* @param element - scrollport with an optional follow owner.
|
|
1860
|
+
* @returns its controller, when bound.
|
|
1861
|
+
*/
|
|
1862
|
+
static forElement(element) {
|
|
1863
|
+
return this.owners.get(element);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Share this controller with reading-position compensation for the same scrollport.
|
|
1867
|
+
* @param element - owned scrollport.
|
|
1868
|
+
* @returns release the association on unmount or close.
|
|
1869
|
+
*/
|
|
1870
|
+
bind(element) {
|
|
1871
|
+
ScrollFollow.owners.set(element, this);
|
|
1872
|
+
return () => {
|
|
1873
|
+
if (ScrollFollow.owners.get(element) === this) ScrollFollow.owners.delete(element);
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Expose follow intent independently of the current offset.
|
|
1878
|
+
* @returns whether content growth should follow the floor.
|
|
1879
|
+
*/
|
|
1880
|
+
get active() {
|
|
1881
|
+
return this.following;
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Expose outstanding native motion before accepting reader input.
|
|
1885
|
+
* @returns whether a native follow animation has an outstanding target.
|
|
1886
|
+
*/
|
|
1887
|
+
get animating() {
|
|
1888
|
+
return this.target !== null;
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Classify bottom arrivals using this scrollport's own tolerance.
|
|
1892
|
+
* @param metrics - current scroll geometry.
|
|
1893
|
+
* @returns whether the position is within the follow threshold.
|
|
1894
|
+
*/
|
|
1895
|
+
nearBottom(metrics) {
|
|
1896
|
+
return metrics.floor - metrics.top <= this.threshold;
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Commit caller-owned follow decisions without moving the scrollport.
|
|
1900
|
+
* @param active - externally committed follow intent.
|
|
1901
|
+
*/
|
|
1902
|
+
setFollowing(active) {
|
|
1903
|
+
this.following = active;
|
|
1904
|
+
if (!active) this.target = null;
|
|
1905
|
+
}
|
|
1906
|
+
/** Adopt the next visible layout as a fresh reader position. */
|
|
1907
|
+
reset() {
|
|
1908
|
+
this.setFollowing(false);
|
|
1909
|
+
this.sampledTop = void 0;
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Adopt delivered scrolling while retaining intent during native animation.
|
|
1913
|
+
* @param metrics - current geometry.
|
|
1914
|
+
* @param movedByReader - caller attribution; omitted callers compare the last sampled position.
|
|
1915
|
+
* @returns updated follow intent.
|
|
1916
|
+
*/
|
|
1917
|
+
sample(metrics, movedByReader = this.sampledTop === void 0 || Math.abs(metrics.top - this.sampledTop) > .5) {
|
|
1918
|
+
this.sampledTop = metrics.top;
|
|
1919
|
+
if (!this.animating && movedByReader) this.following = this.nearBottom(metrics);
|
|
1920
|
+
return this.active;
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Settle native scrolling; an off-target stop releases follow intent.
|
|
1924
|
+
* @param metrics - actual geometry delivered at scrollend.
|
|
1925
|
+
* @returns follow intent after completing or interrupting native motion.
|
|
1926
|
+
*/
|
|
1927
|
+
settle(metrics) {
|
|
1928
|
+
const target = this.target;
|
|
1929
|
+
this.target = null;
|
|
1930
|
+
return this.sample(metrics, target === null ? void 0 : Math.abs(metrics.top - Math.min(target, metrics.floor)) > this.threshold);
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Position immediately and adopt the resulting follow intent.
|
|
1934
|
+
* @param element - scrolling element.
|
|
1935
|
+
* @param metrics - geometry before positioning.
|
|
1936
|
+
* @param top - requested offset, clamped to the measured range.
|
|
1937
|
+
* @returns geometry after positioning.
|
|
1938
|
+
*/
|
|
1939
|
+
jump(element, metrics, top) {
|
|
1940
|
+
const animated = this.animating;
|
|
1941
|
+
this.target = null;
|
|
1942
|
+
const target = Math.max(0, Math.min(metrics.floor, top));
|
|
1943
|
+
if (animated) element.scrollTo({
|
|
1944
|
+
top: target,
|
|
1945
|
+
behavior: "instant"
|
|
1946
|
+
});
|
|
1947
|
+
else if (target !== metrics.top) element.scrollTop = target;
|
|
1948
|
+
const landed = {
|
|
1949
|
+
...metrics,
|
|
1950
|
+
top: element.scrollTop
|
|
1951
|
+
};
|
|
1952
|
+
this.sampledTop = landed.top;
|
|
1953
|
+
this.following = this.nearBottom(landed);
|
|
1954
|
+
return landed;
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Follow the measured floor, respecting reduced motion for smooth requests.
|
|
1958
|
+
* An outstanding smooth target finishes before another is issued.
|
|
1959
|
+
* Within-tolerance positioning is immediate while no animation is outstanding.
|
|
1960
|
+
* @param element - scrolling element.
|
|
1961
|
+
* @param metrics - current geometry.
|
|
1962
|
+
* @param behavior - native animation for growth, or immediate positioning.
|
|
1963
|
+
* @returns current geometry; smooth requests retain their starting position until native scroll delivery.
|
|
1964
|
+
*/
|
|
1965
|
+
toBottom(element, metrics, behavior) {
|
|
1966
|
+
this.following = true;
|
|
1967
|
+
if (behavior === "instant" || metrics.top >= metrics.floor || !this.animating && this.nearBottom(metrics)) return this.jump(element, metrics, metrics.floor);
|
|
1968
|
+
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) return this.jump(element, metrics, metrics.floor);
|
|
1969
|
+
if (this.target === null) {
|
|
1970
|
+
this.target = metrics.floor;
|
|
1971
|
+
element.scrollTo({
|
|
1972
|
+
top: metrics.floor,
|
|
1973
|
+
behavior: "smooth"
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
return metrics;
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Cancel native motion before a reader gesture; only subsequent actual movement changes follow intent.
|
|
1980
|
+
* @param element - scrolling element.
|
|
1981
|
+
* @param metrics - position at interruption.
|
|
1982
|
+
*/
|
|
1983
|
+
interrupt(element, metrics) {
|
|
1984
|
+
if (!this.animating) return;
|
|
1985
|
+
this.target = null;
|
|
1986
|
+
this.sampledTop = metrics.top;
|
|
1987
|
+
element.scrollTo({
|
|
1988
|
+
top: metrics.top,
|
|
1989
|
+
behavior: "instant"
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
/**
|
|
1994
|
+
* Retain one independent follow controller without React updates for scroll samples.
|
|
1995
|
+
* @param initial - initial follow intent.
|
|
1996
|
+
* @param threshold - accepted distance from the floor, in pixels.
|
|
1997
|
+
* @returns the stable controller shared by the caller's scroll and resize handlers.
|
|
1998
|
+
*/
|
|
1999
|
+
function useScrollFollow(initial, threshold) {
|
|
2000
|
+
const [follow] = (0, react.useState)(() => new ScrollFollow(initial, threshold));
|
|
2001
|
+
return follow;
|
|
2002
|
+
}
|
|
2003
|
+
//#endregion
|
|
2004
|
+
//#region lib/types/client/chat/use-process-scroll.js
|
|
2005
|
+
/** Capped process-group scrolling and fades over the shared follow controller. */
|
|
2006
|
+
const AT_REST = {
|
|
2007
|
+
canScrollUp: false,
|
|
2008
|
+
canScrollDown: false
|
|
2009
|
+
};
|
|
2010
|
+
const SCROLL_KEYS$1 = new Set([
|
|
2011
|
+
"ArrowUp",
|
|
2012
|
+
"ArrowDown",
|
|
2013
|
+
"PageUp",
|
|
2014
|
+
"PageDown",
|
|
2015
|
+
"Home",
|
|
2016
|
+
"End",
|
|
2017
|
+
" "
|
|
2018
|
+
]);
|
|
2019
|
+
/**
|
|
2020
|
+
* Observe one group's body and content without coupling its follow intent to the outer transcript.
|
|
2021
|
+
* Wheel, touchstart, any pointerdown, and unprevented scroll keys interrupt active animations,
|
|
2022
|
+
* including events from editable controls; subsequent position sampling determines follow intent.
|
|
2023
|
+
* @param bodyRef - capped scrolling body.
|
|
2024
|
+
* @param contentRef - uncapped content whose size reports growth.
|
|
2025
|
+
* @param open - local disclosure state.
|
|
2026
|
+
* @param grouped - whether the display mode retains the group's height cap.
|
|
2027
|
+
* @returns edge fades, DOM event bindings, and one-shot positioning for manual opening.
|
|
2028
|
+
*/
|
|
2029
|
+
function useProcessScroll(bodyRef, contentRef, open, grouped) {
|
|
2030
|
+
const follow = useScrollFollow(false, 1);
|
|
2031
|
+
const initialPosition = (0, react.useRef)(null);
|
|
2032
|
+
const [edges, setEdges] = (0, react.useState)(AT_REST);
|
|
2033
|
+
const initialize = (0, react.useCallback)((position) => {
|
|
2034
|
+
initialPosition.current = position;
|
|
2035
|
+
}, []);
|
|
2036
|
+
const sync = (0, react.useCallback)((cause) => {
|
|
2037
|
+
const body = bodyRef.current;
|
|
2038
|
+
let next = AT_REST;
|
|
2039
|
+
if (body !== null && body.closest("[hidden], [data-group-expanded-mode]") === null) {
|
|
2040
|
+
let metrics = scrollMetrics(body);
|
|
2041
|
+
const initial = cause === "resize" ? initialPosition.current : null;
|
|
2042
|
+
if (initial !== null) {
|
|
2043
|
+
metrics = follow.jump(body, metrics, initial === "bottom" ? metrics.floor : 0);
|
|
2044
|
+
if (initial === "top") follow.setFollowing(false);
|
|
2045
|
+
initialPosition.current = null;
|
|
2046
|
+
} else {
|
|
2047
|
+
const wasAnimating = follow.animating;
|
|
2048
|
+
if (cause === "scrollend") follow.settle(metrics);
|
|
2049
|
+
else follow.sample(metrics);
|
|
2050
|
+
if (follow.active && (cause === "resize" || cause === "scrollend" && wasAnimating)) metrics = follow.toBottom(body, metrics, "smooth");
|
|
2051
|
+
}
|
|
2052
|
+
next = {
|
|
2053
|
+
canScrollUp: metrics.top > 1,
|
|
2054
|
+
canScrollDown: metrics.top < metrics.floor - 1
|
|
2055
|
+
};
|
|
2056
|
+
} else follow.reset();
|
|
2057
|
+
setEdges((previous) => previous.canScrollUp === next.canScrollUp && previous.canScrollDown === next.canScrollDown ? previous : next);
|
|
2058
|
+
}, [bodyRef, follow]);
|
|
2059
|
+
const interrupt = (0, react.useCallback)(() => {
|
|
2060
|
+
const body = bodyRef.current;
|
|
2061
|
+
if (body !== null && follow.animating) follow.interrupt(body, scrollMetrics(body));
|
|
2062
|
+
}, [bodyRef, follow]);
|
|
2063
|
+
const events = (0, react.useMemo)(() => ({
|
|
2064
|
+
onScroll: () => {
|
|
2065
|
+
sync("scroll");
|
|
2066
|
+
},
|
|
2067
|
+
onWheel: interrupt,
|
|
2068
|
+
onTouchStart: interrupt,
|
|
2069
|
+
onPointerDown: interrupt,
|
|
2070
|
+
onKeyDown: (event) => {
|
|
2071
|
+
if (!event.defaultPrevented && SCROLL_KEYS$1.has(event.key)) interrupt();
|
|
2072
|
+
}
|
|
2073
|
+
}), [interrupt, sync]);
|
|
2074
|
+
(0, react.useLayoutEffect)(() => {
|
|
2075
|
+
interrupt();
|
|
2076
|
+
follow.reset();
|
|
2077
|
+
if (!grouped || !open) initialPosition.current = null;
|
|
2078
|
+
}, [
|
|
2079
|
+
follow,
|
|
2080
|
+
grouped,
|
|
2081
|
+
interrupt,
|
|
2082
|
+
open
|
|
2083
|
+
]);
|
|
2084
|
+
(0, react.useLayoutEffect)(() => {
|
|
2085
|
+
const body = bodyRef.current;
|
|
2086
|
+
if (body === null || !open || typeof ResizeObserver === "undefined") return;
|
|
2087
|
+
const unbind = follow.bind(body);
|
|
2088
|
+
const observer = new ResizeObserver(() => {
|
|
2089
|
+
sync("resize");
|
|
2090
|
+
});
|
|
2091
|
+
const onScrollEnd = (event) => {
|
|
2092
|
+
if (event.target === body) sync("scrollend");
|
|
2093
|
+
};
|
|
2094
|
+
body.addEventListener("scrollend", onScrollEnd);
|
|
2095
|
+
observer.observe(body);
|
|
2096
|
+
if (contentRef.current !== null) observer.observe(contentRef.current);
|
|
2097
|
+
return () => {
|
|
2098
|
+
unbind();
|
|
2099
|
+
observer.disconnect();
|
|
2100
|
+
body.removeEventListener("scrollend", onScrollEnd);
|
|
2101
|
+
};
|
|
2102
|
+
}, [
|
|
2103
|
+
bodyRef,
|
|
2104
|
+
contentRef,
|
|
2105
|
+
follow,
|
|
2106
|
+
open,
|
|
2107
|
+
sync
|
|
2108
|
+
]);
|
|
2109
|
+
return {
|
|
2110
|
+
edges,
|
|
2111
|
+
events,
|
|
2112
|
+
initialize
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
//#endregion
|
|
1821
2116
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/ChatGroupSeat.module.css.mjs
|
|
1822
2117
|
const css$12 = ".O_Ebla_root{min-width:0}.O_Ebla_title{max-width:100%;color:var(--dsw-alias-label-secondary);font:inherit;font-size:var(--dsh-content-font-size,14px);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:0;transition:color .1s;display:flex}.O_Ebla_title:hover{color:var(--dsw-alias-label-primary)}.O_Ebla_leading{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex;position:relative}.O_Ebla_activityIcon,.O_Ebla_chevron{justify-content:center;align-items:center;transition:opacity .1s;display:inline-flex;position:absolute;inset:0}.O_Ebla_activityIcon{opacity:1}.O_Ebla_chevron,.O_Ebla_title:is(:hover,:focus-visible) .O_Ebla_activityIcon{opacity:0}.O_Ebla_title:is(:hover,:focus-visible) .O_Ebla_chevron{opacity:1}.O_Ebla_title[aria-expanded=true] .O_Ebla_activityIcon{opacity:0}.O_Ebla_title[aria-expanded=true] .O_Ebla_chevron{opacity:1}.O_Ebla_title[aria-expanded=true]{padding-bottom:16px}.O_Ebla_body{--dsh-chat-flow-gap:8px;overscroll-behavior-y:auto;scrollbar-gutter:stable;max-height:min(400px,50vh);overflow-y:auto}.O_Ebla_label{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.O_Ebla_fadeTop{mask-image:linear-gradient(#0000 0,#000 24px 100%)}.O_Ebla_fadeBottom{mask-image:linear-gradient(#000 0 calc(100% - 24px),#0000 100%)}.O_Ebla_fadeTop.O_Ebla_fadeBottom{mask-image:linear-gradient(#0000 0,#000 24px calc(100% - 24px),#0000 100%)}@media (prefers-reduced-motion:reduce){.O_Ebla_title,.O_Ebla_activityIcon,.O_Ebla_chevron{transition:none}}.O_Ebla_content{flex-direction:column;display:flex}.O_Ebla_content>*{flex-shrink:0}.O_Ebla_content>:not([hidden]):not(:empty)~:not([hidden]):not(:empty){margin-top:var(--dsh-chat-flow-gap,8px)}.O_Ebla_expandedBody{--dsh-chat-flow-gap:16px;scrollbar-gutter:auto;max-height:none;overflow:visible}";
|
|
1823
2118
|
const tagId$12 = "@deepseek-ai/dsh-client-ui-chat/ChatGroupSeat.module.css";
|
|
@@ -1844,10 +2139,6 @@ window.__ModuleLoader__.load({
|
|
|
1844
2139
|
//#endregion
|
|
1845
2140
|
//#region lib/types/client/chat/ChatGroupSeat.js
|
|
1846
2141
|
/** Stable process container; display policy changes visibility, never member parents. */
|
|
1847
|
-
const PROCESS_SCROLL_AT_REST = {
|
|
1848
|
-
canScrollUp: false,
|
|
1849
|
-
canScrollDown: false
|
|
1850
|
-
};
|
|
1851
2142
|
const PROCESS_TITLE_MINIMUM_MS = 150;
|
|
1852
2143
|
const PROCESS_ICONS = {
|
|
1853
2144
|
thinking: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutlineRegular, {}),
|
|
@@ -1863,15 +2154,6 @@ window.__ModuleLoader__.load({
|
|
|
1863
2154
|
questions: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutlineRegular, {}),
|
|
1864
2155
|
tools: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkleRegular, { size: 14 })
|
|
1865
2156
|
};
|
|
1866
|
-
function processScrollEdges(element) {
|
|
1867
|
-
return {
|
|
1868
|
-
canScrollUp: element.scrollTop > 1,
|
|
1869
|
-
canScrollDown: element.scrollTop < element.scrollHeight - element.clientHeight - 1
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
function sameProcessScrollEdges(left, right) {
|
|
1873
|
-
return left.canScrollUp === right.canScrollUp && left.canScrollDown === right.canScrollDown;
|
|
1874
|
-
}
|
|
1875
2157
|
function sameLiveProcessTitle(left, right) {
|
|
1876
2158
|
return left.activity === right.activity && left.detail === right.detail;
|
|
1877
2159
|
}
|
|
@@ -1958,8 +2240,9 @@ window.__ModuleLoader__.load({
|
|
|
1958
2240
|
const ChatGroupSeat = (0, react.memo)(function ChatGroupSeat({ groupKey, useChatGroup, ...props }) {
|
|
1959
2241
|
const members = useChatGroup(groupKey, (group) => group?.members);
|
|
1960
2242
|
const turn = useChatGroup(groupKey, (group) => group?.data.turn);
|
|
2243
|
+
const closed = useChatGroup(groupKey, (group) => group?.data.closed);
|
|
1961
2244
|
const foldCompleted = props.usePresentation((policy) => policy.foldCompletedTurns);
|
|
1962
|
-
const { expanded: open, setExpanded: setOpen
|
|
2245
|
+
const { expanded: open, setExpanded: setOpen } = useDisclosure();
|
|
1963
2246
|
const firstKey = members?.[0]?.key ?? "";
|
|
1964
2247
|
const presentation = props.useChatNodeProcess(firstKey);
|
|
1965
2248
|
const turnLocation = props.useChatNode(firstKey, (node) => {
|
|
@@ -1993,25 +2276,15 @@ window.__ModuleLoader__.load({
|
|
|
1993
2276
|
const bodyRef = useSearchableHidden(grouped && !open, reveal);
|
|
1994
2277
|
const contentRef = (0, react.useRef)(null);
|
|
1995
2278
|
const bodyId = (0, react.useId)();
|
|
1996
|
-
const
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
setEdges((previous) => sameProcessScrollEdges(previous, next) ? previous : next);
|
|
2001
|
-
}, [bodyRef]);
|
|
2002
|
-
(0, react.useLayoutEffect)(() => {
|
|
2003
|
-
const body = bodyRef.current;
|
|
2004
|
-
if (body === null || !open || typeof ResizeObserver === "undefined") return;
|
|
2005
|
-
const observer = new ResizeObserver(sync);
|
|
2006
|
-
observer.observe(body);
|
|
2007
|
-
if (contentRef.current !== null) observer.observe(contentRef.current);
|
|
2008
|
-
return () => {
|
|
2009
|
-
observer.disconnect();
|
|
2010
|
-
};
|
|
2279
|
+
const { edges, events, initialize } = useProcessScroll(bodyRef, contentRef, open, grouped);
|
|
2280
|
+
const toggle = (0, react.useCallback)(() => {
|
|
2281
|
+
if (!open) initialize(closed === false ? "bottom" : "top");
|
|
2282
|
+
setOpen(!open);
|
|
2011
2283
|
}, [
|
|
2012
|
-
|
|
2284
|
+
closed,
|
|
2285
|
+
initialize,
|
|
2013
2286
|
open,
|
|
2014
|
-
|
|
2287
|
+
setOpen
|
|
2015
2288
|
]);
|
|
2016
2289
|
if (members === void 0) return null;
|
|
2017
2290
|
const classes = [
|
|
@@ -2048,7 +2321,7 @@ window.__ModuleLoader__.load({
|
|
|
2048
2321
|
"data-step-process-body": true,
|
|
2049
2322
|
"data-scroll-up": edges.canScrollUp || void 0,
|
|
2050
2323
|
"data-scroll-down": edges.canScrollDown || void 0,
|
|
2051
|
-
|
|
2324
|
+
...events,
|
|
2052
2325
|
children: (0, react_jsx_runtime.jsx)("div", {
|
|
2053
2326
|
ref: contentRef,
|
|
2054
2327
|
className: ChatGroupSeat_module_css_default.content,
|
|
@@ -3241,7 +3514,7 @@ window.__ModuleLoader__.load({
|
|
|
3241
3514
|
}
|
|
3242
3515
|
//#endregion
|
|
3243
3516
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/TurnNavigator.module.css.mjs
|
|
3244
|
-
const css$11 = ".eGxaPq_slot{z-index:7;pointer-events:none;
|
|
3517
|
+
const css$11 = ".eGxaPq_slot{z-index:7;height:0;padding-inline:calc(var(--dsh-composer-side-clearance) + 16px);pointer-events:none;position:absolute;top:0;left:0;right:0;container-type:inline-size}[data-conversation-scroll] .eGxaPq_slot{position:sticky}.eGxaPq_frame{--turn-rail-band:calc(var(--dsh-conversation-viewport-height,100dvh) - var(--dsh-composer-height,152px));--turn-preview-height:100px;top:calc(var(--turn-rail-band) / 2);width:28px;max-height:min(max(0px, calc(var(--turn-rail-band) - 64px)), 420px);contain:layout;cursor:pointer;pointer-events:auto;position:absolute;right:12px;transform:translateY(-50%)}.eGxaPq_scroller{max-height:inherit;overscroll-behavior:contain;scrollbar-width:none;position:relative;overflow-y:auto}.eGxaPq_scroller::-webkit-scrollbar{display:none}.eGxaPq_fadeTop{mask-image:linear-gradient(#0000 0,#000 24px 100%)}.eGxaPq_fadeBottom{mask-image:linear-gradient(#000 0 calc(100% - 24px),#0000 100%)}.eGxaPq_fadeTop.eGxaPq_fadeBottom{mask-image:linear-gradient(#0000 0,#000 24px calc(100% - 24px),#0000 100%)}.eGxaPq_marks{position:relative}.eGxaPq_mark{cursor:pointer;background:0 0;border:0;border-radius:8px;height:10px;padding:0;position:absolute;top:0;left:0;right:0}.eGxaPq_mark:before{background:var(--dsw-alias-border-l4);content:\"\";transform-origin:100%;border-radius:2px;width:20px;height:2px;transition:transform .14s,background-color .14s;position:absolute;top:50%;right:0;transform:translateY(-50%)scaleX(.6)}.eGxaPq_markUnloaded:before{opacity:.6;transform:translateY(-50%)scaleX(.4)}.eGxaPq_markPreview:before{background:var(--dsw-alias-label-tertiary);transform:translateY(-50%)scaleX(.9)}.eGxaPq_markBusy:before{animation:1s ease-in-out infinite eGxaPq_dsh-turn-mark-busy}.eGxaPq_markActive:before{background:var(--dsw-alias-label-primary);transform:translateY(-50%)scaleX(1)}.eGxaPq_mark:focus-visible:before{background:var(--dsw-alias-state-business-primary);transform:translateY(-50%)scaleX(1)}.eGxaPq_mark:focus-visible{outline:none}.eGxaPq_mark:focus-visible:after{border-radius:inherit;outline:1px solid var(--dsw-alias-state-business-primary);outline-offset:2px;content:\"\";width:20px;position:absolute;inset:0 0 0 auto}.eGxaPq_preview{top:clamp(0px, calc(var(--turn-preview-center) - var(--turn-preview-height) / 2), calc(100% - var(--turn-preview-height)));box-sizing:border-box;width:min(300px,100cqw - 120px);max-height:var(--turn-preview-height);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-panel);pointer-events:none;border:0;border-radius:10px;padding:10px 12px;transition:top .14s cubic-bezier(.2,.8,.2,1);animation:.12s ease-out eGxaPq_dsh-turn-preview-enter;position:absolute;right:calc(100% + 10px);overflow:hidden}.eGxaPq_previewPrompt,.eGxaPq_previewResponse{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.eGxaPq_previewPrompt{font:var(--dsw-font-xs-strong-13);-webkit-line-clamp:1}.eGxaPq_previewResponse{color:var(--dsw-alias-label-caption);font:var(--dsw-font-xxs-12);-webkit-line-clamp:3;margin-top:4px}@keyframes eGxaPq_dsh-turn-preview-enter{0%{opacity:0;transform:translate(4px)}to{opacity:1;transform:translate(0)}}@keyframes eGxaPq_dsh-turn-mark-busy{0%,to{opacity:1}50%{opacity:.35}}@container (width<=900px){.eGxaPq_frame{display:none}}@media (prefers-reduced-motion:reduce){.eGxaPq_frame,.eGxaPq_scroller,.eGxaPq_mark:before,.eGxaPq_markBusy:before,.eGxaPq_preview{scroll-behavior:auto;transition:none;animation:none}}";
|
|
3245
3518
|
const tagId$11 = "@deepseek-ai/dsh-client-ui-chat/TurnNavigator.module.css";
|
|
3246
3519
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$11) + "]") === null) {
|
|
3247
3520
|
const tag = document.createElement("style");
|
|
@@ -3839,14 +4112,16 @@ window.__ModuleLoader__.load({
|
|
|
3839
4112
|
store;
|
|
3840
4113
|
state;
|
|
3841
4114
|
onChange;
|
|
4115
|
+
follow;
|
|
3842
4116
|
sampleTimer = null;
|
|
3843
4117
|
probeFrame = null;
|
|
3844
4118
|
sampled = null;
|
|
3845
|
-
constructor(viewport, store, state, onChange) {
|
|
4119
|
+
constructor(viewport, store, state, onChange, follow) {
|
|
3846
4120
|
this.viewport = viewport;
|
|
3847
4121
|
this.store = store;
|
|
3848
4122
|
this.state = state;
|
|
3849
4123
|
this.onChange = onChange;
|
|
4124
|
+
this.follow = follow;
|
|
3850
4125
|
}
|
|
3851
4126
|
/**
|
|
3852
4127
|
* Expose pending reader ownership to navigation and resize handlers.
|
|
@@ -3895,7 +4170,7 @@ window.__ModuleLoader__.load({
|
|
|
3895
4170
|
}
|
|
3896
4171
|
/** Land at the current floor and clear saved reader position. */
|
|
3897
4172
|
followTail() {
|
|
3898
|
-
const landing = this.viewport.scrollToBottom();
|
|
4173
|
+
const landing = this.viewport.scrollToBottom(this.follow);
|
|
3899
4174
|
if (landing === null) return;
|
|
3900
4175
|
this.cancelPending();
|
|
3901
4176
|
this.commit(landing, true, this.viewport.latestTurn);
|
|
@@ -3910,7 +4185,7 @@ window.__ModuleLoader__.load({
|
|
|
3910
4185
|
const landing = this.viewport.restore(saved);
|
|
3911
4186
|
if (landing === null) return;
|
|
3912
4187
|
this.cancelPending();
|
|
3913
|
-
const following = this.nearBottom(landing.metrics);
|
|
4188
|
+
const following = this.follow.nearBottom(landing.metrics);
|
|
3914
4189
|
this.commit(landing, following, following ? this.viewport.latestTurn : this.state.activeTurn, following);
|
|
3915
4190
|
if (!this.state.followingTail && landing.position === null) {
|
|
3916
4191
|
const position = this.viewport.capturePosition();
|
|
@@ -3924,7 +4199,7 @@ window.__ModuleLoader__.load({
|
|
|
3924
4199
|
*/
|
|
3925
4200
|
acceptNavigation(landing) {
|
|
3926
4201
|
this.cancelPending();
|
|
3927
|
-
const following = this.nearBottom(landing.metrics);
|
|
4202
|
+
const following = this.follow.nearBottom(landing.metrics);
|
|
3928
4203
|
this.commit(landing, following, landing.turn ?? (following ? this.viewport.latestTurn : this.state.activeTurn));
|
|
3929
4204
|
}
|
|
3930
4205
|
/**
|
|
@@ -3976,9 +4251,6 @@ window.__ModuleLoader__.load({
|
|
|
3976
4251
|
if (typeof requestAnimationFrame !== "function") this.probe();
|
|
3977
4252
|
else this.probeFrame = requestAnimationFrame(this.probe);
|
|
3978
4253
|
}
|
|
3979
|
-
nearBottom(metrics) {
|
|
3980
|
-
return metrics.floor - metrics.top <= 25;
|
|
3981
|
-
}
|
|
3982
4254
|
commit(landing, followingTail, activeTurn, initialized = true) {
|
|
3983
4255
|
if (followingTail) this.store.save(null);
|
|
3984
4256
|
else if (landing.position !== null) this.store.save(landing.position);
|
|
@@ -3989,6 +4261,7 @@ window.__ModuleLoader__.load({
|
|
|
3989
4261
|
});
|
|
3990
4262
|
}
|
|
3991
4263
|
publish(state) {
|
|
4264
|
+
this.follow.setFollowing(state.followingTail);
|
|
3992
4265
|
if (state.initialized === this.state.initialized && state.followingTail === this.state.followingTail && state.activeTurn === this.state.activeTurn) return;
|
|
3993
4266
|
this.state = state;
|
|
3994
4267
|
this.onChange(state);
|
|
@@ -4004,7 +4277,7 @@ window.__ModuleLoader__.load({
|
|
|
4004
4277
|
if (this.pending) return;
|
|
4005
4278
|
const scroll = this.viewport.readScroll();
|
|
4006
4279
|
if (scroll === null) return;
|
|
4007
|
-
const activeTurn = this.nearBottom(scroll.metrics) ? this.viewport.latestTurn : this.viewport.readVisibleTurn(scroll.metrics);
|
|
4280
|
+
const activeTurn = this.follow.nearBottom(scroll.metrics) ? this.viewport.latestTurn : this.viewport.readVisibleTurn(scroll.metrics);
|
|
4008
4281
|
this.publish({
|
|
4009
4282
|
...this.state,
|
|
4010
4283
|
initialized: true,
|
|
@@ -4016,14 +4289,14 @@ window.__ModuleLoader__.load({
|
|
|
4016
4289
|
this.cancelPending();
|
|
4017
4290
|
const scroll = this.viewport.readScroll();
|
|
4018
4291
|
if (scroll === null) return;
|
|
4019
|
-
const followingTail =
|
|
4292
|
+
const followingTail = this.follow.sample(scroll.metrics, scroll.movedByReader);
|
|
4020
4293
|
let position = null;
|
|
4021
4294
|
if (!scroll.movedByReader && followingTail) this.followTail();
|
|
4022
4295
|
else {
|
|
4023
4296
|
position = followingTail ? null : this.viewport.capturePosition();
|
|
4024
4297
|
this.viewport.acknowledge(scroll.metrics);
|
|
4025
4298
|
if (followingTail || position !== null) this.store.save(position);
|
|
4026
|
-
const activeTurn = this.nearBottom(scroll.metrics) ? this.viewport.latestTurn : this.viewport.readVisibleTurn(scroll.metrics);
|
|
4299
|
+
const activeTurn = this.follow.nearBottom(scroll.metrics) ? this.viewport.latestTurn : this.viewport.readVisibleTurn(scroll.metrics);
|
|
4027
4300
|
this.publish({
|
|
4028
4301
|
initialized: true,
|
|
4029
4302
|
followingTail,
|
|
@@ -4050,7 +4323,8 @@ window.__ModuleLoader__.load({
|
|
|
4050
4323
|
followingTail: store.read() === null,
|
|
4051
4324
|
activeTurn: initialTurn
|
|
4052
4325
|
}));
|
|
4053
|
-
const
|
|
4326
|
+
const follow = useScrollFollow(state.followingTail, 25);
|
|
4327
|
+
const [reading] = (0, react.useState)(() => new ChatReading(viewport, store, state, setState, follow));
|
|
4054
4328
|
(0, react.useLayoutEffect)(() => {
|
|
4055
4329
|
reading.setStore(store);
|
|
4056
4330
|
}, [reading, store]);
|
|
@@ -4198,12 +4472,7 @@ window.__ModuleLoader__.load({
|
|
|
4198
4472
|
metrics() {
|
|
4199
4473
|
const scroller = this.elements?.scroller;
|
|
4200
4474
|
if (scroller === void 0) return null;
|
|
4201
|
-
|
|
4202
|
-
return {
|
|
4203
|
-
top: scroller.scrollTop,
|
|
4204
|
-
height,
|
|
4205
|
-
floor: Math.max(0, scroller.scrollHeight - height)
|
|
4206
|
-
};
|
|
4475
|
+
return scrollMetrics(scroller);
|
|
4207
4476
|
}
|
|
4208
4477
|
anchor(key, identity = "position") {
|
|
4209
4478
|
if (this.elements === null) return null;
|
|
@@ -4381,6 +4650,7 @@ window.__ModuleLoader__.load({
|
|
|
4381
4650
|
}
|
|
4382
4651
|
/**
|
|
4383
4652
|
* Compensate inner scrolling first, then the outer scrollport, within their actual scroll ranges.
|
|
4653
|
+
* An inner write pauses its bound follow controller so the reading anchor takes priority.
|
|
4384
4654
|
* @returns the actual landing, or null when no visible retained row remains.
|
|
4385
4655
|
*/
|
|
4386
4656
|
preserve() {
|
|
@@ -4403,9 +4673,16 @@ window.__ModuleLoader__.load({
|
|
|
4403
4673
|
}
|
|
4404
4674
|
if (group !== null && group.body.contains(row)) {
|
|
4405
4675
|
const top = row.getBoundingClientRect().top - group.body.getBoundingClientRect().top;
|
|
4406
|
-
const
|
|
4407
|
-
const target = Math.max(0, Math.min(floor,
|
|
4408
|
-
if (
|
|
4676
|
+
const metrics = scrollMetrics(group.body);
|
|
4677
|
+
const target = Math.max(0, Math.min(metrics.floor, metrics.top + top - group.top));
|
|
4678
|
+
if (metrics.top !== target) {
|
|
4679
|
+
const follow = ScrollFollow.forElement(group.body);
|
|
4680
|
+
if (follow === void 0) group.body.scrollTop = target;
|
|
4681
|
+
else {
|
|
4682
|
+
follow.jump(group.body, metrics, target);
|
|
4683
|
+
follow.setFollowing(false);
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4409
4686
|
}
|
|
4410
4687
|
const metrics = this.metrics();
|
|
4411
4688
|
if (metrics === null) return null;
|
|
@@ -4418,11 +4695,22 @@ window.__ModuleLoader__.load({
|
|
|
4418
4695
|
}
|
|
4419
4696
|
/**
|
|
4420
4697
|
* Align the scrollport with its current floor.
|
|
4698
|
+
* @param follow - independent follow intent and scrolling controller.
|
|
4421
4699
|
* @returns the actual floor landing, or null while detached.
|
|
4422
4700
|
*/
|
|
4423
|
-
scrollToBottom() {
|
|
4701
|
+
scrollToBottom(follow) {
|
|
4424
4702
|
const metrics = this.metrics();
|
|
4425
|
-
|
|
4703
|
+
if (metrics === null || this.elements === null) return null;
|
|
4704
|
+
const landing = {
|
|
4705
|
+
metrics: follow.toBottom(this.elements.scroller, metrics, "instant"),
|
|
4706
|
+
position: null,
|
|
4707
|
+
turn: this.latestTurn
|
|
4708
|
+
};
|
|
4709
|
+
this.observation = {
|
|
4710
|
+
top: landing.metrics.top,
|
|
4711
|
+
landing
|
|
4712
|
+
};
|
|
4713
|
+
return landing;
|
|
4426
4714
|
}
|
|
4427
4715
|
align(row, offset, turn) {
|
|
4428
4716
|
const metrics = this.metrics();
|
|
@@ -4646,29 +4934,28 @@ window.__ModuleLoader__.load({
|
|
|
4646
4934
|
return message === "" ? fallback : message;
|
|
4647
4935
|
}
|
|
4648
4936
|
/**
|
|
4649
|
-
*
|
|
4650
|
-
*
|
|
4651
|
-
*
|
|
4652
|
-
* atomic — no duplicate, no gap — regardless of when the echo leaves the
|
|
4653
|
-
* session snapshot.
|
|
4937
|
+
* Durable input identities suppress matching echoes in the same render.
|
|
4938
|
+
* The last input's Turn also distinguishes an empty opening control from
|
|
4939
|
+
* one whose human input or trigger notice is already present.
|
|
4654
4940
|
*/
|
|
4655
|
-
function
|
|
4941
|
+
function observedInputs(order, nodes) {
|
|
4656
4942
|
const observed = /* @__PURE__ */ new Set();
|
|
4943
|
+
let lastInputTurn;
|
|
4657
4944
|
for (const key of order) {
|
|
4658
4945
|
const node = nodes.get(key);
|
|
4659
|
-
if (node === void 0 || node.kind !== "user" && node.kind !== "steering") continue;
|
|
4946
|
+
if (node === void 0 || node.kind !== "user" && node.kind !== "steering" && node.kind !== "turn-trigger") continue;
|
|
4947
|
+
if (node.location.kind === "turn" || node.location.kind === "step") lastInputTurn = node.location.turn.turn;
|
|
4948
|
+
if (node.kind === "turn-trigger") continue;
|
|
4660
4949
|
const source = node.data.source;
|
|
4661
4950
|
if (source?.kind === "user" && typeof source.rpcId === "string") observed.add(source.rpcId);
|
|
4662
4951
|
}
|
|
4663
|
-
const pending = /* @__PURE__ */ new Set();
|
|
4664
|
-
for (const { source } of [...inbox?.["next-turn"] ?? [], ...inbox?.["next-step"] ?? []]) if (source.kind === "user" && "rpcId" in source) pending.add(source.rpcId);
|
|
4665
4952
|
return {
|
|
4666
|
-
|
|
4667
|
-
|
|
4953
|
+
rpcIds: observed,
|
|
4954
|
+
lastInputTurn
|
|
4668
4955
|
};
|
|
4669
4956
|
}
|
|
4670
|
-
const ChatNodeList = (0, react.memo)(function ChatNodeList({ entries, useChatGroup, ...seatProps }) {
|
|
4671
|
-
|
|
4957
|
+
const ChatNodeList = (0, react.memo)(function ChatNodeList({ entries, useChatGroup, pendingInputs, lastInputTurn, ...seatProps }) {
|
|
4958
|
+
const rows = entries.map((entry) => {
|
|
4672
4959
|
switch (entry.kind) {
|
|
4673
4960
|
case "node": return (0, react.createElement)(ChatNodeSeat, {
|
|
4674
4961
|
...seatProps,
|
|
@@ -4685,6 +4972,22 @@ window.__ModuleLoader__.load({
|
|
|
4685
4972
|
default: return assertNever(entry);
|
|
4686
4973
|
}
|
|
4687
4974
|
});
|
|
4975
|
+
const pendingRows = pendingInputs.map((item) => "requestId" in item ? (0, react_jsx_runtime.jsx)(PendingSubmissionBubble, {
|
|
4976
|
+
submission: item,
|
|
4977
|
+
renderMessageImages: seatProps.renderMessageImages,
|
|
4978
|
+
t: seatProps.t
|
|
4979
|
+
}, item.requestId) : (0, react_jsx_runtime.jsx)(PendingSteeringBubble, {
|
|
4980
|
+
content: item.content,
|
|
4981
|
+
renderMessageImages: seatProps.renderMessageImages,
|
|
4982
|
+
t: seatProps.t
|
|
4983
|
+
}, item.id));
|
|
4984
|
+
const tail = entries.at(-1);
|
|
4985
|
+
const node = tail?.kind === "node" ? seatProps.nodeStore.get(tail.key) : void 0;
|
|
4986
|
+
if (node?.kind === "turn-process" && node.location.kind === "turn" && node.location.turn.status === "open" && node.location.turn.turn !== lastInputTurn) {
|
|
4987
|
+
const index = pendingInputs.findIndex((item) => "requestId" in item && item.placement === "transcript");
|
|
4988
|
+
if (index !== -1) rows.splice(rows.length - 1, 0, ...pendingRows.splice(index, 1));
|
|
4989
|
+
}
|
|
4990
|
+
return [...rows, ...pendingRows];
|
|
4688
4991
|
});
|
|
4689
4992
|
/**
|
|
4690
4993
|
* The chat view slot entry: pure component over the composed props; each
|
|
@@ -4734,27 +5037,31 @@ window.__ModuleLoader__.load({
|
|
|
4734
5037
|
}, []);
|
|
4735
5038
|
const inboxSteering = (0, react.useMemo)(() => inbox?.["next-step"].filter((message) => message.source.kind === "user") ?? [], [inbox]);
|
|
4736
5039
|
const pendingSubmissions = useSession((s) => s.pendingSubmissions);
|
|
4737
|
-
const visibleSubmissions = (0, react.useMemo)(() => {
|
|
4738
|
-
if (pendingSubmissions.length === 0) return pendingSubmissions;
|
|
4739
|
-
const observed =
|
|
4740
|
-
return pendingSubmissions.filter((submission) => submission.placement !== "queued" && !observed.
|
|
5040
|
+
const [visibleSubmissions, lastInputTurn] = (0, react.useMemo)(() => {
|
|
5041
|
+
if (pendingSubmissions.length === 0) return [pendingSubmissions, void 0];
|
|
5042
|
+
const observed = observedInputs(order, nodeStore);
|
|
5043
|
+
return [pendingSubmissions.filter((submission) => submission.placement !== "queued" && !observed.rpcIds.has(submission.requestId)), observed.lastInputTurn];
|
|
4741
5044
|
}, [
|
|
4742
5045
|
pendingSubmissions,
|
|
4743
5046
|
order,
|
|
4744
|
-
nodeStore
|
|
4745
|
-
inbox
|
|
5047
|
+
nodeStore
|
|
4746
5048
|
]);
|
|
4747
5049
|
const pendingInputs = (0, react.useMemo)(() => {
|
|
4748
5050
|
const local = new Map(visibleSubmissions.map((submission) => [submission.requestId, submission]));
|
|
4749
|
-
|
|
5051
|
+
const localIds = new Set(pendingSubmissions.filter((submission) => submission.placement !== "queued").map((submission) => submission.requestId));
|
|
5052
|
+
return [...inboxSteering.flatMap((item) => {
|
|
4750
5053
|
const source = item.source;
|
|
4751
|
-
if (source.kind !== "user" || !("rpcId" in source)) return item;
|
|
5054
|
+
if (source.kind !== "user" || !("rpcId" in source)) return [item];
|
|
4752
5055
|
const submission = local.get(source.rpcId);
|
|
4753
|
-
if (submission === void 0) return item;
|
|
5056
|
+
if (submission === void 0) return localIds.has(source.rpcId) ? [] : [item];
|
|
4754
5057
|
local.delete(source.rpcId);
|
|
4755
|
-
return submission;
|
|
5058
|
+
return [submission];
|
|
4756
5059
|
}), ...local.values()];
|
|
4757
|
-
}, [
|
|
5060
|
+
}, [
|
|
5061
|
+
inboxSteering,
|
|
5062
|
+
pendingSubmissions,
|
|
5063
|
+
visibleSubmissions
|
|
5064
|
+
]);
|
|
4758
5065
|
const renderMessageImages = (0, react.useCallback)((owner) => renderSlot("conversation.message.images", {
|
|
4759
5066
|
...owner,
|
|
4760
5067
|
loadImage
|
|
@@ -4781,99 +5088,96 @@ window.__ModuleLoader__.load({
|
|
|
4781
5088
|
loadedTurns: turnNavigationItems
|
|
4782
5089
|
});
|
|
4783
5090
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
4784
|
-
className: ChatView_module_css_default.
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
scroll.
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
entries,
|
|
4827
|
-
nodeStore,
|
|
4828
|
-
useChatGroup,
|
|
4829
|
-
useChatNode,
|
|
4830
|
-
useChatNodeProcess,
|
|
4831
|
-
usePresentation,
|
|
4832
|
-
useStore,
|
|
4833
|
-
actions,
|
|
4834
|
-
cwd,
|
|
5091
|
+
className: ChatView_module_css_default.frame,
|
|
5092
|
+
children: [
|
|
5093
|
+
scroll.initialized && (0, react_jsx_runtime.jsx)(TurnNavigator, {
|
|
5094
|
+
items: railItems,
|
|
5095
|
+
activeTurn: scroll.activeTurn,
|
|
5096
|
+
busyTurn: scroll.busyTurn,
|
|
5097
|
+
onNavigate: scroll.navigateToTurn,
|
|
5098
|
+
t
|
|
5099
|
+
}),
|
|
5100
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
5101
|
+
className: ChatView_module_css_default.root,
|
|
5102
|
+
"data-chat-following-tail": scroll.followingTail ? "" : void 0,
|
|
5103
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
5104
|
+
ref: scroll.listRef,
|
|
5105
|
+
className: ChatView_module_css_default.scroll,
|
|
5106
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
5107
|
+
ref: scroll.columnRef,
|
|
5108
|
+
className: ChatView_module_css_default.column,
|
|
5109
|
+
"data-chat-flow": "",
|
|
5110
|
+
children: [
|
|
5111
|
+
openState === "loading" && (0, react_jsx_runtime.jsx)("div", {
|
|
5112
|
+
className: ChatView_module_css_default.hint,
|
|
5113
|
+
children: t("chat.loadingHistory")
|
|
5114
|
+
}),
|
|
5115
|
+
openState === "error" && openError !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
5116
|
+
className: ChatView_module_css_default.openError,
|
|
5117
|
+
children: t("chat.loadError", {
|
|
5118
|
+
message: openError.message,
|
|
5119
|
+
code: openError.code
|
|
5120
|
+
})
|
|
5121
|
+
}),
|
|
5122
|
+
hasMore && (0, react_jsx_runtime.jsx)("div", {
|
|
5123
|
+
className: ChatView_module_css_default.older,
|
|
5124
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
5125
|
+
type: "button",
|
|
5126
|
+
disabled: loadingOlder,
|
|
5127
|
+
onClick: scroll.loadEarlier,
|
|
5128
|
+
children: loadingOlder ? t("loading") : t("chat.loadOlder")
|
|
5129
|
+
})
|
|
5130
|
+
}),
|
|
5131
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownDelegateProvider, {
|
|
5132
|
+
openExternalLink,
|
|
4835
5133
|
openFile: requestOpenFile,
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
5134
|
+
children: (0, react_jsx_runtime.jsx)(ChatNodeList, {
|
|
5135
|
+
entries,
|
|
5136
|
+
pendingInputs,
|
|
5137
|
+
lastInputTurn,
|
|
5138
|
+
nodeStore,
|
|
5139
|
+
useChatGroup,
|
|
5140
|
+
useChatNode,
|
|
5141
|
+
useChatNodeProcess,
|
|
5142
|
+
usePresentation,
|
|
5143
|
+
useStore,
|
|
5144
|
+
actions,
|
|
5145
|
+
cwd,
|
|
5146
|
+
openFile: requestOpenFile,
|
|
5147
|
+
openSkill,
|
|
5148
|
+
inspectCall,
|
|
5149
|
+
forkAt,
|
|
5150
|
+
loadImage,
|
|
5151
|
+
renderMessageImages,
|
|
5152
|
+
fileMentions,
|
|
5153
|
+
renderSlot,
|
|
5154
|
+
t
|
|
5155
|
+
})
|
|
4844
5156
|
})
|
|
4845
|
-
|
|
4846
|
-
pendingInputs.map((item) => "requestId" in item ? (0, react_jsx_runtime.jsx)(PendingSubmissionBubble, {
|
|
4847
|
-
submission: item,
|
|
4848
|
-
renderMessageImages,
|
|
4849
|
-
t
|
|
4850
|
-
}, item.requestId) : (0, react_jsx_runtime.jsx)(PendingSteeringBubble, {
|
|
4851
|
-
content: item.content,
|
|
4852
|
-
renderMessageImages,
|
|
4853
|
-
t
|
|
4854
|
-
}, item.id))
|
|
4855
|
-
]
|
|
4856
|
-
}),
|
|
4857
|
-
!scroll.followingTail && (0, react_jsx_runtime.jsx)("div", {
|
|
4858
|
-
className: ChatView_module_css_default.toBottomSlot,
|
|
4859
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
4860
|
-
type: "button",
|
|
4861
|
-
className: ChatView_module_css_default.toBottom,
|
|
4862
|
-
"aria-label": t("chat.toBottom"),
|
|
4863
|
-
onClick: scroll.returnToBottom,
|
|
4864
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {})
|
|
5157
|
+
]
|
|
4865
5158
|
})
|
|
4866
5159
|
})
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
5160
|
+
}),
|
|
5161
|
+
!scroll.followingTail && (0, react_jsx_runtime.jsx)("div", {
|
|
5162
|
+
className: ChatView_module_css_default.toBottomSlot,
|
|
5163
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
5164
|
+
type: "button",
|
|
5165
|
+
className: ChatView_module_css_default.toBottom,
|
|
5166
|
+
"aria-label": t("chat.toBottom"),
|
|
5167
|
+
onClick: scroll.returnToBottom,
|
|
5168
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {})
|
|
5169
|
+
})
|
|
5170
|
+
}),
|
|
5171
|
+
fileOpenError !== null && (0, react_jsx_runtime.jsx)(FileOpenErrorDialog, {
|
|
5172
|
+
message: fileOpenError.message,
|
|
5173
|
+
busy: fileOpenBusy,
|
|
5174
|
+
onClose: closeFileOpenError,
|
|
5175
|
+
onRetry: () => {
|
|
5176
|
+
requestOpenFile(fileOpenError.path);
|
|
5177
|
+
},
|
|
5178
|
+
t
|
|
5179
|
+
})
|
|
5180
|
+
]
|
|
4877
5181
|
});
|
|
4878
5182
|
}
|
|
4879
5183
|
/** In-page Host open-path refusal: the wire reason plus a retry of the same path. */
|
|
@@ -4974,10 +5278,10 @@ window.__ModuleLoader__.load({
|
|
|
4974
5278
|
"settings.performance.description": "选择性能与用量信息展示的详细程度",
|
|
4975
5279
|
"settings.performance.compact": "简洁",
|
|
4976
5280
|
"settings.performance.detailed": "详细",
|
|
4977
|
-
"settings.links.title": "
|
|
4978
|
-
"settings.links.description": "
|
|
4979
|
-
"settings.links.sidebar": "
|
|
4980
|
-
"settings.links.newTab": "
|
|
5281
|
+
"settings.links.title": "网页链接默认打开方式",
|
|
5282
|
+
"settings.links.description": "对话中网页链接的打开位置",
|
|
5283
|
+
"settings.links.sidebar": "应用内侧边栏",
|
|
5284
|
+
"settings.links.newTab": "默认浏览器",
|
|
4981
5285
|
"settings.transcript.title": "工作过程展示",
|
|
4982
5286
|
"settings.transcript.description": "控制轮次和步骤的默认展开方式",
|
|
4983
5287
|
"settings.transcript.compact": "简洁",
|
|
@@ -5129,9 +5433,9 @@ window.__ModuleLoader__.load({
|
|
|
5129
5433
|
"settings.performance.compact": "Compact",
|
|
5130
5434
|
"settings.performance.detailed": "Detailed",
|
|
5131
5435
|
"settings.links.title": "Open chat links in",
|
|
5132
|
-
"settings.links.description": "Choose where
|
|
5133
|
-
"settings.links.sidebar": "
|
|
5134
|
-
"settings.links.newTab": "
|
|
5436
|
+
"settings.links.description": "Choose where to open web links",
|
|
5437
|
+
"settings.links.sidebar": "In-App Sidebar",
|
|
5438
|
+
"settings.links.newTab": "Default Browser",
|
|
5135
5439
|
"settings.transcript.title": "Work details",
|
|
5136
5440
|
"settings.transcript.description": "Controls how turns and steps expand by default",
|
|
5137
5441
|
"settings.transcript.compact": "Compact",
|