@adminide-stack/yantra-mobile 12.0.49 → 12.0.50-alpha.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/lib/bridge/NativeWebViewBridge.js +14 -1
- package/lib/bridge/NativeWebViewBridge.js.map +1 -1
- package/lib/components/KeyboardComposerDock.js +23 -17
- package/lib/components/KeyboardComposerDock.js.map +1 -1
- package/lib/features/apps/AppsCatalog.js +9 -0
- package/lib/features/apps/AppsCatalog.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +16 -8
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/skills/skillTemplates.js.map +1 -1
- package/lib/hooks/useChatStream.js +30 -6
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/screens/Chat/index.js +5 -8
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js +7 -17
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/DeepSearchModal.js +1 -1
- package/lib/screens/Home/components/DeepSearchModal.js.map +1 -1
- package/lib/screens/Home/components/WebBuilderPrewarm.js +4 -47
- package/lib/screens/Home/components/WebBuilderPrewarm.js.map +1 -1
- package/lib/screens/WebBuilder/webbuilderWarmCache.js +1 -15
- package/lib/screens/WebBuilder/webbuilderWarmCache.js.map +1 -1
- package/package.json +4 -4
- package/lib/hooks/useKeyboardBottomOffset.js +0 -42
- package/lib/hooks/useKeyboardBottomOffset.js.map +0 -1
|
@@ -16,6 +16,8 @@ class NativeWebViewBridge {
|
|
|
16
16
|
batchDelayMs: 100
|
|
17
17
|
});
|
|
18
18
|
__publicField(this, "listeners", /* @__PURE__ */ new Map());
|
|
19
|
+
__publicField(this, "disposed", false);
|
|
20
|
+
__publicField(this, "rafHandles", /* @__PURE__ */ new Set());
|
|
19
21
|
this.webViewRef = webViewRef;
|
|
20
22
|
}
|
|
21
23
|
on(event, listener) {
|
|
@@ -113,6 +115,7 @@ class NativeWebViewBridge {
|
|
|
113
115
|
this.sendMessage(response);
|
|
114
116
|
}
|
|
115
117
|
handleMessage(event) {
|
|
118
|
+
if (this.disposed) return;
|
|
116
119
|
const rawData = event.nativeEvent.data;
|
|
117
120
|
const data = typeof rawData === "string" ? rawData : JSON.stringify(rawData);
|
|
118
121
|
try {
|
|
@@ -135,6 +138,9 @@ class NativeWebViewBridge {
|
|
|
135
138
|
}
|
|
136
139
|
}
|
|
137
140
|
dispose() {
|
|
141
|
+
this.disposed = true;
|
|
142
|
+
this.rafHandles.forEach((handle) => cancelAnimationFrame(handle));
|
|
143
|
+
this.rafHandles.clear();
|
|
138
144
|
this.flushBatch();
|
|
139
145
|
this.pendingRequests.forEach((pending) => {
|
|
140
146
|
clearTimeout(pending.timeout);
|
|
@@ -201,18 +207,25 @@ class NativeWebViewBridge {
|
|
|
201
207
|
this.emitLocal("bridge:error", message.payload);
|
|
202
208
|
}
|
|
203
209
|
sendMessage(message) {
|
|
210
|
+
if (this.disposed) return;
|
|
204
211
|
if (!this.isReady && message.action !== BRIDGE_READY_ACTION) {
|
|
205
212
|
this.messageQueue.push(message);
|
|
206
213
|
return;
|
|
207
214
|
}
|
|
208
215
|
this.debugger.logSent(message);
|
|
209
216
|
const deliver = (serialized) => {
|
|
217
|
+
if (this.disposed) return;
|
|
210
218
|
const post = () => {
|
|
211
219
|
var _a;
|
|
220
|
+
if (this.disposed) return;
|
|
212
221
|
(_a = this.webViewRef.current) == null ? void 0 : _a.postMessage(serialized);
|
|
213
222
|
};
|
|
214
223
|
if (Platform.OS === "ios") {
|
|
215
|
-
requestAnimationFrame(
|
|
224
|
+
const handle = requestAnimationFrame(() => {
|
|
225
|
+
this.rafHandles.delete(handle);
|
|
226
|
+
post();
|
|
227
|
+
});
|
|
228
|
+
this.rafHandles.add(handle);
|
|
216
229
|
} else {
|
|
217
230
|
post();
|
|
218
231
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeWebViewBridge.js","sources":["../../src/bridge/NativeWebViewBridge.ts"],"sourcesContent":["import { Platform } from 'react-native';\nimport type { WebView, WebViewMessageEvent } from 'react-native-webview';\nimport type React from 'react';\nimport { CancellableRequest } from './CancellableRequest';\nimport { ChunkedMessageHandler } from './ChunkedMessageHandler';\nimport { BridgeDebugger } from './BridgeDebugger';\nimport {\n BRIDGE_PROTOCOL_VERSION,\n BRIDGE_READY_ACTION,\n DEFAULT_BRIDGE_TIMEOUT_MS,\n MessageType,\n type BridgeErrorPayload,\n type BridgeMessage,\n type BridgeResponse,\n} from './types';\n\ntype PendingRequest = {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n};\n\ntype BatchConfig = {\n batchSize: number;\n batchDelayMs: number;\n};\n\ntype BridgeListener = (payload: any) => void;\n\n/**\n * Native-side type-safe WebView bridge with READY queue, timeouts,\n * cancellation, chunking, analytics batching, and iOS/Android quirks.\n */\nexport class NativeWebViewBridge {\n private webViewRef: React.RefObject<WebView | null>;\n\n private pendingRequests = new Map<string, PendingRequest>();\n\n private messageQueue: BridgeMessage[] = [];\n\n private isReady = false;\n\n private readonly chunker = new ChunkedMessageHandler();\n\n private readonly debugger = new BridgeDebugger();\n\n private batch: BridgeMessage[] = [];\n\n private batchTimeout?: ReturnType<typeof setTimeout>;\n\n private readonly batchConfig: BatchConfig = { batchSize: 10, batchDelayMs: 100 };\n\n private readonly listeners = new Map<string, Set<BridgeListener>>();\n\n constructor(webViewRef: React.RefObject<WebView | null>) {\n this.webViewRef = webViewRef;\n }\n\n on(event: string, listener: BridgeListener): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n off(event: string, listener: BridgeListener): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n private emitLocal(event: string, payload?: unknown): void {\n const set = this.listeners.get(event);\n if (!set) return;\n set.forEach((listener) => {\n try {\n listener(payload);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`[Bridge] listener error for ${event}:`, error);\n }\n });\n }\n\n private removeAllListeners(): void {\n this.listeners.clear();\n }\n\n getDebugger(): BridgeDebugger {\n return this.debugger;\n }\n\n request<TRequest, TResponse>(\n action: string,\n payload: TRequest,\n timeoutMs = DEFAULT_BRIDGE_TIMEOUT_MS,\n ): CancellableRequest<TResponse> {\n const id = this.generateId();\n const message: BridgeMessage<TRequest> = {\n id,\n type: MessageType.REQUEST,\n action,\n payload,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const promise = new Promise<TResponse>((resolve, reject) => {\n timeoutId = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Request ${action} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout: timeoutId,\n });\n\n this.sendMessage(message);\n });\n\n const cancellable = new CancellableRequest<TResponse>(promise, () => {\n clearTimeout(timeoutId);\n this.pendingRequests.delete(id);\n this.sendEvent('request.cancelled', { requestId: id });\n });\n cancellable.addCleanup(() => clearTimeout(timeoutId));\n\n return cancellable;\n }\n\n /** One-way event toward the WebView (batches analytics.* / metrics.*). */\n sendEvent(action: string, payload?: unknown): void {\n const message: BridgeMessage = {\n id: this.generateId(),\n type: MessageType.EVENT,\n action,\n payload,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n\n if (this.shouldBatch(action)) {\n this.addToBatch(message);\n return;\n }\n\n this.sendMessage(message);\n }\n\n sendResponse(id: string, success: boolean, data?: unknown, error?: BridgeErrorPayload): void {\n const response: BridgeMessage = {\n id,\n type: MessageType.RESPONSE,\n action: 'response',\n payload: {\n id,\n success,\n data,\n error,\n } satisfies BridgeResponse,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n this.sendMessage(response);\n }\n\n handleMessage(event: WebViewMessageEvent): void {\n // Android may send string; iOS sometimes surfaces an object.\n const rawData = event.nativeEvent.data;\n const data = typeof rawData === 'string' ? rawData : JSON.stringify(rawData);\n\n try {\n const parsed: unknown = JSON.parse(data);\n\n if (this.chunker.isChunkEnvelope(parsed)) {\n const complete = this.chunker.handleChunk(parsed);\n if (complete) this.dispatchMessage(complete);\n return;\n }\n\n this.dispatchMessage(parsed as BridgeMessage);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Bridge] Failed to parse message:', error);\n }\n }\n\n onBridgeReady(): void {\n this.isReady = true;\n while (this.messageQueue.length > 0) {\n const message = this.messageQueue.shift();\n if (message) this.sendMessage(message);\n }\n }\n\n dispose(): void {\n this.flushBatch();\n this.pendingRequests.forEach((pending) => {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Bridge disposed'));\n });\n this.pendingRequests.clear();\n this.messageQueue = [];\n this.isReady = false;\n this.removeAllListeners();\n }\n\n private dispatchMessage(message: BridgeMessage): void {\n this.debugger.logReceived(message);\n\n switch (message.type) {\n case MessageType.RESPONSE:\n this.handleResponse(message);\n break;\n case MessageType.REQUEST:\n this.handleRequest(message);\n break;\n case MessageType.EVENT:\n this.handleEvent(message);\n break;\n case MessageType.ERROR:\n this.handleError(message);\n break;\n default:\n break;\n }\n }\n\n private handleResponse(message: BridgeMessage): void {\n const payload = message.payload as BridgeResponse | undefined;\n const responseId = payload?.id ?? message.id;\n const pending = this.pendingRequests.get(responseId);\n if (!pending) return;\n\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(responseId);\n\n const success = payload?.success ?? true;\n if (success) {\n pending.resolve(payload?.data !== undefined ? payload.data : message.payload);\n } else {\n pending.reject(new Error(payload?.error?.message || 'Unknown error'));\n }\n }\n\n private handleRequest(message: BridgeMessage): void {\n this.emitLocal(`request:${message.action}`, message);\n }\n\n private handleEvent(message: BridgeMessage): void {\n if (message.action === BRIDGE_READY_ACTION) {\n this.onBridgeReady();\n }\n if (message.action === 'batch' && Array.isArray(message.payload)) {\n (message.payload as BridgeMessage[]).forEach((inner) => this.dispatchMessage(inner));\n return;\n }\n this.emitLocal(`event:${message.action}`, message.payload);\n }\n\n private handleError(message: BridgeMessage): void {\n const pending = this.pendingRequests.get(message.id);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(message.id);\n const err = message.payload as BridgeErrorPayload | undefined;\n pending.reject(new Error(err?.message || 'Bridge error'));\n }\n this.emitLocal('bridge:error', message.payload);\n }\n\n private sendMessage(message: BridgeMessage): void {\n if (!this.isReady && message.action !== BRIDGE_READY_ACTION) {\n this.messageQueue.push(message);\n return;\n }\n\n this.debugger.logSent(message);\n\n const deliver = (serialized: string) => {\n const post = () => {\n this.webViewRef.current?.postMessage(serialized);\n };\n // iOS WebView is more reliable when postMessage is deferred one frame.\n if (Platform.OS === 'ios') {\n requestAnimationFrame(post);\n } else {\n post();\n }\n };\n\n this.chunker.sendChunked(message, deliver);\n\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log(`[Bridge] Sent: ${message.action}`, message);\n }\n }\n\n private shouldBatch(action: string): boolean {\n return action.startsWith('analytics.') || action.startsWith('metrics.');\n }\n\n private addToBatch(message: BridgeMessage): void {\n this.batch.push(message);\n if (this.batch.length >= this.batchConfig.batchSize) {\n this.flushBatch();\n } else if (!this.batchTimeout) {\n this.batchTimeout = setTimeout(() => this.flushBatch(), this.batchConfig.batchDelayMs);\n }\n }\n\n private flushBatch(): void {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout);\n this.batchTimeout = undefined;\n }\n if (this.batch.length === 0) return;\n\n const batchMessage: BridgeMessage = {\n id: this.generateId(),\n type: MessageType.EVENT,\n action: 'batch',\n payload: this.batch,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n this.batch = [];\n this.sendMessage(batchMessage);\n }\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NativeWebViewBridge.js","sources":["../../src/bridge/NativeWebViewBridge.ts"],"sourcesContent":["import { Platform } from 'react-native';\nimport type { WebView, WebViewMessageEvent } from 'react-native-webview';\nimport type React from 'react';\nimport { CancellableRequest } from './CancellableRequest';\nimport { ChunkedMessageHandler } from './ChunkedMessageHandler';\nimport { BridgeDebugger } from './BridgeDebugger';\nimport {\n BRIDGE_PROTOCOL_VERSION,\n BRIDGE_READY_ACTION,\n DEFAULT_BRIDGE_TIMEOUT_MS,\n MessageType,\n type BridgeErrorPayload,\n type BridgeMessage,\n type BridgeResponse,\n} from './types';\n\ntype PendingRequest = {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n};\n\ntype BatchConfig = {\n batchSize: number;\n batchDelayMs: number;\n};\n\ntype BridgeListener = (payload: any) => void;\n\n/**\n * Native-side type-safe WebView bridge with READY queue, timeouts,\n * cancellation, chunking, analytics batching, and iOS/Android quirks.\n */\nexport class NativeWebViewBridge {\n private webViewRef: React.RefObject<WebView | null>;\n\n private pendingRequests = new Map<string, PendingRequest>();\n\n private messageQueue: BridgeMessage[] = [];\n\n private isReady = false;\n\n private readonly chunker = new ChunkedMessageHandler();\n\n private readonly debugger = new BridgeDebugger();\n\n private batch: BridgeMessage[] = [];\n\n private batchTimeout?: ReturnType<typeof setTimeout>;\n\n private readonly batchConfig: BatchConfig = { batchSize: 10, batchDelayMs: 100 };\n\n private readonly listeners = new Map<string, Set<BridgeListener>>();\n\n private disposed = false;\n\n private readonly rafHandles = new Set<number>();\n\n constructor(webViewRef: React.RefObject<WebView | null>) {\n this.webViewRef = webViewRef;\n }\n\n on(event: string, listener: BridgeListener): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n return this;\n }\n\n off(event: string, listener: BridgeListener): this {\n this.listeners.get(event)?.delete(listener);\n return this;\n }\n\n private emitLocal(event: string, payload?: unknown): void {\n const set = this.listeners.get(event);\n if (!set) return;\n set.forEach((listener) => {\n try {\n listener(payload);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`[Bridge] listener error for ${event}:`, error);\n }\n });\n }\n\n private removeAllListeners(): void {\n this.listeners.clear();\n }\n\n getDebugger(): BridgeDebugger {\n return this.debugger;\n }\n\n request<TRequest, TResponse>(\n action: string,\n payload: TRequest,\n timeoutMs = DEFAULT_BRIDGE_TIMEOUT_MS,\n ): CancellableRequest<TResponse> {\n const id = this.generateId();\n const message: BridgeMessage<TRequest> = {\n id,\n type: MessageType.REQUEST,\n action,\n payload,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const promise = new Promise<TResponse>((resolve, reject) => {\n timeoutId = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(`Request ${action} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout: timeoutId,\n });\n\n this.sendMessage(message);\n });\n\n const cancellable = new CancellableRequest<TResponse>(promise, () => {\n clearTimeout(timeoutId);\n this.pendingRequests.delete(id);\n this.sendEvent('request.cancelled', { requestId: id });\n });\n cancellable.addCleanup(() => clearTimeout(timeoutId));\n\n return cancellable;\n }\n\n /** One-way event toward the WebView (batches analytics.* / metrics.*). */\n sendEvent(action: string, payload?: unknown): void {\n const message: BridgeMessage = {\n id: this.generateId(),\n type: MessageType.EVENT,\n action,\n payload,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n\n if (this.shouldBatch(action)) {\n this.addToBatch(message);\n return;\n }\n\n this.sendMessage(message);\n }\n\n sendResponse(id: string, success: boolean, data?: unknown, error?: BridgeErrorPayload): void {\n const response: BridgeMessage = {\n id,\n type: MessageType.RESPONSE,\n action: 'response',\n payload: {\n id,\n success,\n data,\n error,\n } satisfies BridgeResponse,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n this.sendMessage(response);\n }\n\n handleMessage(event: WebViewMessageEvent): void {\n if (this.disposed) return;\n // Android may send string; iOS sometimes surfaces an object.\n const rawData = event.nativeEvent.data;\n const data = typeof rawData === 'string' ? rawData : JSON.stringify(rawData);\n\n try {\n const parsed: unknown = JSON.parse(data);\n\n if (this.chunker.isChunkEnvelope(parsed)) {\n const complete = this.chunker.handleChunk(parsed);\n if (complete) this.dispatchMessage(complete);\n return;\n }\n\n this.dispatchMessage(parsed as BridgeMessage);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Bridge] Failed to parse message:', error);\n }\n }\n\n onBridgeReady(): void {\n this.isReady = true;\n while (this.messageQueue.length > 0) {\n const message = this.messageQueue.shift();\n if (message) this.sendMessage(message);\n }\n }\n\n dispose(): void {\n this.disposed = true;\n this.rafHandles.forEach((handle) => cancelAnimationFrame(handle));\n this.rafHandles.clear();\n this.flushBatch();\n this.pendingRequests.forEach((pending) => {\n clearTimeout(pending.timeout);\n pending.reject(new Error('Bridge disposed'));\n });\n this.pendingRequests.clear();\n this.messageQueue = [];\n this.isReady = false;\n this.removeAllListeners();\n }\n\n private dispatchMessage(message: BridgeMessage): void {\n this.debugger.logReceived(message);\n\n switch (message.type) {\n case MessageType.RESPONSE:\n this.handleResponse(message);\n break;\n case MessageType.REQUEST:\n this.handleRequest(message);\n break;\n case MessageType.EVENT:\n this.handleEvent(message);\n break;\n case MessageType.ERROR:\n this.handleError(message);\n break;\n default:\n break;\n }\n }\n\n private handleResponse(message: BridgeMessage): void {\n const payload = message.payload as BridgeResponse | undefined;\n const responseId = payload?.id ?? message.id;\n const pending = this.pendingRequests.get(responseId);\n if (!pending) return;\n\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(responseId);\n\n const success = payload?.success ?? true;\n if (success) {\n pending.resolve(payload?.data !== undefined ? payload.data : message.payload);\n } else {\n pending.reject(new Error(payload?.error?.message || 'Unknown error'));\n }\n }\n\n private handleRequest(message: BridgeMessage): void {\n this.emitLocal(`request:${message.action}`, message);\n }\n\n private handleEvent(message: BridgeMessage): void {\n if (message.action === BRIDGE_READY_ACTION) {\n this.onBridgeReady();\n }\n if (message.action === 'batch' && Array.isArray(message.payload)) {\n (message.payload as BridgeMessage[]).forEach((inner) => this.dispatchMessage(inner));\n return;\n }\n this.emitLocal(`event:${message.action}`, message.payload);\n }\n\n private handleError(message: BridgeMessage): void {\n const pending = this.pendingRequests.get(message.id);\n if (pending) {\n clearTimeout(pending.timeout);\n this.pendingRequests.delete(message.id);\n const err = message.payload as BridgeErrorPayload | undefined;\n pending.reject(new Error(err?.message || 'Bridge error'));\n }\n this.emitLocal('bridge:error', message.payload);\n }\n\n private sendMessage(message: BridgeMessage): void {\n if (this.disposed) return;\n if (!this.isReady && message.action !== BRIDGE_READY_ACTION) {\n this.messageQueue.push(message);\n return;\n }\n\n this.debugger.logSent(message);\n\n const deliver = (serialized: string) => {\n if (this.disposed) return;\n const post = () => {\n if (this.disposed) return;\n this.webViewRef.current?.postMessage(serialized);\n };\n // iOS WebView is more reliable when postMessage is deferred one frame.\n if (Platform.OS === 'ios') {\n const handle = requestAnimationFrame(() => {\n this.rafHandles.delete(handle);\n post();\n });\n this.rafHandles.add(handle);\n } else {\n post();\n }\n };\n\n this.chunker.sendChunked(message, deliver);\n\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log(`[Bridge] Sent: ${message.action}`, message);\n }\n }\n\n private shouldBatch(action: string): boolean {\n return action.startsWith('analytics.') || action.startsWith('metrics.');\n }\n\n private addToBatch(message: BridgeMessage): void {\n this.batch.push(message);\n if (this.batch.length >= this.batchConfig.batchSize) {\n this.flushBatch();\n } else if (!this.batchTimeout) {\n this.batchTimeout = setTimeout(() => this.flushBatch(), this.batchConfig.batchDelayMs);\n }\n }\n\n private flushBatch(): void {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout);\n this.batchTimeout = undefined;\n }\n if (this.batch.length === 0) return;\n\n const batchMessage: BridgeMessage = {\n id: this.generateId(),\n type: MessageType.EVENT,\n action: 'batch',\n payload: this.batch,\n timestamp: Date.now(),\n version: BRIDGE_PROTOCOL_VERSION,\n };\n this.batch = [];\n this.sendMessage(batchMessage);\n }\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,36 +1,42 @@
|
|
|
1
|
-
import {jsx}from'react/jsx-runtime';import {
|
|
1
|
+
import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {View,StyleSheet,Platform}from'react-native';import Animated,{useAnimatedKeyboard,useAnimatedStyle,KeyboardState}from'react-native-reanimated';const OPEN_GAP = 8;
|
|
2
|
+
function KeyboardAwareScreen({
|
|
3
|
+
children,
|
|
4
|
+
style
|
|
5
|
+
}) {
|
|
6
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.flex, style], children });
|
|
7
|
+
}
|
|
2
8
|
function KeyboardComposerDock({
|
|
3
9
|
children,
|
|
4
|
-
closedInset
|
|
5
|
-
keyboardVisible = false
|
|
10
|
+
closedInset
|
|
6
11
|
}) {
|
|
7
12
|
const keyboard = useAnimatedKeyboard();
|
|
8
13
|
const closedPadding = Math.max(closedInset, OPEN_GAP);
|
|
9
|
-
const
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
visible.value = keyboardVisible;
|
|
12
|
-
}, [keyboardVisible, visible]);
|
|
13
|
-
const dockStyle = useAnimatedStyle(() => {
|
|
14
|
+
const spacerStyle = useAnimatedStyle(() => {
|
|
14
15
|
if (Platform.OS === "android") {
|
|
15
|
-
return {};
|
|
16
|
-
}
|
|
17
|
-
if (!visible.value) {
|
|
18
16
|
return {
|
|
19
|
-
|
|
17
|
+
height: 0
|
|
20
18
|
};
|
|
21
19
|
}
|
|
20
|
+
const height = keyboard.state.value === KeyboardState.CLOSED ? 0 : keyboard.height.value;
|
|
21
|
+
const lift = height > 0 ? Math.max(0, height - closedPadding + OPEN_GAP) : 0;
|
|
22
22
|
return {
|
|
23
|
-
|
|
23
|
+
height: lift
|
|
24
24
|
};
|
|
25
25
|
});
|
|
26
|
-
return /* @__PURE__ */
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27
|
+
/* @__PURE__ */ jsx(View, { style: [styles.dock, {
|
|
28
|
+
paddingBottom: closedPadding
|
|
29
|
+
}], children }),
|
|
30
|
+
Platform.OS === "ios" ? /* @__PURE__ */ jsx(Animated.View, { style: spacerStyle }) : null
|
|
31
|
+
] });
|
|
29
32
|
}
|
|
30
33
|
const styles = StyleSheet.create({
|
|
34
|
+
flex: {
|
|
35
|
+
flex: 1
|
|
36
|
+
},
|
|
31
37
|
dock: {
|
|
32
38
|
width: "100%",
|
|
33
39
|
alignItems: "center",
|
|
34
40
|
zIndex: 20
|
|
35
41
|
}
|
|
36
|
-
});export{KeyboardComposerDock};//# sourceMappingURL=KeyboardComposerDock.js.map
|
|
42
|
+
});export{KeyboardAwareScreen,KeyboardComposerDock};//# sourceMappingURL=KeyboardComposerDock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyboardComposerDock.js","sources":["../../src/components/KeyboardComposerDock.tsx"],"sourcesContent":["import React
|
|
1
|
+
{"version":3,"file":"KeyboardComposerDock.js","sources":["../../src/components/KeyboardComposerDock.tsx"],"sourcesContent":["import React from 'react';\nimport { Platform, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';\nimport Animated, { KeyboardState, useAnimatedKeyboard, useAnimatedStyle } from 'react-native-reanimated';\n\nconst OPEN_GAP = 8;\n\nexport type KeyboardComposerDockProps = {\n children: React.ReactNode;\n /** Home-indicator inset applied when the keyboard is closed. */\n closedInset: number;\n /** Kept for call-site compatibility; lift is not gated on JS keyboard state. */\n keyboardVisible?: boolean;\n};\n\nexport function KeyboardAwareScreen({ children, style }: { children: React.ReactNode; style?: StyleProp<ViewStyle> }) {\n return <View style={[styles.flex, style]}>{children}</View>;\n}\n\n/**\n * Pins the composer above the keyboard in Expo Go and TestFlight.\n *\n * The TextInput lives in a dock with a *static* home-indicator padding. A\n * separate spacer below it tracks `useAnimatedKeyboard()` height, so the\n * field slides as a unit instead of being relaid out every frame (the blink).\n * Android `adjustResize` already shrinks the window, so the spacer is skipped.\n */\nexport function KeyboardComposerDock({ children, closedInset }: KeyboardComposerDockProps) {\n const keyboard = useAnimatedKeyboard();\n const closedPadding = Math.max(closedInset, OPEN_GAP);\n const spacerStyle = useAnimatedStyle(() => {\n if (Platform.OS === 'android') {\n return { height: 0 };\n }\n const height = keyboard.state.value === KeyboardState.CLOSED ? 0 : keyboard.height.value;\n const lift = height > 0 ? Math.max(0, height - closedPadding + OPEN_GAP) : 0;\n return { height: lift };\n });\n\n return (\n <>\n <View style={[styles.dock, { paddingBottom: closedPadding }]}>{children}</View>\n {Platform.OS === 'ios' ? <Animated.View style={spacerStyle} /> : null}\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n flex: {\n flex: 1,\n },\n dock: {\n width: '100%',\n alignItems: 'center',\n zIndex: 20,\n },\n});\n"],"names":[],"mappings":"wMAGA,MAAM,QAAW,GAAA,CAAA;AAQV,SAAS,mBAAoB,CAAA;AAAA,EAClC,QAAA;AAAA,EACA;AACF,CAGG,EAAA;AACD,EAAO,uBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,IAAM,EAAA,KAAK,GAAI,QAAS,EAAA,CAAA;AACtD;AAUO,SAAS,oBAAqB,CAAA;AAAA,EACnC,QAAA;AAAA,EACA;AACF,CAA8B,EAAA;AAC5B,EAAA,MAAM,WAAW,mBAAoB,EAAA;AACrC,EAAA,MAAM,aAAgB,GAAA,IAAA,CAAK,GAAI,CAAA,WAAA,EAAa,QAAQ,CAAA;AACpD,EAAM,MAAA,WAAA,GAAc,iBAAiB,MAAM;AACzC,IAAI,IAAA,QAAA,CAAS,OAAO,SAAW,EAAA;AAC7B,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,OACV;AAAA;AAEF,IAAM,MAAA,MAAA,GAAS,SAAS,KAAM,CAAA,KAAA,KAAU,cAAc,MAAS,GAAA,CAAA,GAAI,SAAS,MAAO,CAAA,KAAA;AACnF,IAAM,MAAA,IAAA,GAAO,SAAS,CAAI,GAAA,IAAA,CAAK,IAAI,CAAG,EAAA,MAAA,GAAS,aAAgB,GAAA,QAAQ,CAAI,GAAA,CAAA;AAC3E,IAAO,OAAA;AAAA,MACL,MAAQ,EAAA;AAAA,KACV;AAAA,GACD,CAAA;AACD,EAAA,uBACU,IAAA,CAAA,QAAA,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,CAAC,MAAA,CAAO,IAAM,EAAA;AAAA,MACjC,aAAe,EAAA;AAAA,KAChB,GAAI,QAAS,EAAA,CAAA;AAAA,IACL,QAAA,CAAS,OAAO,KAAQ,mBAAA,GAAA,CAAC,SAAS,IAAT,EAAA,EAAc,KAAO,EAAA,WAAA,EAAa,CAAK,GAAA;AAAA,GACrE,EAAA,CAAA;AACR;AACA,MAAM,MAAA,GAAS,WAAW,MAAO,CAAA;AAAA,EAC/B,IAAM,EAAA;AAAA,IACJ,IAAM,EAAA;AAAA,GACR;AAAA,EACA,IAAM,EAAA;AAAA,IACJ,KAAO,EAAA,MAAA;AAAA,IACP,UAAY,EAAA,QAAA;AAAA,IACZ,MAAQ,EAAA;AAAA;AAEZ,CAAC,CAAA"}
|
|
@@ -54,6 +54,15 @@ function AppsCatalog({
|
|
|
54
54
|
channelId,
|
|
55
55
|
appTitle: app.label
|
|
56
56
|
};
|
|
57
|
+
if (app.nativeRoute === "characters-native") {
|
|
58
|
+
navigation.dispatch(CommonActions.navigate({
|
|
59
|
+
name: "MainStack.CharactersNative",
|
|
60
|
+
params: __spreadProps(__spreadValues({}, params), {
|
|
61
|
+
manifestUrl: config.CHARACTERS_NATIVE_MANIFEST_URL
|
|
62
|
+
})
|
|
63
|
+
}));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
57
66
|
if (app.nativeRoute === "canvas-native") {
|
|
58
67
|
navigation.dispatch(CommonActions.navigate({
|
|
59
68
|
name: "MainStack.CanvasNative",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppsCatalog.js","sources":["../../../src/features/apps/AppsCatalog.tsx"],"sourcesContent":["/**\n * Full-screen Apps catalog: search + grouped tiles, then navigate into a\n * native surface or a new chat. Shared by the Apps screen (not a popup).\n */\nimport React, { useCallback, useMemo, useState } from 'react';\nimport { Pressable, ScrollView, StyleSheet, Text, TextInput, View, useColorScheme } from 'react-native';\nimport { CommonActions, useNavigation, useRoute } from '@react-navigation/native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { v4 as uuidv4 } from 'uuid';\nimport { config } from '../../config/env-config';\nimport { mobileTokens } from '../../theme/mobileTokens';\nimport { useChatMutations } from '../../hooks/useChatApi';\nimport { resolveSurfaceUrl } from './resolveSurfaceUrl';\nimport { groupMobileApps, MOBILE_APP_CATALOG, type MobileAppItem } from './mobileAppCatalog';\n\nfunction buildChannelTitle(channelId: string): string {\n return `chat-channel-${channelId}`;\n}\n\nexport function AppsCatalog({ orgName: orgNameProp }: { orgName?: string | null }) {\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n const colorScheme = useColorScheme();\n const isDark = colorScheme === 'dark';\n const { createChannel } = useChatMutations();\n const [query, setQuery] = useState('');\n\n const orgName = useMemo(() => {\n if (typeof orgNameProp === 'string' && orgNameProp.trim()) return orgNameProp.trim();\n const raw = (route?.params?.orgName as string | undefined) ?? '';\n return typeof raw === 'string' && raw.trim() ? raw.trim() : null;\n }, [orgNameProp, route?.params?.orgName]);\n\n const grouped = useMemo(() => groupMobileApps(MOBILE_APP_CATALOG, query), [query]);\n\n const muted = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n const foreground = isDark ? '#e2e8f0' : mobileTokens.color.text;\n const surface = isDark ? '#111827' : mobileTokens.color.surface;\n const screenBg = isDark ? '#0f172a' : mobileTokens.color.bg;\n const borderColor = isDark ? 'rgba(148,163,184,0.22)' : mobileTokens.color.border;\n const fieldBg = isDark ? '#1e293b' : mobileTokens.color.surfaceMuted;\n const accent = isDark ? '#a5b4fc' : mobileTokens.color.primary;\n const accentSoft = isDark ? 'rgba(99,102,241,0.18)' : mobileTokens.color.primarySoft;\n\n const openApp = useCallback(\n (app: MobileAppItem) => {\n const channelId = uuidv4();\n const params: Record<string, unknown> = {\n orgName: orgName ?? undefined,\n channelId,\n appTitle: app.label,\n };\n\n if (app.nativeRoute === 'canvas-native') {\n // Web canvas opens the boards grid first. Do not mint a chat\n // channelId here — that id is not an index row, and passing it\n // looks like a deep link into a board that does not exist.\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.CanvasNative',\n params: { orgName: orgName ?? undefined, appTitle: app.label },\n }),\n );\n return;\n }\n\n if (app.nativeRoute === 'webbuilder') {\n const surfaceUrl = resolveSurfaceUrl(app, config);\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.WebBuilder',\n params: surfaceUrl ? { ...params, url: surfaceUrl } : params,\n }),\n );\n return;\n }\n\n void createChannel({\n id: channelId,\n title: buildChannelTitle(channelId),\n isShared: false,\n sharedSlug: null,\n isArchived: false,\n isPinned: false,\n model: null,\n systemPrompt: null,\n orgName,\n } as any).catch((err) => {\n console.error('[AppsCatalog] createChannel failed:', err);\n });\n\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.Chat',\n params: {\n ...params,\n initialQuery: app.prompt,\n initialMode: 'chat',\n },\n }),\n );\n },\n [createChannel, navigation, orgName],\n );\n\n return (\n <View style={[styles.root, { backgroundColor: screenBg }]}>\n <View style={[styles.searchWrap, { backgroundColor: fieldBg, borderColor }]}>\n <MaterialCommunityIcons name=\"magnify\" size={18} color={muted} />\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search apps\"\n placeholderTextColor={muted}\n autoCorrect={false}\n autoCapitalize=\"none\"\n style={[styles.searchInput, { color: foreground }]}\n />\n </View>\n <ScrollView keyboardShouldPersistTaps=\"handled\" contentContainerStyle={styles.list}>\n {grouped.length === 0 ? (\n <Text style={[styles.empty, { color: muted }]}>No apps match that search.</Text>\n ) : (\n grouped.map((group) => (\n <View key={group.label} style={styles.group}>\n <Text style={[styles.groupLabel, { color: muted }]}>{group.label}</Text>\n <View style={styles.groupGrid}>\n {group.items.map((app) => (\n <Pressable\n key={app.key}\n onPress={() => openApp(app)}\n accessibilityRole=\"button\"\n accessibilityLabel={app.label}\n style={({ pressed }) => [\n styles.appTile,\n {\n backgroundColor: surface,\n borderColor,\n },\n pressed && { backgroundColor: accentSoft },\n ]}\n >\n <View style={[styles.appIcon, { backgroundColor: accentSoft }]}>\n <MaterialCommunityIcons name={app.icon} size={18} color={accent} />\n </View>\n <Text style={[styles.appLabel, { color: foreground }]} numberOfLines={2}>\n {app.label}\n </Text>\n </Pressable>\n ))}\n </View>\n </View>\n ))\n )}\n </ScrollView>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n },\n searchWrap: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n marginHorizontal: 16,\n marginTop: 12,\n marginBottom: 8,\n borderWidth: 1,\n borderRadius: 12,\n paddingHorizontal: 12,\n minHeight: 42,\n },\n searchInput: {\n flex: 1,\n fontSize: 15,\n paddingVertical: 8,\n },\n list: {\n paddingHorizontal: 16,\n paddingTop: 8,\n paddingBottom: 28,\n gap: 16,\n },\n group: {\n gap: 8,\n },\n groupLabel: {\n fontSize: 11,\n fontWeight: '600',\n letterSpacing: 0.7,\n textTransform: 'uppercase',\n },\n groupGrid: {\n flexDirection: 'row',\n flexWrap: 'wrap',\n gap: 8,\n },\n appTile: {\n width: '31%',\n flexGrow: 1,\n minWidth: 96,\n maxWidth: '32.5%',\n borderWidth: 1,\n borderRadius: 14,\n paddingHorizontal: 10,\n paddingVertical: 12,\n gap: 8,\n },\n appIcon: {\n width: 32,\n height: 32,\n borderRadius: 10,\n alignItems: 'center',\n justifyContent: 'center',\n },\n appLabel: {\n fontSize: 13,\n fontWeight: '600',\n lineHeight: 17,\n },\n empty: {\n textAlign: 'center',\n paddingVertical: 28,\n fontSize: 14,\n },\n});\n"],"names":["_a","uuidv4"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AppsCatalog.js","sources":["../../../src/features/apps/AppsCatalog.tsx"],"sourcesContent":["/**\n * Full-screen Apps catalog: search + grouped tiles, then navigate into a\n * native surface or a new chat. Shared by the Apps screen (not a popup).\n */\nimport React, { useCallback, useMemo, useState } from 'react';\nimport { Pressable, ScrollView, StyleSheet, Text, TextInput, View, useColorScheme } from 'react-native';\nimport { CommonActions, useNavigation, useRoute } from '@react-navigation/native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { v4 as uuidv4 } from 'uuid';\nimport { config } from '../../config/env-config';\nimport { mobileTokens } from '../../theme/mobileTokens';\nimport { useChatMutations } from '../../hooks/useChatApi';\nimport { resolveSurfaceUrl } from './resolveSurfaceUrl';\nimport { groupMobileApps, MOBILE_APP_CATALOG, type MobileAppItem } from './mobileAppCatalog';\n\nfunction buildChannelTitle(channelId: string): string {\n return `chat-channel-${channelId}`;\n}\n\nexport function AppsCatalog({ orgName: orgNameProp }: { orgName?: string | null }) {\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n const colorScheme = useColorScheme();\n const isDark = colorScheme === 'dark';\n const { createChannel } = useChatMutations();\n const [query, setQuery] = useState('');\n\n const orgName = useMemo(() => {\n if (typeof orgNameProp === 'string' && orgNameProp.trim()) return orgNameProp.trim();\n const raw = (route?.params?.orgName as string | undefined) ?? '';\n return typeof raw === 'string' && raw.trim() ? raw.trim() : null;\n }, [orgNameProp, route?.params?.orgName]);\n\n const grouped = useMemo(() => groupMobileApps(MOBILE_APP_CATALOG, query), [query]);\n\n const muted = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n const foreground = isDark ? '#e2e8f0' : mobileTokens.color.text;\n const surface = isDark ? '#111827' : mobileTokens.color.surface;\n const screenBg = isDark ? '#0f172a' : mobileTokens.color.bg;\n const borderColor = isDark ? 'rgba(148,163,184,0.22)' : mobileTokens.color.border;\n const fieldBg = isDark ? '#1e293b' : mobileTokens.color.surfaceMuted;\n const accent = isDark ? '#a5b4fc' : mobileTokens.color.primary;\n const accentSoft = isDark ? 'rgba(99,102,241,0.18)' : mobileTokens.color.primarySoft;\n\n const openApp = useCallback(\n (app: MobileAppItem) => {\n const channelId = uuidv4();\n const params: Record<string, unknown> = {\n orgName: orgName ?? undefined,\n channelId,\n appTitle: app.label,\n };\n\n if (app.nativeRoute === 'characters-native') {\n // Same wiring as AppsMenu: the tile opens the native character\n // studio, never a chat with the tile's prompt.\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.CharactersNative',\n params: {\n ...params,\n manifestUrl: config.CHARACTERS_NATIVE_MANIFEST_URL,\n },\n }),\n );\n return;\n }\n\n if (app.nativeRoute === 'canvas-native') {\n // Web canvas opens the boards grid first. Do not mint a chat\n // channelId here — that id is not an index row, and passing it\n // looks like a deep link into a board that does not exist.\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.CanvasNative',\n params: { orgName: orgName ?? undefined, appTitle: app.label },\n }),\n );\n return;\n }\n\n if (app.nativeRoute === 'webbuilder') {\n const surfaceUrl = resolveSurfaceUrl(app, config);\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.WebBuilder',\n params: surfaceUrl ? { ...params, url: surfaceUrl } : params,\n }),\n );\n return;\n }\n\n void createChannel({\n id: channelId,\n title: buildChannelTitle(channelId),\n isShared: false,\n sharedSlug: null,\n isArchived: false,\n isPinned: false,\n model: null,\n systemPrompt: null,\n orgName,\n } as any).catch((err) => {\n console.error('[AppsCatalog] createChannel failed:', err);\n });\n\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.Chat',\n params: {\n ...params,\n initialQuery: app.prompt,\n initialMode: 'chat',\n },\n }),\n );\n },\n [createChannel, navigation, orgName],\n );\n\n return (\n <View style={[styles.root, { backgroundColor: screenBg }]}>\n <View style={[styles.searchWrap, { backgroundColor: fieldBg, borderColor }]}>\n <MaterialCommunityIcons name=\"magnify\" size={18} color={muted} />\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search apps\"\n placeholderTextColor={muted}\n autoCorrect={false}\n autoCapitalize=\"none\"\n style={[styles.searchInput, { color: foreground }]}\n />\n </View>\n <ScrollView keyboardShouldPersistTaps=\"handled\" contentContainerStyle={styles.list}>\n {grouped.length === 0 ? (\n <Text style={[styles.empty, { color: muted }]}>No apps match that search.</Text>\n ) : (\n grouped.map((group) => (\n <View key={group.label} style={styles.group}>\n <Text style={[styles.groupLabel, { color: muted }]}>{group.label}</Text>\n <View style={styles.groupGrid}>\n {group.items.map((app) => (\n <Pressable\n key={app.key}\n onPress={() => openApp(app)}\n accessibilityRole=\"button\"\n accessibilityLabel={app.label}\n style={({ pressed }) => [\n styles.appTile,\n {\n backgroundColor: surface,\n borderColor,\n },\n pressed && { backgroundColor: accentSoft },\n ]}\n >\n <View style={[styles.appIcon, { backgroundColor: accentSoft }]}>\n <MaterialCommunityIcons name={app.icon} size={18} color={accent} />\n </View>\n <Text style={[styles.appLabel, { color: foreground }]} numberOfLines={2}>\n {app.label}\n </Text>\n </Pressable>\n ))}\n </View>\n </View>\n ))\n )}\n </ScrollView>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n },\n searchWrap: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n marginHorizontal: 16,\n marginTop: 12,\n marginBottom: 8,\n borderWidth: 1,\n borderRadius: 12,\n paddingHorizontal: 12,\n minHeight: 42,\n },\n searchInput: {\n flex: 1,\n fontSize: 15,\n paddingVertical: 8,\n },\n list: {\n paddingHorizontal: 16,\n paddingTop: 8,\n paddingBottom: 28,\n gap: 16,\n },\n group: {\n gap: 8,\n },\n groupLabel: {\n fontSize: 11,\n fontWeight: '600',\n letterSpacing: 0.7,\n textTransform: 'uppercase',\n },\n groupGrid: {\n flexDirection: 'row',\n flexWrap: 'wrap',\n gap: 8,\n },\n appTile: {\n width: '31%',\n flexGrow: 1,\n minWidth: 96,\n maxWidth: '32.5%',\n borderWidth: 1,\n borderRadius: 14,\n paddingHorizontal: 10,\n paddingVertical: 12,\n gap: 8,\n },\n appIcon: {\n width: 32,\n height: 32,\n borderRadius: 10,\n alignItems: 'center',\n justifyContent: 'center',\n },\n appLabel: {\n fontSize: 13,\n fontWeight: '600',\n lineHeight: 17,\n },\n empty: {\n textAlign: 'center',\n paddingVertical: 28,\n fontSize: 14,\n },\n});\n"],"names":["_a","uuidv4"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {FlatList,StyleSheet,View,Text}from'react-native';import Markdown from'react-native-markdown-display';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';var __defProp = Object.defineProperty;
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {Keyboard,FlatList,StyleSheet,View,Text}from'react-native';import Markdown from'react-native-markdown-display';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -23,8 +23,7 @@ function ChatTranscript({
|
|
|
23
23
|
streamingContent,
|
|
24
24
|
renderMessageActions,
|
|
25
25
|
listContentStyle,
|
|
26
|
-
isDark = false
|
|
27
|
-
keyboardVisible = false
|
|
26
|
+
isDark = false
|
|
28
27
|
}) {
|
|
29
28
|
const listRef = useRef(null);
|
|
30
29
|
const stickToBottomRef = useRef(true);
|
|
@@ -56,9 +55,12 @@ function ChatTranscript({
|
|
|
56
55
|
return () => clearTimeout(t);
|
|
57
56
|
}, [rows.length, streamingContent == null ? void 0 : streamingContent.length, scrollToLatest]);
|
|
58
57
|
useEffect(() => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
const show = Keyboard.addListener("keyboardDidShow", () => {
|
|
59
|
+
if (!stickToBottomRef.current) return;
|
|
60
|
+
scrollToLatest(true);
|
|
61
|
+
});
|
|
62
|
+
return () => show.remove();
|
|
63
|
+
}, [scrollToLatest]);
|
|
62
64
|
const handleScroll = useCallback((e) => {
|
|
63
65
|
const {
|
|
64
66
|
contentOffset,
|
|
@@ -79,7 +81,7 @@ function ChatTranscript({
|
|
|
79
81
|
const renderItem = useCallback(({
|
|
80
82
|
item
|
|
81
83
|
}) => /* @__PURE__ */ jsx(TranscriptRowView, { item, isDark, renderMessageActions: item.streaming ? void 0 : renderMessageActions }), [isDark, renderMessageActions]);
|
|
82
|
-
return /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: rows, keyExtractor: (item) => item.id, renderItem, extraData:
|
|
84
|
+
return /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: rows, keyExtractor: (item) => item.id, renderItem, extraData: streamingContent, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 7, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
|
|
83
85
|
if (stickToBottomRef.current) scrollToLatest(false);
|
|
84
86
|
}, scrollEventThrottle: 16, keyboardShouldPersistTaps: "always", keyboardDismissMode: "none", automaticallyAdjustKeyboardInsets: false, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", contentContainerStyle: [styles.listContent, listContentStyle], style: styles.list });
|
|
85
87
|
}
|
|
@@ -102,7 +104,9 @@ const TranscriptRowView = memo(function TranscriptRowView2({
|
|
|
102
104
|
const codeBg = isDark ? "#1e293b" : "#f3f4f6";
|
|
103
105
|
return /* @__PURE__ */ jsxs(View, { style: styles.assistantCol, children: [
|
|
104
106
|
attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "start", isDark }) : null,
|
|
105
|
-
text ? /* @__PURE__ */ jsx(
|
|
107
|
+
text ? item.streaming ? /* @__PURE__ */ jsx(Text, { style: [styles.assistantStreamText, {
|
|
108
|
+
color: markdownColor
|
|
109
|
+
}], children: text }) : /* @__PURE__ */ jsx(Markdown, { style: {
|
|
106
110
|
body: {
|
|
107
111
|
color: markdownColor,
|
|
108
112
|
fontSize: 15,
|
|
@@ -199,5 +203,9 @@ const styles = StyleSheet.create({
|
|
|
199
203
|
alignItems: "flex-start",
|
|
200
204
|
marginBottom: 8,
|
|
201
205
|
paddingHorizontal: 4
|
|
206
|
+
},
|
|
207
|
+
assistantStreamText: {
|
|
208
|
+
fontSize: 15,
|
|
209
|
+
lineHeight: 22
|
|
202
210
|
}
|
|
203
211
|
});export{ChatTranscript};//# sourceMappingURL=ChatTranscript.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { FlatList, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean };\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n keyboardVisible = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n isDark?: boolean;\n keyboardVisible?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptRow>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n\n const historyRows = useMemo<TranscriptRow[]>(\n () =>\n messages\n .filter((msg) => msg.role !== 'user' || shouldRenderUserTranscriptTurn(msg.content, msg.attachments))\n .map((msg) => ({ ...msg, streaming: false })),\n [messages],\n );\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const stream = streamingContent?.trim();\n if (!stream) return historyRows;\n return [\n ...historyRows,\n {\n id: '__streaming__',\n role: 'assistant',\n content: stream,\n streaming: true,\n },\n ];\n }, [historyRows, streamingContent]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (rows.length === 0) return;\n isProgrammaticScrollRef.current = true;\n listRef.current?.scrollToEnd({ animated });\n },\n [rows.length],\n );\n\n useEffect(() => {\n if (rows.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [rows.length, streamingContent?.length, scrollToLatest]);\n\n useEffect(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [keyboardVisible, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptRow }) => (\n <TranscriptRowView\n item={item}\n isDark={isDark}\n renderMessageActions={item.streaming ? undefined : renderMessageActions}\n />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <FlatList\n ref={listRef}\n data={rows}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={`${isDark}:${streamingContent ?? ''}`}\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"always\"\n keyboardDismissMode=\"none\"\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle]}\n style={styles.list}\n />\n );\n}\n\nconst TranscriptRowView = memo(function TranscriptRowView({\n item,\n isDark,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const isUser = item.role === 'user';\n const attachments = item.attachments ?? [];\n const text = isUser ? displayUserMessageText(item.content, attachments) : item.content;\n\n if (isUser) {\n return (\n <View style={styles.userCol}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={styles.userBubble}>\n <Text style={styles.userText}>{text}</Text>\n </View>\n ) : null}\n </View>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\n\n return (\n <View style={styles.assistantCol}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {text ? (\n <Markdown\n style={{\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700',\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700',\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600',\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n }}\n >\n {text}\n </Markdown>\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </View>\n );\n});\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n padding: 12,\n maxWidth: '92%',\n },\n userText: {\n color: '#ffffff',\n fontSize: 15,\n lineHeight: 22,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n});\n"],"names":["TranscriptRowView"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { FlatList, Keyboard, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean };\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptRow>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n\n const historyRows = useMemo<TranscriptRow[]>(\n () =>\n messages\n .filter((msg) => msg.role !== 'user' || shouldRenderUserTranscriptTurn(msg.content, msg.attachments))\n .map((msg) => ({ ...msg, streaming: false })),\n [messages],\n );\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const stream = streamingContent?.trim();\n if (!stream) return historyRows;\n return [\n ...historyRows,\n {\n id: '__streaming__',\n role: 'assistant',\n content: stream,\n streaming: true,\n },\n ];\n }, [historyRows, streamingContent]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (rows.length === 0) return;\n isProgrammaticScrollRef.current = true;\n listRef.current?.scrollToEnd({ animated });\n },\n [rows.length],\n );\n\n useEffect(() => {\n if (rows.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [rows.length, streamingContent?.length, scrollToLatest]);\n\n useEffect(() => {\n const show = Keyboard.addListener('keyboardDidShow', () => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(true);\n });\n return () => show.remove();\n }, [scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptRow }) => (\n <TranscriptRowView\n item={item}\n isDark={isDark}\n renderMessageActions={item.streaming ? undefined : renderMessageActions}\n />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <FlatList\n ref={listRef}\n data={rows}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={streamingContent}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"always\"\n keyboardDismissMode=\"none\"\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle]}\n style={styles.list}\n />\n );\n}\n\nconst TranscriptRowView = memo(function TranscriptRowView({\n item,\n isDark,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const isUser = item.role === 'user';\n const attachments = item.attachments ?? [];\n const text = isUser ? displayUserMessageText(item.content, attachments) : item.content;\n\n if (isUser) {\n return (\n <View style={styles.userCol}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={styles.userBubble}>\n <Text style={styles.userText}>{text}</Text>\n </View>\n ) : null}\n </View>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\n\n return (\n <View style={styles.assistantCol}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {text ? (\n item.streaming ? (\n <Text style={[styles.assistantStreamText, { color: markdownColor }]}>{text}</Text>\n ) : (\n <Markdown\n style={{\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700',\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700',\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600',\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n }}\n >\n {text}\n </Markdown>\n )\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </View>\n );\n});\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n padding: 12,\n maxWidth: '92%',\n },\n userText: {\n color: '#ffffff',\n fontSize: 15,\n lineHeight: 22,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantStreamText: {\n fontSize: 15,\n lineHeight: 22,\n },\n});\n"],"names":["TranscriptRowView"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skillTemplates.js","sources":["../../../src/features/skills/skillTemplates.ts"],"sourcesContent":["/**\n * Skill templates - the mobile twin of the browser's\n * `services/skillTemplates.ts`. The SkillTemplate shape is the CONTRACT\n * (HeaderSkillsButton hands one to cdecli connect + agentSessionManager) and\n * must not drift from the web's.\n *\n * Mobile
|
|
1
|
+
{"version":3,"file":"skillTemplates.js","sources":["../../../src/features/skills/skillTemplates.ts"],"sourcesContent":["/**\n * Skill templates - the mobile twin of the browser's\n * `services/skillTemplates.ts`. The SkillTemplate shape is the CONTRACT\n * (HeaderSkillsButton hands one to cdecli connect + agentSessionManager) and\n * must not drift from the web's.\n *\n * Mobile ships the built-in templates plus the browser's pure helpers\n * (parseFormToSkillTemplate/searchTemplates) so useSkillTemplates can merge\n * marketplace `aiTemplates` the same way the web does. Custom templates have\n * no on-device store yet: loadCustomTemplates answers [] until a mobile\n * storage owner exists (web persists them to localStorage).\n */\n\nexport interface SkillTemplate {\n id: string;\n name: string;\n description: string;\n icon: string;\n /** SOUL.md content - agent persona/instructions */\n soul: string;\n /** Recommended model (e.g. 'anthropic/claude-opus-4-6') */\n model?: string;\n /** Skills that should be enabled for this template */\n requiredSkills?: string[];\n /** Category for UI grouping */\n category: 'coding' | 'research' | 'automation' | 'creative' | 'data' | 'general';\n /** Tags for search/filtering */\n tags: string[];\n /**\n * Shipped with the app (not backed by a marketplace form_table row) - the\n * template's `id` is a connector slug, so form_table lookups must be\n * skipped.\n */\n builtin?: boolean;\n}\n\nexport const BUILTIN_SKILL_TEMPLATES: SkillTemplate[] = [\n {\n id: 'yantra-github-search',\n name: 'GitHub Research',\n description:\n 'Search and analyze GitHub repositories. Ask a question and get an AI-powered summary of the most relevant open-source projects.',\n icon: '🔍',\n soul: 'You are a GitHub research assistant powered by the yantra-github-search connector. When the user provides a topic or question, search GitHub for relevant repositories, analyze the top results, and return a concise summary with key findings and citations.',\n model: undefined,\n requiredSkills: ['yantra-github-search'],\n category: 'research',\n tags: ['github', 'search', 'repositories', 'open-source', 'analysis'],\n builtin: true,\n },\n];\n\n/** User-custom templates: no on-device store yet (web uses localStorage). */\nexport function loadCustomTemplates(): SkillTemplate[] {\n return [];\n}\n\n/** Parse a form entry from aiTemplates into a SkillTemplate (browser twin). */\nexport function parseFormToSkillTemplate(\n formKey: string,\n form: Record<string, unknown>,\n parentGroup: Record<string, unknown> = {},\n): SkillTemplate | null {\n if (!form.title && !form.formTemplateId) return null;\n\n const rawTags = form.tags as Record<string, unknown> | undefined;\n const tags: string[] = rawTags ? Object.values(rawTags).filter((v): v is string => typeof v === 'string') : [];\n\n const rawSkills = form.requiredSkills as Record<string, unknown> | undefined;\n const requiredSkills: string[] = rawSkills\n ? Object.values(rawSkills).filter((v): v is string => typeof v === 'string')\n : [];\n\n const validCategories = new Set(['coding', 'research', 'automation', 'creative', 'data', 'general']);\n const rawCategory = (form.category as string) || '';\n const category: SkillTemplate['category'] = validCategories.has(rawCategory)\n ? (rawCategory as SkillTemplate['category'])\n : 'general';\n\n return {\n id: (form.formTemplateId as string) || formKey,\n name: (form.title as string) || formKey,\n description: (form.description as string) || (parentGroup.description as string) || '',\n icon: (form.icon as string) || (parentGroup.formIcon as string) || '🌟',\n soul: (form.soul as string) || '',\n model: (form.model as string) || undefined,\n requiredSkills: requiredSkills.length > 0 ? requiredSkills : undefined,\n category,\n tags,\n };\n}\n\n/** Search templates by name, description, or tags (browser twin). */\nexport function searchTemplates(templates: SkillTemplate[], query: string): SkillTemplate[] {\n const q = query.toLowerCase();\n return templates.filter(\n (t) =>\n t.name.toLowerCase().includes(q) ||\n t.description.toLowerCase().includes(q) ||\n t.tags.some((tag) => tag.includes(q)),\n );\n}\n"],"names":[],"mappings":"AAmCO,MAAM,0BAA2C,CAAC;AAAA,EACvD,EAAI,EAAA,sBAAA;AAAA,EACJ,IAAM,EAAA,iBAAA;AAAA,EACN,WAAa,EAAA,iIAAA;AAAA,EACb,IAAM,EAAA,WAAA;AAAA,EACN,IAAM,EAAA,gQAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,cAAA,EAAgB,CAAC,sBAAsB,CAAA;AAAA,EACvC,QAAU,EAAA,UAAA;AAAA,EACV,MAAM,CAAC,QAAA,EAAU,QAAU,EAAA,cAAA,EAAgB,eAAe,UAAU,CAAA;AAAA,EACpE,OAAS,EAAA;AACX,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useState,useRef,useCallback,useMemo
|
|
1
|
+
import {useState,useRef,useCallback,useEffect,useMemo}from'react';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {useChatMutations,useChatMessages}from'./useChatApi.js';import {useCdecliChannel,isNoActiveSessionError}from'./useCdecliChannel.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {buildGatewayMediaPreferUrls}from'../features/attachments/buildGatewayMedia.js';import {preserveAttachmentPreviews}from'../features/attachments/preserveAttachmentPreviews.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -45,6 +45,23 @@ function useChatStream(sessionId, routing = {
|
|
|
45
45
|
const cdecliRoundHandledRef = useRef(false);
|
|
46
46
|
const routingRef = useRef(routing);
|
|
47
47
|
routingRef.current = routing;
|
|
48
|
+
const pendingStreamRef = useRef("");
|
|
49
|
+
const streamFlushTimerRef = useRef(null);
|
|
50
|
+
const clearPendingStream = useCallback(() => {
|
|
51
|
+
if (streamFlushTimerRef.current != null) {
|
|
52
|
+
clearTimeout(streamFlushTimerRef.current);
|
|
53
|
+
streamFlushTimerRef.current = null;
|
|
54
|
+
}
|
|
55
|
+
pendingStreamRef.current = "";
|
|
56
|
+
}, []);
|
|
57
|
+
const flushPendingStream = useCallback(() => {
|
|
58
|
+
streamFlushTimerRef.current = null;
|
|
59
|
+
const pending = pendingStreamRef.current;
|
|
60
|
+
if (!pending) return;
|
|
61
|
+
pendingStreamRef.current = "";
|
|
62
|
+
setResponse((r) => r + pending);
|
|
63
|
+
}, []);
|
|
64
|
+
useEffect(() => () => clearPendingStream(), [clearPendingStream]);
|
|
48
65
|
const {
|
|
49
66
|
saveMessages
|
|
50
67
|
} = useChatMutations();
|
|
@@ -61,6 +78,7 @@ function useChatStream(sessionId, routing = {
|
|
|
61
78
|
const lastHydratedSessionIdRef = useRef(null);
|
|
62
79
|
const streamChannelId = sessionId || cdecliStreamOverrideId;
|
|
63
80
|
const finalizeCdecliRound = useCallback((assistantContent) => {
|
|
81
|
+
clearPendingStream();
|
|
64
82
|
saveSessionIdRef.current = null;
|
|
65
83
|
userMsgForSaveRef.current = null;
|
|
66
84
|
sessionLiveRef.current = false;
|
|
@@ -74,17 +92,21 @@ function useChatStream(sessionId, routing = {
|
|
|
74
92
|
content: assistantContent
|
|
75
93
|
}]);
|
|
76
94
|
}
|
|
77
|
-
}, []);
|
|
95
|
+
}, [clearPendingStream]);
|
|
78
96
|
const cdecliCallbacks = useMemo(() => ({
|
|
79
97
|
onChunk: (text) => {
|
|
80
|
-
|
|
98
|
+
pendingStreamRef.current += text;
|
|
81
99
|
if (!sessionLiveRef.current) {
|
|
82
100
|
sessionLiveRef.current = true;
|
|
83
101
|
void flushPendingSteersRef.current();
|
|
84
102
|
}
|
|
103
|
+
if (streamFlushTimerRef.current == null) {
|
|
104
|
+
streamFlushTimerRef.current = setTimeout(flushPendingStream, 50);
|
|
105
|
+
}
|
|
85
106
|
},
|
|
86
107
|
onComplete: (text) => {
|
|
87
108
|
var _a;
|
|
109
|
+
clearPendingStream();
|
|
88
110
|
if (cdecliRoundHandledRef.current) {
|
|
89
111
|
return;
|
|
90
112
|
}
|
|
@@ -154,7 +176,7 @@ function useChatStream(sessionId, routing = {
|
|
|
154
176
|
setError(err || "CDeCLI stream error.");
|
|
155
177
|
finalizeCdecliRound();
|
|
156
178
|
}
|
|
157
|
-
}), [saveMessages, accountUserId, finalizeCdecliRound]);
|
|
179
|
+
}), [saveMessages, accountUserId, finalizeCdecliRound, flushPendingStream, clearPendingStream]);
|
|
158
180
|
const {
|
|
159
181
|
sendMessage: sendCdecliMessage,
|
|
160
182
|
steer: steerCdecliMessage,
|
|
@@ -318,6 +340,7 @@ function useChatStream(sessionId, routing = {
|
|
|
318
340
|
sendMessageRef.current = sendMessage;
|
|
319
341
|
const cancel = useCallback(() => {
|
|
320
342
|
var _a;
|
|
343
|
+
clearPendingStream();
|
|
321
344
|
pendingSteersRef.current = [];
|
|
322
345
|
sessionLiveRef.current = false;
|
|
323
346
|
const sid = (_a = saveSessionIdRef.current) != null ? _a : sessionId;
|
|
@@ -326,12 +349,13 @@ function useChatStream(sessionId, routing = {
|
|
|
326
349
|
cdecliRoundHandledRef.current = true;
|
|
327
350
|
finalizeCdecliRound();
|
|
328
351
|
}
|
|
329
|
-
}, [cancelCdecliTurn, finalizeCdecliRound, sessionId]);
|
|
352
|
+
}, [cancelCdecliTurn, finalizeCdecliRound, sessionId, clearPendingStream]);
|
|
330
353
|
const clearMessages = useCallback(() => {
|
|
354
|
+
clearPendingStream();
|
|
331
355
|
setMessages([]);
|
|
332
356
|
setResponse("");
|
|
333
357
|
setError(null);
|
|
334
|
-
}, []);
|
|
358
|
+
}, [clearPendingStream]);
|
|
335
359
|
const messagesLoading = !!sessionId && backendMessagesLoading && !backendMessagesLoaded;
|
|
336
360
|
return {
|
|
337
361
|
messages,
|