@nextclaw/ui 0.9.10 → 0.9.11

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/assets/{ChannelsList-BgJbR6E9.js → ChannelsList-Brc1qLSU.js} +1 -1
  3. package/dist/assets/{ChatPage-Bv9UJPse.js → ChatPage-DmGI776q.js} +1 -1
  4. package/dist/assets/{DocBrowser-Dw9BGO1m.js → DocBrowser-xLVf1p4L.js} +1 -1
  5. package/dist/assets/{LogoBadge-CLc2B6st.js → LogoBadge-CcTyimdr.js} +1 -1
  6. package/dist/assets/{MarketplacePage-ChqCNL7k.js → MarketplacePage-Bk-qXxyh.js} +1 -1
  7. package/dist/assets/{McpMarketplacePage-B3PF-7ED.js → McpMarketplacePage-gFqAYekc.js} +1 -1
  8. package/dist/assets/{ModelConfig-Dqz_NOow.js → ModelConfig-DnKNTuw6.js} +1 -1
  9. package/dist/assets/{ProvidersList-D2WaZShJ.js → ProvidersList-Cjr8EFu_.js} +1 -1
  10. package/dist/assets/{RemoteAccessPage-D_l9irp4.js → RemoteAccessPage-Rzi5a6Gc.js} +1 -1
  11. package/dist/assets/{RuntimeConfig-TDxQLuGy.js → RuntimeConfig-CttN--Tv.js} +1 -1
  12. package/dist/assets/{SearchConfig-gba64nGn.js → SearchConfig-D-GzinsL.js} +1 -1
  13. package/dist/assets/{SecretsConfig-DpL8wgly.js → SecretsConfig-BvqQq4Ds.js} +1 -1
  14. package/dist/assets/{SessionsConfig-CAODVTNW.js → SessionsConfig-DbtnLmI6.js} +1 -1
  15. package/dist/assets/{chat-message-CSG50nNb.js → chat-message-DYQjL1tD.js} +1 -1
  16. package/dist/assets/{index-DaEflNCE.js → index-ClLy_7T2.js} +5 -5
  17. package/dist/assets/{label-3T28q3PJ.js → label-DBSKOMGE.js} +1 -1
  18. package/dist/assets/{page-layout-BrXOQeua.js → page-layout-B5th9UzR.js} +1 -1
  19. package/dist/assets/{popover-BrBJjElY.js → popover-BEIWRoeP.js} +1 -1
  20. package/dist/assets/{security-config-oGAhN4Zf.js → security-config-D72JskP5.js} +1 -1
  21. package/dist/assets/{skeleton-CIPQUKo2.js → skeleton-B_Pn9x0i.js} +1 -1
  22. package/dist/assets/{status-dot-QL3hmT1d.js → status-dot-CU5ZpOn1.js} +1 -1
  23. package/dist/assets/{switch-Dbt2kUg2.js → switch-BdaXEtXk.js} +1 -1
  24. package/dist/assets/{tabs-custom-y5hdkzXk.js → tabs-custom-BVhSoteN.js} +1 -1
  25. package/dist/assets/{useConfirmDialog-B4zwBVbl.js → useConfirmDialog-Dugi9V-Z.js} +1 -1
  26. package/dist/index.html +1 -1
  27. package/package.json +4 -4
  28. package/src/api/config.ts +1 -4
  29. package/src/hooks/use-realtime-query-bridge.ts +77 -71
  30. package/src/transport/local.transport.ts +5 -123
  31. package/src/transport/remote.transport.ts +39 -74
  32. package/src/transport/sse-stream.ts +114 -0
  33. package/src/transport/transport-websocket-url.ts +24 -0
@@ -0,0 +1,114 @@
1
+ import type { StreamEvent } from './transport.types';
2
+
3
+ type SseErrorPayload = { message?: string } | string | undefined;
4
+
5
+ function parseSseFrame(frame: string): StreamEvent | null {
6
+ const lines = frame.split('\n');
7
+ let name = '';
8
+ const dataLines: string[] = [];
9
+ for (const raw of lines) {
10
+ const line = raw.trimEnd();
11
+ if (!line || line.startsWith(':')) {
12
+ continue;
13
+ }
14
+ if (line.startsWith('event:')) {
15
+ name = line.slice(6).trim();
16
+ continue;
17
+ }
18
+ if (line.startsWith('data:')) {
19
+ dataLines.push(line.slice(5).trimStart());
20
+ }
21
+ }
22
+ if (!name) {
23
+ return null;
24
+ }
25
+
26
+ let payload: unknown = undefined;
27
+ const data = dataLines.join('\n');
28
+ if (data) {
29
+ try {
30
+ payload = JSON.parse(data);
31
+ } catch {
32
+ payload = data;
33
+ }
34
+ }
35
+
36
+ return { name, payload };
37
+ }
38
+
39
+ function readSseErrorMessage(payload: SseErrorPayload, fallback: string): string {
40
+ return typeof payload === 'string'
41
+ ? payload
42
+ : payload?.message ?? fallback;
43
+ }
44
+
45
+ function processSseFrame(
46
+ rawFrame: string,
47
+ onEvent: (event: StreamEvent) => void,
48
+ setFinalResult: (value: unknown) => void
49
+ ): void {
50
+ const frame = parseSseFrame(rawFrame);
51
+ if (!frame) {
52
+ return;
53
+ }
54
+ if (frame.name === 'final') {
55
+ setFinalResult(frame.payload);
56
+ return;
57
+ }
58
+ if (frame.name === 'error') {
59
+ throw new Error(readSseErrorMessage(frame.payload as SseErrorPayload, 'chat stream failed'));
60
+ }
61
+ onEvent(frame);
62
+ }
63
+
64
+ function flushBufferedFrames(
65
+ bufferState: { value: string },
66
+ onEvent: (event: StreamEvent) => void,
67
+ setFinalResult: (value: unknown) => void
68
+ ): void {
69
+ let boundary = bufferState.value.indexOf('\n\n');
70
+ while (boundary !== -1) {
71
+ processSseFrame(bufferState.value.slice(0, boundary), onEvent, setFinalResult);
72
+ bufferState.value = bufferState.value.slice(boundary + 2);
73
+ boundary = bufferState.value.indexOf('\n\n');
74
+ }
75
+ }
76
+
77
+ export async function readSseStreamResult<TFinal>(
78
+ response: Response,
79
+ onEvent: (event: StreamEvent) => void
80
+ ): Promise<TFinal> {
81
+ const reader = response.body?.getReader();
82
+ if (!reader) {
83
+ throw new Error('SSE response body unavailable');
84
+ }
85
+
86
+ const decoder = new TextDecoder();
87
+ const bufferState = { value: '' };
88
+ let finalResult: unknown = undefined;
89
+
90
+ try {
91
+ while (true) {
92
+ const { value, done } = await reader.read();
93
+ if (done) {
94
+ break;
95
+ }
96
+ bufferState.value += decoder.decode(value, { stream: true });
97
+ flushBufferedFrames(bufferState, onEvent, (nextValue) => {
98
+ finalResult = nextValue;
99
+ });
100
+ }
101
+ if (bufferState.value.trim()) {
102
+ processSseFrame(bufferState.value, onEvent, (nextValue) => {
103
+ finalResult = nextValue;
104
+ });
105
+ }
106
+ } finally {
107
+ reader.releaseLock();
108
+ }
109
+
110
+ if (finalResult === undefined) {
111
+ throw new Error('stream ended without final event');
112
+ }
113
+ return finalResult as TFinal;
114
+ }
@@ -0,0 +1,24 @@
1
+ export function resolveTransportWebSocketUrl(base: string, path: string): string {
2
+ const normalizedBase = base.replace(/\/$/, '');
3
+ try {
4
+ const resolved = new URL(normalizedBase, window.location.origin);
5
+ const protocol =
6
+ resolved.protocol === 'https:'
7
+ ? 'wss:'
8
+ : resolved.protocol === 'http:'
9
+ ? 'ws:'
10
+ : resolved.protocol;
11
+ return `${protocol}//${resolved.host}${path}`;
12
+ } catch {
13
+ if (normalizedBase.startsWith('wss://') || normalizedBase.startsWith('ws://')) {
14
+ return `${normalizedBase}${path}`;
15
+ }
16
+ if (normalizedBase.startsWith('https://')) {
17
+ return `${normalizedBase.replace(/^https:/, 'wss:')}${path}`;
18
+ }
19
+ if (normalizedBase.startsWith('http://')) {
20
+ return `${normalizedBase.replace(/^http:/, 'ws:')}${path}`;
21
+ }
22
+ return `${normalizedBase}${path}`;
23
+ }
24
+ }