From aed94ff6bc887329087b8c1b5cfe463acd6c1c5f Mon Sep 17 00:00:00 2001 From: tiye Date: Thu, 17 Sep 2026 15:19:13 +0800 Subject: [PATCH] chore: upgrade Calcit and enforce strict checks --- .github/workflows/upload.yaml | 8 +- calcit.cirru | 1323 ++++++++++++++------------------- config/calcit-quality.cirru | 270 +++---- deps.cirru | 15 +- package.json | 6 +- yarn.lock | 10 +- 6 files changed, 692 insertions(+), 940 deletions(-) diff --git a/.github/workflows/upload.yaml b/.github/workflows/upload.yaml index 5da8a6b..96f739d 100644 --- a/.github/workflows/upload.yaml +++ b/.github/workflows/upload.yaml @@ -50,22 +50,22 @@ jobs: git diff --exit-code -- calcit.cirru - name: Check Calcit types - run: calcit calcit.cirru --compat-types --check-only + run: calcit calcit.cirru --check-only - name: Check application dynamic methods - run: calcit calcit.cirru --compat-types analyze dynamic-methods --max 0 --format json + run: calcit calcit.cirru analyze dynamic-methods --max 0 --format json - name: Check deprecated Calcit APIs run: calcit calcit.cirru analyze deprecated --summary-only - name: Audit provider dynamic methods - run: calcit calcit.cirru --compat-types analyze dynamic-methods --deps --format json + run: calcit calcit.cirru analyze dynamic-methods --deps --format json - name: Check Calcit quality baseline run: calcit calcit.cirru analyze quality --baseline config/calcit-quality.cirru - name: Run Calcit regression tests - run: calcit calcit.cirru --compat-types test --summary-only --format json + run: calcit calcit.cirru test --require-match --summary-only --format json - name: Build extension package run: yarn build diff --git a/calcit.cirru b/calcit.cirru index 1e1169a..63a924e 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -3,8 +3,7 @@ :about "|Machine-generated snapshot. Do not edit directly — changes will be overwritten. Use `calcit query` to inspect and `calcit edit`/`calcit tree` to modify. Run `calcit docs agents --contract` before mutations; use `--full` for first orientation or changed contract digest. Manual edits must follow format and schema conventions, then run `calcit edit format`." :package |app :entries $ {} $ :default - {} (:description |) (:init-fn 'app.main/main!) (:mode :js) - :reload-fn 'app.main/reload! + {} (:description |) (:init-fn 'app.main/main!) (:mode :js) (:reload-fn 'app.main/reload!) :feature-policy $ {} :modules $ [] |respo.calcit/ |respo-ui.calcit/ |reel.calcit/ |respo-markdown.calcit/ |alerts.calcit/ :type-slots $ {} @@ -16,9 +15,9 @@ :examples $ [] :schema $ :: 'Ref '*archived-sessions $ %{} 'CodeEntry (:doc |) - :code $ quote $ defatom *archived-sessions false + :code $ quote $ defatom *archived-sessions nil :examples $ [] - :schema $ :: 'Ref + :schema $ :: 'Ref $ :: 'JsNullish (:: 'List 'app.schema/ChatSession) '*gen-ai-new $ %{} 'CodeEntry (:doc |) :code $ quote $ defatom *gen-ai-new false :examples $ [] @@ -27,15 +26,14 @@ :code $ quote $ defatom *image-cache false :examples $ [] :schema $ :: 'Ref - '*openai $ %{} 'CodeEntry - :doc "|called openai sdk, but actually for openrouter" + '*openai $ %{} 'CodeEntry (:doc "|called openai sdk, but actually for openrouter") :code $ quote $ defatom *openai false :examples $ [] :schema $ :: 'Ref '*viewing-archive-session $ %{} 'CodeEntry (:doc |) - :code $ quote $ defatom *viewing-archive-session false + :code $ quote $ defatom *viewing-archive-session nil :examples $ [] - :schema $ :: 'Ref + :schema $ :: 'Ref $ :: 'JsNullish 'app.schema/ChatSession 'DomTokenListHost $ %{} 'CodeEntry (:doc |) :code $ quote $ deftrait DomTokenListHost .contains? $ :: 'Fn $ {} @@ -68,26 +66,26 @@ :code $ quote $ defstruct StreamChunk (:text 'String) (:thinking? 'Bool) :examples $ [] :schema $ :: 'StructDef + 'abort-dynamic! $ %{} 'CodeEntry (:doc |) + :code $ quote $ defn abort-dynamic! (value) + ffi/abort! $ unsafe-coerce value 'js-ffi.shared/AbortControllerHost + :examples $ [] + :schema $ :: 'Fn $ {} (:return 'Unit) + :args $ [] 'Dynamic + :features $ #{} :js-ffi 'append-user-message $ %{} 'CodeEntry (:doc |) :code $ quote $ defn append-user-message (messages content) - conj messages $ %{} ChatMessage (:role :user) (:content content) (:thinking |) + conj messages $ ChatMessage :role :user :content content :thinking | :examples $ [] :schema $ :: 'Fn $ {} - :args $ [] - :: 'List 'app.schema/ChatMessage - , 'String + :args $ [] (:: 'List 'app.schema/ChatMessage) 'String :return $ :: 'List 'app.schema/ChatMessage 'call-anthropic-msg! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn call-anthropic-msg! (cursor state prompt-text model thinking? d!) - hint-fn $ {} (:async true) - :args $ [] 'List 'app.schema/ChatState 'String 'String 'Bool 'Dynamic let abort $ deref *abort-control - when - js-present-dynamic? abort - let - abort-controller $ unsafe-coerce abort 'Dynamic - do (js/console.warn |Aborting-prev) (.!abort abort-controller) + when (js-present-dynamic? abort) + do (ffi/console-warn! |Aborting-prev) (abort-dynamic! abort) d! $ :: :change-model let axios $ unsafe-coerce @@ -95,15 +93,13 @@ , 'Dynamic selected $ let selected0 $ js-await $ get-selected - if (js-present? selected0) (stream-text selected0) "|<未找到内容>" - content $ .replace prompt-text |{{selected}} selected + if (js-present-dynamic? selected0) (stream-text selected0) "|<未找到内容>" + content $ &str:replace prompt-text |{{selected}} selected messages0 $ append-user-message (:messages state) content messages1 $ upsert-assistant-message messages0 | | - result $ js-await $ .!post axios - str |https://sa.chenyong.life/v1/messages + result $ js-await $ .!post axios (str |https://sa.chenyong.life/v1/messages) js-object - :model $ option:unwrap-or (get-env |claude-model) - or model |claude-3-5-sonnet-latest + :model $ option:unwrap-or (get-env |claude-model) (or model |claude-3-5-sonnet-latest) :max_tokens 1024 :stream true :thinking $ if thinking? @@ -112,18 +108,16 @@ :messages $ messages->anthropic messages0 js-object :params $ js-object - :headers $ js-object - ; :Accept |text/event-stream - ; :Content-Type |application/json + :headers $ js-object (; :Accept |text/event-stream) (; :Content-Type |application/json) |x-api-key $ get-anthropic-key! |anthropic-version |2023-06-01 |anthropic-dangerous-direct-browser-access true :responseType |stream :adapter |fetch :signal $ let - abort $ new js/AbortController + abort $ ffi/abort-controller-create reset! *abort-control abort - .-signal abort + ffi/abort-signal abort stream $ unsafe-coerce (.-data result) 'Dynamic reader $ unsafe-coerce .!getReader $ unsafe-coerce @@ -142,19 +136,14 @@ if (wo-log done?) (:: :unit) do let - events $ -> (stream-text value) .split-lines - filter $ fn (s) - hint-fn $ {} $ :args ([] 'String) - .starts-with? s "|data: " + events $ -> (stream-text value) split-lines + filter $ fn (s) (starts-with? s "|data: ") map $ fn (s) - hint-fn $ {} $ :args ([] 'String) - -> (.strip-prefix s "|data: ") js/JSON.parse to-calcit-data + -> (strip-prefix s "|data: ") js/JSON.parse to-calcit-data apply-args (events) fn (xs) list-match xs - () $ do - println "|no thing to handle in this Loop" - , &unit + () $ do (println "|no thing to handle in this Loop") &unit (x0 xss) let stop? $ = @@ -170,19 +159,18 @@ do (println |content-is-nil) (recur xss) let content $ stream-text $ option:unwrap-or content-opt | - do (swap! *text str content) - d! (:: :states-merge cursor state) - {} (:answer @*text) (:loading? false) (:done? false) - :messages $ upsert-assistant-message messages1 @*text | - recur xss + swap! *text str content + d! (:: :states-merge cursor state) + {} (:answer @*text) (:loading? false) (:done? false) + :messages $ upsert-assistant-message messages1 @*text | + recur xss recur :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'List 'app.schema/ChatState 'String 'String 'Bool 'Dynamic + :schema $ :: 'Fn $ {} (:async true) (:return 'Dynamic) + :args $ [] (:: 'List 'Dynamic) 'app.schema/ChatState 'String 'String 'Bool 'Dynamic :features $ #{} :js-ffi 'call-flash-imagen-msg! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn call-flash-imagen-msg! (variant cursor state prompt-text d!) - hint-fn $ {} $ :async true if (= false @*gen-ai-new) let mod $ js-await $ js/import |@google/genai @@ -191,37 +179,31 @@ :apiKey $ get-gemini-key! let target0 $ js/document.querySelector |.show-image - when - js-present-dynamic? target0 + when (js-present-dynamic? target0) let target $ unsafe-coerce target0 'Dynamic .!setAttribute target |src | let abort $ deref *abort-control - when - js-present-dynamic? abort - let - abort-controller $ unsafe-coerce abort 'Dynamic - do (js/console.warn |Aborting-prev) (.!abort abort-controller) + when (js-present-dynamic? abort) + do (ffi/console-warn! |Aborting-prev) (abort-dynamic! abort) clear-image-cache! d! $ :: :states cursor $ -> state (assoc :answer |) (assoc :loading? true) let selected $ let selected0 $ js-await $ get-selected - if (js-present? selected0) (unsafe-coerce selected0 'String) "|<未找到选中内容>" + if (js-present-dynamic? selected0) (unsafe-coerce selected0 'String) "|<未找到选中内容>" gen-ai @*gen-ai-new - content $ .!replace prompt-text |{{selected}} selected + content $ &str:replace prompt-text |{{selected}} selected abort-signal $ let - abort $ new js/AbortController + abort $ ffi/abort-controller-create reset! *abort-control abort - .-signal abort + ffi/abort-signal abort sdk-result $ js-await $ .!generateContent unsafe-coerce .-models $ unsafe-coerce gen-ai 'Dynamic , 'Dynamic - js-object - :model |gemini-2.5-flash-image - :contents content + js-object (:model |gemini-2.5-flash-image) (:contents content) :config $ js-object (:abortSignal abort-signal) :httpOptions $ js-object $ :baseUrl |https://ja.chenyong.life :responseModalities $ js-array |TEXT |IMAGE @@ -238,48 +220,41 @@ , 'Dynamic *text $ atom | js-await $ .!forEach parts $ fn (chunk _a _b) - if - js-present-dynamic? chunk + if (js-present-dynamic? chunk) let chunk-data $ unsafe-coerce chunk 'Dynamic text0 $ .-text chunk-data - if - js-present-dynamic? text0 + if (js-present-dynamic? text0) let text $ unsafe-coerce text0 'String - do (swap! *text str text) - d! (:: :states cursor) - -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) + swap! *text str text + d! (:: :states cursor) + -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) let image-data0 $ .-inlineData chunk-data - when - js-present-dynamic? image-data0 + when (js-present-dynamic? image-data0) let image-data $ unsafe-coerce image-data0 'Dynamic data $ unsafe-coerce (.-data image-data) 'String image-blob $ base64ToBlob data url $ js/URL.createObjectURL image-blob - target $ unsafe-coerce - js/document.querySelector |.show-image - , 'Dynamic + target $ unsafe-coerce (js/document.querySelector |.show-image) 'Dynamic .!setAttribute target |src url reset! *image-cache url - do (swap! *text str | "|(image ready)") - d! (:: :states cursor) - -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) + swap! *text str | "|(image ready)" + d! (:: :states cursor) + -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) d! (:: :states cursor) -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) d! $ :: :states cursor $ -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) d! $ :: :states cursor $ -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? true) :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'Tag 'List 'app.schema/ChatState 'String 'Dynamic + :schema $ :: 'Fn $ {} (:async true) (:return 'Dynamic) + :args $ [] 'Tag (:: 'List 'Dynamic) 'app.schema/ChatState 'String 'Dynamic :features $ #{} :js-ffi 'call-genai-msg! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn call-genai-msg! variant cursor state prompt-text search? think? d! *text *thinking-text - hint-fn $ {} (:async true) - :args $ [] 'Tag 'List 'app.schema/ChatState 'String 'Bool 'Bool 'Dynamic 'Ref 'Ref if (= false @*gen-ai-new) let mod $ js-await $ js/import |@google/genai @@ -288,38 +263,27 @@ :apiKey $ get-gemini-key! let abort $ deref *abort-control - when - js-present-dynamic? abort - let - abort-controller $ unsafe-coerce abort 'Dynamic - do (js/console.warn |Aborting-prev) (.!abort abort-controller) + when (js-present-dynamic? abort) + do (ffi/console-warn! |Aborting-prev) (abort-dynamic! abort) let selected $ let - selected0 $ if (.includes? prompt-text |{{selected}}) + selected0 $ if (includes? prompt-text |{{selected}}) js-await $ get-selected , js/undefined - if (js-present? selected0) (unsafe-coerce selected0 'String) "|<未找到选中内容>" + if (js-present-dynamic? selected0) (stream-text selected0) "|<未找到选中内容>" gen-ai @*gen-ai-new model $ pick-model variant - content $ .!replace prompt-text |{{selected}} selected + content $ &str:replace prompt-text |{{selected}} selected json? $ or (includes? prompt-text |{{json}}) (includes? prompt-text |{{JSON}}) pro? $ includes? model |pro has-url? $ or (includes? prompt-text |http://) (includes? prompt-text |https://) - messages0 $ or (:messages state) ([]) + messages0 $ :messages state messages1 $ upsert-assistant-message messages0 | | abort-signal $ let - abort $ new js/AbortController + abort $ ffi/abort-controller-create reset! *abort-control abort - .-signal abort - tools $ -> - js-array - if search? - js-object $ :googleSearch $ js-object - , js/undefined - if has-url? - js-object $ :urlContext $ js-object - , js/undefined - .!filter $ fn (x _idx _arr) x + ffi/abort-signal abort + tools $ make-genai-tools search? has-url? sdk-result $ js-await $ .!generateContentStream unsafe-coerce .-models $ unsafe-coerce gen-ai 'Dynamic @@ -331,9 +295,7 @@ js-object (:thinkingLevel |high) (:includeThoughts true) js-object :thinkingLevel $ if - contains? - #{} |gemini-3.7-flash |gemini-3.8-flash - , model + contains? (#{} |gemini-3.7-flash |gemini-3.8-flash) model , |low |minimal :includeThoughts false :tools $ if @@ -346,32 +308,27 @@ :abortSignal abort-signal :httpOptions $ js-object $ :baseUrl |https://ja.chenyong.life :responseMimeType $ if json? |application/json js/undefined - do - js/setTimeout $ fn () $ d! - :: :states-merge cursor state $ {} (:answer |) (:thinking |) (:loading? true) (:done? false) (:messages messages1) - js-await $ js-for-await sdk-result $ fn (chunk) - if - js-present-dynamic? chunk - let - stream-chunk $ unsafe-coerce - decode-genai-chunk chunk - , 'app.comp.container/StreamChunk - is-thinking? $ :thinking? stream-chunk - text $ :text stream-chunk - if is-thinking? (swap! *thinking-text str text) (swap! *text str text) - d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) - :messages $ upsert-assistant-message messages1 @*text @*thinking-text + js/setTimeout $ fn () $ d! + :: :states-merge cursor state $ {} (:answer |) (:thinking |) (:loading? true) (:done? false) (:messages messages1) + js-await $ js-for-await sdk-result $ fn (chunk) + if (js-present-dynamic? chunk) + let + stream-chunk $ unsafe-coerce (decode-genai-chunk chunk) 'app.comp.container/StreamChunk + is-thinking? $ :thinking? stream-chunk + text $ :text stream-chunk + if is-thinking? (swap! *thinking-text str text) (swap! *text str text) d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) :messages $ upsert-assistant-message messages1 @*text @*thinking-text - d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? true) - :messages $ upsert-assistant-message messages1 @*text @*thinking-text + d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) + :messages $ upsert-assistant-message messages1 @*text @*thinking-text + d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? true) + :messages $ upsert-assistant-message messages1 @*text @*thinking-text :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'Tag 'List 'app.schema/ChatState 'String 'Bool 'Bool 'Dynamic 'Ref 'Ref + :schema $ :: 'Fn $ {} (:async true) (:return 'Dynamic) + :args $ [] 'Tag (:: 'List 'Dynamic) 'app.schema/ChatState 'String 'Bool 'Bool 'Dynamic (:: 'Ref 'String) (:: 'Ref 'String) :features $ #{} :js-ffi 'call-imagen-4-msg! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn call-imagen-4-msg! (variant cursor state prompt-text d!) - hint-fn $ {} $ :async true if (= false @*gen-ai-new) let mod $ js-await $ js/import |@google/genai @@ -380,69 +337,55 @@ :apiKey $ get-gemini-key! let target0 $ js/document.querySelector |.show-image - when - js-present-dynamic? target0 + when (js-present-dynamic? target0) let target $ unsafe-coerce target0 'Dynamic .!removeAttribute target |src let abort $ deref *abort-control - when - js-present-dynamic? abort - let - abort-controller $ unsafe-coerce abort 'Dynamic - do (js/console.warn |Aborting-prev) (.!abort abort-controller) + when (js-present-dynamic? abort) + do (ffi/console-warn! |Aborting-prev) (abort-dynamic! abort) clear-image-cache! d! $ :: :states cursor $ -> state (assoc :answer |) (assoc :loading? true) let gen-ai @*gen-ai-new abort-signal $ let - abort $ new js/AbortController + abort $ ffi/abort-controller-create reset! *abort-control abort - .-signal abort + ffi/abort-signal abort response $ js-await $ .!generateImages unsafe-coerce .-models $ unsafe-coerce gen-ai 'Dynamic , 'Dynamic - js-object - :model |imagen-4.0-generate-001 - :prompt prompt-text - :config $ js-object (:numberOfImages 1) - :includeRaiReason true + js-object (:model |imagen-4.0-generate-001) (:prompt prompt-text) + :config $ js-object (:numberOfImages 1) (:includeRaiReason true) :httpOptions $ js-object $ :baseUrl |https://ja.chenyong.life :signal abort-signal *text $ atom | let response-data $ unsafe-coerce response 'Dynamic - generated-images $ unsafe-coerce - .-generatedImages response-data - , 'Dynamic + generated-images $ unsafe-coerce (.-generatedImages response-data) 'Dynamic generated-image $ unsafe-coerce (.-0 generated-images) 'Dynamic image $ unsafe-coerce (.-image generated-image) 'Dynamic image-data0 $ .-imageBytes image - when - js-present-dynamic? image-data0 + when (js-present-dynamic? image-data0) let image-data $ unsafe-coerce image-data0 'String image-blob $ base64ToBlob image-data url $ js/URL.createObjectURL image-blob - target $ unsafe-coerce - js/document.querySelector |.show-image - , 'Dynamic + target $ unsafe-coerce (js/document.querySelector |.show-image) 'Dynamic reset! *image-cache url .!setAttribute target |src url - do (swap! *text str | "|(image ready)") - d! (:: :states cursor) - -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) + swap! *text str | "|(image ready)" + d! (:: :states cursor) + -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? false) d! $ :: :states cursor $ -> state (assoc :answer @*text) (assoc :loading? false) (assoc :done? true) :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'Tag 'List 'app.schema/ChatState 'String 'Dynamic + :schema $ :: 'Fn $ {} (:async true) (:return 'Dynamic) + :args $ [] 'Tag (:: 'List 'Dynamic) 'app.schema/ChatState 'String 'Dynamic :features $ #{} :js-ffi 'call-openrouter! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn call-openrouter! (cursor state prompt-text variant thinking? d! *text) - hint-fn $ {} (:async true) - :args $ [] 'List 'app.schema/ChatState 'String 'String 'Bool 'Dynamic 'Ref if (= false @*openai) let mod $ js-await $ js/import |openai @@ -456,21 +399,18 @@ :dangerouslyAllowBrowser true let abort $ deref *abort-control - when - js-present-dynamic? abort - let - abort-controller $ unsafe-coerce abort 'Dynamic - do (js/console.warn |Aborting-prev) (.!abort abort-controller) + when (js-present-dynamic? abort) + do (ffi/console-warn! |Aborting-prev) (abort-dynamic! abort) let selected $ let selected0 $ js-await $ get-selected - if (js-present? selected0) (unsafe-coerce selected0 'String) "|<未找到选中内容>" + if (js-present-dynamic? selected0) (unsafe-coerce selected0 'String) "|<未找到选中内容>" openai $ let ai @*openai , ai - content $ .!replace prompt-text |{{selected}} selected + content $ &str:replace prompt-text |{{selected}} selected json? $ or (includes? prompt-text |{{json}}) (includes? prompt-text |{{JSON}}) - messages0 $ or (:messages state) ([]) + messages0 $ :messages state messages1 $ upsert-assistant-message messages0 | | sdk-result $ js-await $ .!create unsafe-coerce @@ -483,52 +423,44 @@ :stream true :headers $ js-object $ :HTTP-Referer js/location.host js-object $ :signal $ let - abort $ new js/AbortController + abort $ ffi/abort-controller-create reset! *abort-control abort - .-signal abort - do - js/setTimeout $ fn () $ d! - :: :states-merge cursor state $ {} (:answer |) (:thinking |) (:loading? true) (:done? false) (:messages messages1) - let - *thinking-text $ atom | - *char-buf $ atom 0 - js-await $ js-for-await sdk-result $ fn (chunk) - if - js-present-dynamic? chunk - let - chunk-data $ unsafe-coerce chunk 'Dynamic - choices $ unsafe-coerce (.-choices chunk-data) 'Dynamic - choice0 $ unsafe-coerce (.-0 choices) 'Dynamic - choice $ unsafe-coerce (.-delta choice0) 'Dynamic - reason0 $ .-reasoning_content choice - reason $ if - js-present-dynamic? reason0 - stream-text reason0 - , | - text0 $ .-content choice - text $ if - js-present-dynamic? text0 - stream-text text0 - , | - if - not $ blank? reason - swap! *thinking-text str reason - if - not $ blank? text - swap! *text str text - swap! *char-buf + $ count text - when - or (> @*char-buf 80) (includes? text &newline) - do (reset! *char-buf 0) - d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) - :messages $ upsert-assistant-message messages1 @*text @*thinking-text - d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) - :messages $ upsert-assistant-message messages1 @*text @*thinking-text - d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? true) + ffi/abort-signal abort + js/setTimeout $ fn () $ d! + :: :states-merge cursor state $ {} (:answer |) (:thinking |) (:loading? true) (:done? false) (:messages messages1) + let + *thinking-text $ atom | + *char-buf $ atom 0 + js-await $ js-for-await sdk-result $ fn (chunk) + if (js-present-dynamic? chunk) + let + chunk-data $ unsafe-coerce chunk 'Dynamic + choices $ unsafe-coerce (.-choices chunk-data) 'Dynamic + choice0 $ unsafe-coerce (.-0 choices) 'Dynamic + choice $ unsafe-coerce (.-delta choice0) 'Dynamic + reason0 $ .-reasoning_content choice + reason $ if (js-present-dynamic? reason0) (stream-text reason0) | + text0 $ .-content choice + text $ if (js-present-dynamic? text0) (stream-text text0) | + if + not $ blank? reason + swap! *thinking-text str reason + if + not $ blank? text + swap! *text str text + swap! *char-buf + $ count text + when + or (> @*char-buf 80) (includes? text &newline) + do (reset! *char-buf 0) + d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) + :messages $ upsert-assistant-message messages1 @*text @*thinking-text + d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? false) :messages $ upsert-assistant-message messages1 @*text @*thinking-text + d! $ :: :states-merge cursor state $ {} (:answer @*text) (:thinking @*thinking-text) (:loading? false) (:done? true) + :messages $ upsert-assistant-message messages1 @*text @*thinking-text :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'List 'app.schema/ChatState 'String 'String 'Bool 'Dynamic 'Ref + :schema $ :: 'Fn $ {} (:async true) (:return 'Dynamic) + :args $ [] (:: 'List 'Dynamic) 'app.schema/ChatState 'String 'String 'Bool 'Dynamic $ :: 'Ref 'String :features $ #{} :js-ffi 'clear-image-cache! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn clear-image-cache! () @@ -539,7 +471,9 @@ js/URL.revokeObjectURL $ unsafe-coerce url 'String reset! *image-cache false :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'Unit) + :args $ [] + :features $ #{} :js-ffi 'comp-abort $ %{} 'CodeEntry (:doc |) :code $ quote $ defn comp-abort (t) span @@ -550,26 +484,23 @@ :on-click $ fn (e d!) let abort $ deref *abort-control - when - js-present-dynamic? abort - let - abort-controller $ unsafe-coerce abort 'Dynamic - do (js/console.warn |Aborting-prev) (.!abort abort-controller) + when (js-present-dynamic? abort) + do (js/console.warn |Aborting-prev) (abort-dynamic! abort) , &unit <> t =< 8 nil <> "|✕" style-abort-close :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'respo.schema/Element) + :args $ [] 'String + :features $ #{} :js-ffi 'comp-container $ %{} 'CodeEntry (:doc |) :code $ quote $ defcomp comp-container (reel) let app-store $ option:fold (get reel :store) fn () store fn (raw-store) - if (struct? raw-store) - unsafe-coerce raw-store 'app.schema/Store - decode-map-as raw-store 'app.schema/Store + if (struct? raw-store) (unsafe-coerce raw-store 'app.schema/Store) (decode-map-as raw-store 'app.schema/Store) sessions $ :sessions app-store archived-count $ :archived-count app-store current-session-id $ :current-session-id app-store @@ -581,32 +512,39 @@ :messages $ [] :model :gemini :thinking | - unsafe-coerce - normalize-chat-state raw-chat-state - , 'app.schema/ChatState + unsafe-coerce (normalize-chat-state raw-chat-state) 'app.schema/ChatState done? $ :done? state messages $ :messages state model $ if tag? $ :model state :model state , :gemini - is-viewing-history? $ and - some? current-session-id + is-viewing-history? $ and (js-present-dynamic? current-session-id) option:fold - first $ filter sessions $ fn (s) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession - = (:id s) current-session-id - fn () false + assert-type + first $ filter sessions $ fn (s) + hint-fn $ {} + :args $ [] 'app.schema/ChatSession + :return 'Bool + = (:id s) (assert-type current-session-id 'String) + :: 'Option 'app.schema/ChatSession + fn () + hint-fn $ {} $ :return 'Bool + , false fn (session) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession + hint-fn $ {} + :args $ [] 'app.schema/ChatSession + :return 'Bool :is-history? session - last-assistant $ option:fold (last messages) - fn () $ :answer state + last-assistant $ option:fold + assert-type (last messages) (:: 'Option 'app.schema/ChatMessage) + fn () + hint-fn $ {} $ :return 'String + :answer state fn (last-msg) - hint-fn $ {} $ :args - [] 'app.schema/ChatMessage + hint-fn $ {} + :args $ [] 'app.schema/ChatMessage + :return 'String if = :assistant $ :role last-msg :content last-msg @@ -622,58 +560,38 @@ :on-result $ fn (result d!) d! cursor $ assoc state :model $ option:unwrap-or (nth result 1) :gemini reply-plugin $ use-prompt (>> states :reply-prompt) - {} (:text |Follow-up) - :placeholder "|Enter your follow-up" - :multiline? true - :button-text |Send + {} (:text |Follow-up) (:placeholder "|Enter your follow-up") (:multiline? true) (:button-text |Send) :validator $ fn (text) if (blank? text) "|Please enter text" | api-key-plugin $ use-prompt (>> states :api-key-prompt) - {} - :text "|API key required" - :placeholder "|Enter API key" - :button-text |Save + {} (:text "|API key required") (:placeholder "|Enter API key") (:button-text |Save) :validator $ fn (text) if (blank? text) "|Please enter API key" | - text-alert-plugin $ use-alert - >> states :empty-message-alert + text-alert-plugin $ use-alert (>> states :empty-message-alert) {} $ :text "|Please enter text" submit-with-key! $ fn (submit-state text search? think? d!) hint-fn $ {} :args $ [] 'app.schema/ChatState 'String 'Bool 'Bool 'Dynamic :features $ #{} :js-ffi - if (blank? text) - .show text-alert-plugin d! "|Please enter text" + if (blank? text) (.show text-alert-plugin d! "|Please enter text") let storage-key $ model-storage-key model - stored0 $ js/localStorage.getItem storage-key - stored $ if - js-present-dynamic? stored0 - unsafe-coerce stored0 'String - , | + stored $ browser/storage-get-or storage-key | if (blank? stored) - .show api-key-plugin d! $ fn (key) - do - js/localStorage.setItem storage-key key - submit-message! cursor submit-state text search? think? model d! + .show api-key-plugin d! $ fn (key) (browser/storage-set! storage-key key) (submit-message! cursor submit-state text search? think? model d!) submit-message! cursor submit-state text search? think? model d! message-box-state $ let raw-message-box-state $ option:unwrap-or get (>> states :message-box) :data {} (:content |) (:search? false) (:think? false) (:focus-mode? false) - if - struct? raw-message-box-state - unsafe-coerce raw-message-box-state 'app.schema/MessageBoxState - decode-map-as raw-message-box-state 'app.schema/MessageBoxState + if (struct? raw-message-box-state) (unsafe-coerce raw-message-box-state 'app.schema/MessageBoxState) (decode-map-as raw-message-box-state 'app.schema/MessageBoxState) sessions-plugin $ use-drawer (>> states :sessions-modal) - {} - :title "|History Sessions" - :style $ {} - :min-width "||max(320px,30vw)\"" - :max-width |80vw + {} (:title "|History Sessions") + :style $ {} (:min-width "||max(320px,30vw)\"") (:max-width |80vw) :render $ fn (on-close) comp-sessions-modal sessions archived-count fn (session-id d!) + hint-fn $ {} $ :args ([] 'String 'Dynamic) when and > (count messages) 0 @@ -682,8 +600,9 @@ d! $ :: :save-session state d! $ :: :load-session cursor state $ -> sessions filter $ fn (s) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession + hint-fn $ {} + :args $ [] 'app.schema/ChatSession + :return 'Bool = (:id s) session-id , first option:unwrap on-close d! @@ -692,18 +611,14 @@ let archive-key $ :archive-key site raw $ js-await $ db-get archive-key - if (blank? raw) - js/alert "|No archives found!" + if (blank? raw) (js/alert "|No archives found!") let - parsed $ parse-cirru-edn raw + parsed $ parse-cirru-edn-as raw $ :: 'List 'app.schema/ChatSession reset! *archived-sessions parsed div {} $ :class-name $ str-spaced css/preset css/global css/column css/fullscreen css/gap8 style-app-global - if - not= false @*archived-sessions - if - not= false @*viewing-archive-session - ; Render specific read-only archived session + if (js-present? @*archived-sessions) + if (js-present? @*viewing-archive-session) (; Render specific read-only archived session) div {} $ :class-name $ str-spaced css/expand style-message-area div @@ -711,15 +626,10 @@ div $ {} :style $ {} $ :font-weight :bold :inner-text $ str "|Archived: " $ :preview - assert-type - app.schema/normalize-chat-session @*viewing-archive-session - , 'app.schema/ChatSession + assert-type (app.schema/normalize-chat-session @*viewing-archive-session) 'app.schema/ChatSession div - {} - :class-name style-archive-close - :on-click $ fn (e d!) - reset! *viewing-archive-session false - , &unit + {} (:class-name style-archive-close) + :on-click $ fn (e d!) (reset! *viewing-archive-session nil) &unit <> "|✕" ; Messages list $ read only div @@ -728,12 +638,9 @@ list-> {} $ :class-name $ str-spaced css/column css/gap8 -> - :messages $ assert-type - app.schema/normalize-chat-session @*viewing-archive-session - , 'app.schema/ChatSession + :messages $ assert-type (app.schema/normalize-chat-session @*viewing-archive-session) 'app.schema/ChatSession map-indexed $ fn (idx msg) - hint-fn $ {} $ :args - [] 'Number 'app.schema/ChatMessage + hint-fn $ {} $ :args ([] 'Number 'app.schema/ChatMessage) [] idx $ let role $ :role msg content $ stream-text $ :content msg @@ -751,13 +658,9 @@ memo-comp-by thinking comp-md-block thinking $ {} $ :class-name style-md-content if (= role :assistant) if (json-pattern? content) - pre $ {} - :class-name style-code-content - :inner-text content + pre $ {} (:class-name style-code-content) (:inner-text content) memo-comp-by content comp-md-block content $ {} $ :class-name style-md-content - pre $ {} - :class-name style-message-text - :inner-text content + pre $ {} (:class-name style-message-text) (:inner-text content) ; Render archived sessions list div {} $ :class-name $ str-spaced css/expand style-message-area @@ -767,12 +670,8 @@ :style $ {} $ :font-weight :bold :inner-text "|All Archived Sessions" div - {} - :class-name style-archive-close - :on-click $ fn (e d!) - reset! *archived-sessions false - reset! *viewing-archive-session false - , &unit + {} (:class-name style-archive-close) + :on-click $ fn (e d!) (reset! *archived-sessions nil) (reset! *viewing-archive-session nil) &unit <> "|✕" ; List div @@ -780,30 +679,25 @@ list-> {} $ :class-name css/column let - current-archives $ or @*archived-sessions $ [] + current-archives $ assert-type @*archived-sessions $ :: 'List 'app.schema/ChatSession if (empty? current-archives) [] :empty $ div {} $ :style $ {} (:padding |12px) :color $ hsl 0 0 60 <> "|No archived sessions left." -> current-archives reverse $ map $ fn (session) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession + hint-fn $ {} $ :args ([] 'app.schema/ChatSession) let session-id $ :id session created-at $ :created-at session preview $ :preview session - date-str $ unsafe-coerce - .!toLocaleString $ new js/Date created-at - , 'String + date-str $ ffi/date-local-string $ ffi/date-from-ms created-at [] session-id $ div {} $ :class-name style-session-item div {} (:role |button) :style $ {} (:flex |1) (:cursor :pointer) (:min-width 0) (:overflow :hidden) - :on-click $ fn (e d!) - reset! *viewing-archive-session session - , &unit + :on-click $ fn (e d!) (reset! *viewing-archive-session session) &unit div {} $ :style $ {} (:font-size |12px) :color $ hsl 0 0 60 @@ -812,18 +706,19 @@ {} $ :style $ {} (:margin-top |4px) (:white-space :nowrap) (:overflow :hidden) (:text-overflow :ellipsis) (:max-height |1.2em) (:line-height |1.2) <> preview div - {} - :class-name style-delete-button - :role |button + {} (:class-name style-delete-button) (:role |button) :on-click $ fn (e d!) hint-fn $ {} $ :async true let proceed? $ js/confirm "|Delete this archived session?" when proceed? $ let - new-archives $ filter @*archived-sessions $ fn (s) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession - not= (:id s) session-id + new-archives $ filter + assert-type @*archived-sessions $ :: 'List 'app.schema/ChatSession + fn (s) + hint-fn $ {} + :args $ [] 'app.schema/ChatSession + :return 'Bool + not= (:id s) session-id reset! *archived-sessions new-archives let archive-key $ :archive-key site @@ -863,8 +758,7 @@ list-> {} $ :class-name $ str-spaced css/column css/gap8 -> messages $ map-indexed $ fn (idx msg) - hint-fn $ {} $ :args - [] 'Number 'app.schema/ChatMessage + hint-fn $ {} $ :args ([] 'Number 'app.schema/ChatMessage) [] idx $ let role $ :role msg content $ stream-text $ :content msg @@ -882,13 +776,9 @@ memo-comp-by thinking comp-md-block thinking $ {} $ :class-name style-md-content if (= role :assistant) if (json-pattern? content) - pre $ {} - :class-name style-code-content - :inner-text content + pre $ {} (:class-name style-code-content) (:inner-text content) memo-comp-by content comp-md-block content $ {} $ :class-name style-md-content - pre $ {} - :class-name style-message-text - :inner-text content + pre $ {} (:class-name style-message-text) (:inner-text content) if and (= role :assistant) or done? $ not= idx $ dec (count messages) @@ -909,28 +799,21 @@ :class-name $ str-spaced css/button style-reply-button :on-click $ fn (e d!) .show reply-plugin d! $ fn (text) - submit-with-key! state (stream-text text) - :search? message-box-state - :think? message-box-state - , d! + submit-with-key! state (stream-text text) (:search? message-box-state) (:think? message-box-state) d! , &unit <> |Reply - if - :focus-mode? message-box-state - , nil $ a $ {} (:class-name style-focus-link) (:inner-text |Focus) (:role |button) (:aria-label |focus-composer) - :on-click $ fn (e d!) - let - focused $ .-activeElement $ unsafe-coerce js/document 'Dynamic - do - when - js-present-dynamic? focused - .!blur $ unsafe-coerce focused 'Dynamic - d! - option:unwrap-or - get (>> states :message-box) :cursor - [] - assoc message-box-state :focus-mode? true - , &unit + if (:focus-mode? message-box-state) nil $ a $ {} (:class-name style-focus-link) (:inner-text |Focus) (:role |button) (:aria-label |focus-composer) + :on-click $ fn (e d!) + let + focused $ .-activeElement $ unsafe-coerce js/document 'Dynamic + when (js-present-dynamic? focused) + .!blur $ unsafe-coerce focused 'Dynamic + d! + option:unwrap-or + get (>> states :message-box) :cursor + [] + assoc message-box-state :focus-mode? true + , &unit , nil if (:loading? state) div ({}) (comp-abort |Loading...) @@ -957,8 +840,7 @@ .show model-plugin d! , &unit fn (text search? think? d!) - if (blank? text) - .show text-alert-plugin d! "|Please enter text" + if (blank? text) (.show text-alert-plugin d! "|Please enter text") do when and @@ -983,8 +865,7 @@ if dev? $ comp-reel (>> states :reel) reel $ {} if dev? $ comp-inspect |Store app-store nil :examples $ [] - :schema $ :: 'Fn $ {} - :return 'respo.schema/Component + :schema $ :: 'Fn $ {} (:return 'respo.schema/Component) :args $ [] $ :: 'Map 'Tag 'Dynamic :features $ #{} :js-ffi 'comp-fill $ %{} 'CodeEntry (:doc |) @@ -996,7 +877,9 @@ , &unit comp-local-icon :send 12 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'respo.schema/Component) + :args $ [] 'String + :features $ #{} :js-ffi 'comp-local-icon $ %{} 'CodeEntry (:doc |) :code $ quote $ defcomp comp-local-icon (icon size) span $ {} (:aria-hidden |true) @@ -1008,8 +891,7 @@ :clock "|" :send "|" :examples $ [] - :schema $ :: 'Fn $ {} - :return 'respo.schema/Component + :schema $ :: 'Fn $ {} (:return 'respo.schema/Component) :args $ [] 'Tag 'Number 'comp-message-box $ %{} 'CodeEntry (:doc |) :code $ quote $ defcomp comp-message-box (states picker-el on-submit model) @@ -1018,10 +900,7 @@ state $ let raw-message-box-state $ option:unwrap-or (get states :data) {} (:content |) (:search? false) (:think? false) (:focus-mode? false) - if - struct? raw-message-box-state - unsafe-coerce raw-message-box-state 'app.schema/MessageBoxState - decode-map-as raw-message-box-state 'app.schema/MessageBoxState + if (struct? raw-message-box-state) (unsafe-coerce raw-message-box-state 'app.schema/MessageBoxState) (decode-map-as raw-message-box-state 'app.schema/MessageBoxState) [] (effect-focus) (on-fill cursor state on-submit) div {} $ :class-name $ str-spaced css/center style-message-box-panel @@ -1032,13 +911,10 @@ {} (:role |button) (:aria-label |expand-prompt) :class-name $ str-spaced css/font-code! style-focus-box style-textbox-compact :on-click $ fn (e d!) - do - d! cursor $ assoc state :focus-mode? false - js/setTimeout - fn () $ .!focus $ unsafe-coerce - js/document.querySelector |#message - , 'Dynamic - , 0 + d! cursor $ assoc state :focus-mode? false + js/setTimeout + fn () $ .!focus $ unsafe-coerce (js/document.querySelector |#message) 'Dynamic + , 0 , &unit <> $ if blank? $ :content state @@ -1063,12 +939,8 @@ option:unwrap-or (get e :ctrl?) false on-submit (:content state) (:search? state) (:think? state) d! , &unit - :on-focus $ fn (e d!) - toggle-focus-within! e true - , &unit - :on-blur $ fn (e d!) - toggle-focus-within! e false - , &unit + :on-focus $ fn (e d!) (toggle-focus-within! e true) &unit + :on-blur $ fn (e d!) (toggle-focus-within! e false) &unit if not $ :focus-mode? state do (=< nil 4) @@ -1082,18 +954,14 @@ :aria-label |clear-prompt :on-click $ fn (e d!) d! cursor $ assoc state :content | - .!focus $ unsafe-coerce - js/document.querySelector |#message - , 'Dynamic + .!focus $ unsafe-coerce (js/document.querySelector |#message) 'Dynamic , &unit span $ {} $ :class-name style-clear div {} $ :class-name $ str-spaced css/row style-gap12 , picker-el if - contains? - #{} :gemini-flash :gemini-3.5-flash :gemini-3.1-flash-lite-preview - , model + contains? (#{} :gemini-flash :gemini-3.5-flash :gemini-3.1-flash-lite-preview) model div {} (:role |group) (:aria-label |think-toggle) :class-name $ str-spaced css/row style-checkbox @@ -1127,15 +995,17 @@ , &unit , nil :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'Map 'Dynamic 'Dynamic 'Dynamic + :schema $ :: 'Fn $ {} (:return 'respo.schema/Component) + :args $ [] (:: 'Map 'Tag 'Dynamic) 'respo.schema/Element + :: 'Fn $ {} (:return 'Unit) + :args $ [] 'String 'Bool 'Bool 'Dynamic + , 'Tag :features $ #{} :js-ffi 'comp-sessions-modal $ %{} 'CodeEntry (:doc |) :code $ quote $ defcomp comp-sessions-modal (sessions archived-count on-select on-close on-view-archive) let history-items $ foldl sessions 0 $ fn (acc session) - hint-fn $ {} $ :args - [] 'Number 'app.schema/ChatSession + hint-fn $ {} $ :args ([] 'Number 'app.schema/ChatSession) + acc $ count $ or (:messages session) ([]) div {} $ :class-name $ str-spaced css/column css/gap8 style-sessions-list @@ -1158,15 +1028,12 @@ list-> {} $ :class-name css/column -> sessions reverse $ map $ fn (session) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession + hint-fn $ {} $ :args ([] 'app.schema/ChatSession) let session-id $ :id session created-at $ :created-at session preview $ :preview session - date-str $ unsafe-coerce - .!toLocaleString $ new js/Date created-at - , 'String + date-str $ ffi/date-local-string $ ffi/date-from-ms created-at [] session-id $ div {} $ :class-name style-session-item div @@ -1182,9 +1049,7 @@ {} $ :style $ {} (:margin-top |4px) (:white-space :nowrap) (:overflow :hidden) (:text-overflow :ellipsis) (:max-height |1.2em) (:line-height |1.2) <> preview div - {} - :class-name style-delete-button - :role |button + {} (:class-name style-delete-button) (:role |button) :aria-label $ str |session-delete: session-id :on-click $ fn (e d!) let @@ -1205,20 +1070,15 @@ {} $ :class-name $ str-spaced css/row-parted div {} $ :class-name $ str-spaced css/row css/gap8 - a $ {} (:class-name style-clear) (:inner-text |Data) (:role |button) - :aria-label |sessions-export-data + a $ {} (:class-name style-clear) (:inner-text |Data) (:role |button) (:aria-label |sessions-export-data) :on-click $ fn (e d!) tab-echo! sessions $ %some :edn , &unit - a $ {} (:class-name style-clear) (:inner-text |Download) (:role |button) - :aria-label |sessions-download - :on-click $ fn (e d!) - download-sessions! sessions - , &unit + a $ {} (:class-name style-clear) (:inner-text |Download) (:role |button) (:aria-label |sessions-download) + :on-click $ fn (e d!) (download-sessions! sessions) &unit if > (count sessions) 0 - a $ {} (:class-name style-clear) (:inner-text "|Archive all") (:role |button) - :aria-label |sessions-archive-all + a $ {} (:class-name style-clear) (:inner-text "|Archive all") (:role |button) (:aria-label |sessions-archive-all) :on-click $ fn (e d!) hint-fn $ {} $ :async true let @@ -1226,7 +1086,8 @@ when proceed? $ let archive-key $ :archive-key site raw $ js-await $ db-get archive-key - old-archives $ if (blank? raw) ([]) (parse-cirru-edn raw) + old-archives $ if (blank? raw) (take sessions 0) + parse-cirru-edn-as raw $ :: 'List 'app.schema/ChatSession new-archives $ concat old-archives sessions js-await $ db-set archive-key $ format-cirru-edn new-archives d! $ :: :archive-sessions $ count new-archives @@ -1237,95 +1098,67 @@ , nil :examples $ [] :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] - :: 'List 'app.schema/ChatSession - , 'Number 'Dynamic 'Dynamic 'Dynamic + :args $ [] (:: 'List 'app.schema/ChatSession) 'Number 'Dynamic 'Dynamic 'Dynamic :features $ #{} :js-ffi 'create-session $ %{} 'CodeEntry (:doc |) :code $ quote $ defn create-session (messages model) let id $ generate-session-id - first-msg $ option:fold (first messages) + first-msg $ option:fold + assert-type (first messages) (:: 'Option 'app.schema/ChatMessage) fn () |New-chat fn (message) - hint-fn $ {} $ :args - [] 'app.schema/ChatMessage + hint-fn $ {} + :args $ [] 'app.schema/ChatMessage + :return 'String :content message - %{} ChatSession (:id id) - :created-at $ unsafe-coerce (js/Date.now) 'Number - :messages messages - :model model - :preview $ let + ChatSession :id id :created-at (ffi/now-ms) :messages messages :model model :preview + let len $ count first-msg end $ if (< len 100) len 100 slice first-msg 0 end - :is-history? false + , :is-history? false :examples $ [] - :schema $ :: 'Fn $ {} - :return 'app.schema/ChatSession - :args $ [] - :: 'List 'app.schema/ChatMessage - , 'Tag + :schema $ :: 'Fn $ {} (:return 'app.schema/ChatSession) + :args $ [] (:: 'List 'app.schema/ChatMessage) 'Tag 'decode-genai-chunk $ %{} 'CodeEntry (:doc |) :code $ quote $ defn decode-genai-chunk (chunk) - hint-fn $ {} - :args $ [] 'JsObject - :return 'app.comp.container/StreamChunk - :features $ #{} :js-ffi let chunk-data $ unsafe-coerce chunk 'Dynamic candidates0 $ .?-candidates chunk-data - candidate0 $ if - js-present-dynamic? candidates0 + candidate0 $ if (js-present-dynamic? candidates0) .?-0 $ unsafe-coerce candidates0 'Dynamic , js/undefined - content0 $ if - js-present-dynamic? candidate0 + content0 $ if (js-present-dynamic? candidate0) .?-content $ unsafe-coerce candidate0 'Dynamic , js/undefined - parts0 $ if - js-present-dynamic? content0 + parts0 $ if (js-present-dynamic? content0) .?-parts $ unsafe-coerce content0 'Dynamic , js/undefined - part0 $ if - js-present-dynamic? parts0 + part0 $ if (js-present-dynamic? parts0) .?-0 $ unsafe-coerce parts0 'Dynamic , js/undefined part $ unsafe-coerce part0 'Dynamic - thought0 $ if - js-present-dynamic? part0 - .?-thought part - , js/undefined + thought0 $ if (js-present-dynamic? part0) (.?-thought part) js/undefined thinking? $ if (bool? thought0) (unsafe-coerce thought0 'Bool) false - part-text $ if - js-present-dynamic? part0 - .?-text part - , js/undefined + part-text $ if (js-present-dynamic? part0) (.?-text part) js/undefined chunk-text $ .?-text chunk-data - primary-text $ if - js-present-dynamic? part-text - , part-text chunk-text + primary-text $ if (js-present-dynamic? part-text) part-text chunk-text prompt-feedback $ .?-promptFeedback chunk-data - fallback-text $ if - js-present-dynamic? prompt-feedback + fallback-text $ if (js-present-dynamic? prompt-feedback) .?-blockReason $ unsafe-coerce prompt-feedback 'Dynamic , js/undefined - text0 $ if - js-present-dynamic? primary-text - , primary-text fallback-text + text0 $ if (js-present-dynamic? primary-text) primary-text fallback-text text $ stream-text text0 - %{} StreamChunk (:text text) (:thinking? thinking?) + StreamChunk :text text :thinking? thinking? :examples $ [] $ quote let feedback-only $ js-object $ :promptFeedback js-object $ :blockReason |blocked - decoded $ unsafe-coerce - decode-genai-chunk feedback-only - , 'app.comp.container/StreamChunk + decoded $ unsafe-coerce (decode-genai-chunk feedback-only) 'app.comp.container/StreamChunk assert= |blocked $ :text decoded assert= false $ :thinking? decoded - :schema $ :: 'Fn $ {} - :return 'app.comp.container/StreamChunk + :schema $ :: 'Fn $ {} (:return 'app.comp.container/StreamChunk) :args $ [] 'JsObject :features $ #{} :js-ffi 'download-sessions! $ %{} 'CodeEntry (:doc |) @@ -1336,20 +1169,17 @@ new js/Blob (js-array content) js-object $ :type |application/edn;charset=utf-8 , 'Dynamic - url $ unsafe-coerce - js/URL.createObjectURL blob - , 'String - link $ unsafe-coerce - js/document.createElement |a - , 'Dynamic - filename $ str |sessions- (js/Date.now) |.cirru - do (.!setAttribute link |href url) (.!setAttribute link |download filename) - .!appendChild (unsafe-coerce js/document.body 'Dynamic) link - .!click link - .!remove link - js/setTimeout - fn () $ js/URL.revokeObjectURL url - , 0 + url $ unsafe-coerce (js/URL.createObjectURL blob) 'String + link $ unsafe-coerce (js/document.createElement |a) 'Dynamic + filename $ str |sessions- (ffi/now-ms) |.cirru + .!setAttribute link |href url + .!setAttribute link |download filename + .!appendChild (unsafe-coerce js/document.body 'Dynamic) link + .!click link + .!remove link + js/setTimeout + fn () $ js/URL.revokeObjectURL url + , 0 :examples $ [] :schema $ :: 'Fn $ {} (:return 'Dynamic) :args $ [] $ :: 'List 'app.schema/ChatSession @@ -1365,46 +1195,50 @@ .!select textarea , 0 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'respo.schema/Effect) + :args $ [] + :features $ #{} :js-ffi 'first-line $ %{} 'CodeEntry :doc "|last message from error contains a line starts with \"data: \" and an extra error message. In order that JSON is parsed correctly, only first line is used now." :code $ quote $ defn first-line (tt) let - lines $ -> tt (.!split &newline) - .!filter $ fn (line idx _a) + lines $ filter (split-lines tt) + fn (line) + hint-fn $ {} + :args $ [] 'String + :return 'Bool not $ blank? line - if - > (.-length lines) 1 - js/console.warn "|Droping some unexpected lines:" $ .!slice lines 1 - .-0 lines + when + > (count lines) 1 + eprintln "|Dropping some unexpected lines:" $ slice lines 1 + option:unwrap-or (first lines) | :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] 'String 'generate-session-id $ %{} 'CodeEntry (:doc |) :code $ quote $ defn generate-session-id () - str $ js/Date.now + str $ ffi/now-ms :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] 'get-anthropic-key! $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn get-anthropic-key! () - required-key! |claude-key "|Required claude-key in localStorage" + :code $ quote $ defn get-anthropic-key! () (required-key! |claude-key "|Required claude-key in localStorage") :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] :features $ #{} :js-ffi 'get-deepinfra-key! $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn get-deepinfra-key! () - required-key! |deepinfra-key "|Required deepinfra-key in localStorage" + :code $ quote $ defn get-deepinfra-key! () (required-key! |deepinfra-key "|Required deepinfra-key in localStorage") :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'String) + :args $ [] + :features $ #{} :js-ffi 'get-deepseek-key! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn get-deepseek-key! () let key $ required-key! |deepseek-key "|Required deepseek-key in localStorage" - do - when chrome-extension? $ js/chrome.storage.local.set $ js-object (:deepseekKey key) - , key + when chrome-extension? $ js/chrome.storage.local.set $ js-object (:deepseekKey key) + , key :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] @@ -1413,16 +1247,14 @@ :code $ quote $ defn get-gemini-key! () let key $ required-key! |gemini-key "|Required gemini-key in localStorage" - do - when chrome-extension? $ js/chrome.storage.local.set $ js-object (:geminiKey key) - , key + when chrome-extension? $ js/chrome.storage.local.set $ js-object (:geminiKey key) + , key :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] :features $ #{} :js-ffi 'get-openrouter-key! $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn get-openrouter-key! () - required-key! |openrouter-key "|Required openrouter-key in localStorage" + :code $ quote $ defn get-openrouter-key! () (required-key! |openrouter-key "|Required openrouter-key in localStorage") :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] @@ -1433,17 +1265,33 @@ :examples $ [] :schema $ :: 'Fn $ {} (:return 'Bool) :args $ [] 'Dynamic + :features $ #{} :js-ffi 'json-pattern? $ %{} 'CodeEntry (:doc |) :code $ quote $ defn json-pattern? (text) or (starts-with? text |{) (starts-with? text |[) :examples $ [] :schema $ :: 'Fn $ {} (:return 'Bool) :args $ [] 'String + 'make-genai-tools $ %{} 'CodeEntry (:doc |) + :code $ quote $ defn make-genai-tools (search? has-url?) + cond + and search? has-url? + js-array + js-object $ :googleSearch $ js-object + js-object $ :urlContext $ js-object + search? $ js-array $ js-object + :googleSearch $ js-object + has-url? $ js-array $ js-object + :urlContext $ js-object + true $ js-array + :examples $ [] + :schema $ :: 'Fn $ {} (:return 'Dynamic) + :args $ [] 'Bool 'Bool + :features $ #{} :js-ffi 'messages->anthropic $ %{} 'CodeEntry (:doc |) :code $ quote $ defn messages->anthropic (messages) to-js-data $ map messages $ fn (m) - hint-fn $ {} $ :args - [] 'app.schema/ChatMessage + hint-fn $ {} $ :args ([] 'app.schema/ChatMessage) {} :role $ if = :assistant $ :role m @@ -1456,8 +1304,7 @@ 'messages->gemini $ %{} 'CodeEntry (:doc |) :code $ quote $ defn messages->gemini (messages) to-js-data $ map messages $ fn (m) - hint-fn $ {} $ :args - [] 'app.schema/ChatMessage + hint-fn $ {} $ :args ([] 'app.schema/ChatMessage) {} :role $ if = :assistant $ :role m @@ -1471,8 +1318,7 @@ 'messages->openai $ %{} 'CodeEntry (:doc |) :code $ quote $ defn messages->openai (messages) to-js-data $ map messages $ fn (m) - hint-fn $ {} $ :args - [] 'app.schema/ChatMessage + hint-fn $ {} $ :args ([] 'app.schema/ChatMessage) {} :role $ if = :assistant $ :role m @@ -1484,12 +1330,7 @@ :features $ #{} :js-ffi 'model-storage-key $ %{} 'CodeEntry (:doc |) :code $ quote $ defn model-storage-key (model) - case-default model |gemini-key (:claude-3.7 |claude-key) - :openrouter/anthropic/claude-sonnet-4 |openrouter-key - :openrouter/anthropic/claude-sonnet-4.5 |openrouter-key - :openrouter/anthropic/claude-opus-4 |openrouter-key - :openrouter/anthropic/claude-3.7-sonnet:thinking |openrouter-key - :openrouter/google/gemini-2.5-pro-preview |openrouter-key + case-default model |gemini-key (:claude-3.7 |claude-key) (:openrouter/anthropic/claude-sonnet-4 |openrouter-key) (:openrouter/anthropic/claude-sonnet-4.5 |openrouter-key) (:openrouter/anthropic/claude-opus-4 |openrouter-key) (:openrouter/anthropic/claude-3.7-sonnet:thinking |openrouter-key) (:openrouter/google/gemini-2.5-pro-preview |openrouter-key) :openrouter/google/gemini-2.5-flash-preview-05-20 |openrouter-key :openrouter/openai/gpt-5 |openrouter-key :openrouter/deepseek/deepseek-chat-v3.1 |openrouter-key @@ -1498,8 +1339,7 @@ :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] 'Tag - :tests $ [] $ %{} 'TestEntry - :name |maps-provider-keys + :tests $ [] $ %{} 'TestEntry (:name |maps-provider-keys) :code $ quote $ do assert= |gemini-key $ model-storage-key :gemini assert= |openrouter-key $ model-storage-key :openrouter/openai/gpt-5 @@ -1507,21 +1347,7 @@ assert= |claude-key $ model-storage-key :claude-3.7 'models-menu $ %{} 'CodeEntry (:doc |) :code $ quote $ def models-menu - [] - :: :item :gemini-3.5-flash-lite "|Gemini Flash Lite 3.5" - :: :item :gemini-3.6-flash "|Gemini Flash 3.6" - :: :item :gemini-3.7-flash "|Gemini Flash 3.7" - :: :item :gemini-3.8-flash "|Gemini Flash 3.8" - :: :item :gemini-flash "|Gemini Flash 3" - :: :item :gemini-3.5-flash "|Gemini Flash 3.5" - :: :item :gemini-pro "|Gemini Pro 3.1" - :: :item :gemini-3.1-flash-lite-preview "|Gemini Flash Lite 3.1" - :: :item :flash-imagen "|Flash Imagen" - :: :item :imagen-4 "|Imagen 4" - :: :item :gemma "|Gemma 3 27b" - :: :item :openrouter/anthropic/claude-sonnet-4.5 "|Openrouter Claude Sonnet 4.5" - :: :item :openrouter/anthropic/claude-opus-4 "|Openrouter Claude Opus 4" - :: :item :openrouter/google/gemini-2.5-pro-preview "|Openrouter Google Gemini 2.5 pro preview" + [] (:: :item :gemini-3.5-flash-lite "|Gemini Flash Lite 3.5") (:: :item :gemini-3.6-flash "|Gemini Flash 3.6") (:: :item :gemini-3.7-flash "|Gemini Flash 3.7") (:: :item :gemini-3.8-flash "|Gemini Flash 3.8") (:: :item :gemini-flash "|Gemini Flash 3") (:: :item :gemini-3.5-flash "|Gemini Flash 3.5") (:: :item :gemini-pro "|Gemini Pro 3.1") (:: :item :gemini-3.1-flash-lite-preview "|Gemini Flash Lite 3.1") (:: :item :flash-imagen "|Flash Imagen") (:: :item :imagen-4 "|Imagen 4") (:: :item :gemma "|Gemma 3 27b") (:: :item :openrouter/anthropic/claude-sonnet-4.5 "|Openrouter Claude Sonnet 4.5") (:: :item :openrouter/anthropic/claude-opus-4 "|Openrouter Claude Opus 4") (:: :item :openrouter/google/gemini-2.5-pro-preview "|Openrouter Google Gemini 2.5 pro preview") :: :item :openrouter/google/gemini-2.5-flash-preview-05-20 "|Openrouter Google Gemini 2.5 flash preview" :: :item :openrouter/openai/gpt-5 "|Openrouter GPT 5" :: :item :openrouter/deepseek/deepseek-chat-v3.1 "|Openrouter deepseek-chat-v3.1" @@ -1532,69 +1358,59 @@ :schema $ :: 'List 'normalize-chat-state $ %{} 'CodeEntry (:doc |) :code $ quote $ defn normalize-chat-state (raw) - hint-fn $ {} - :args $ [] 'T - :return 'app.schema/ChatState - :features $ #{} :js-ffi - :generics $ [] 'T if (struct? raw) let state $ unsafe-coerce raw 'app.schema/ChatState - %{} app.schema/ChatState - :answer $ stream-text $ :answer state - :loading? $ :loading? state - :done? $ :done? state - :messages $ :messages state - :model $ if - tag? $ :model state - :model state - , :gemini - :thinking $ stream-text $ :thinking state + app.schema/ChatState :answer + stream-text $ :answer state + , :loading? (:loading? state) :done? (:done? state) :messages (:messages state) :model + if + tag? $ :model state + :model state + , :gemini + , :thinking $ stream-text $ :thinking state let data $ unsafe-coerce raw $ :: 'Map 'Tag 'Dynamic loading-value $ option:unwrap-or (get data :loading?) false done-value $ option:unwrap-or (get data :done?) true model-value $ option:unwrap-or (get data :model) :gemini - %{} app.schema/ChatState - :answer $ stream-text $ option:unwrap-or (get data :answer) | - :loading? $ case-default loading-value false (true true) (false false) - :done? $ case-default done-value true (true true) (false false) - :messages $ unsafe-coerce - option:unwrap-or (get data :messages) ([]) - :: 'List 'app.schema/ChatMessage - :model $ if (tag? model-value) model-value :gemini - :thinking $ stream-text $ option:unwrap-or (get data :thinking) | - :examples $ [] - :schema $ :: 'Fn $ {} - :return 'app.schema/ChatState + app.schema/ChatState :answer + stream-text $ option:unwrap-or (get data :answer) | + , :loading? + case-default loading-value false (true true) (false false) + , :done? + case-default done-value true (true true) (false false) + , :messages + unsafe-coerce + option:unwrap-or (get data :messages) ([]) + :: 'List 'app.schema/ChatMessage + , :model + if (tag? model-value) model-value :gemini + , :thinking $ stream-text $ option:unwrap-or (get data :thinking) | + :examples $ [] + :schema $ :: 'Fn $ {} (:return 'app.schema/ChatState) :args $ [] 'T :features $ #{} :js-ffi :generics $ [] 'T - :tests $ [] $ %{} 'TestEntry - :name |legacy-nil-fields + :tests $ [] $ %{} 'TestEntry (:name |legacy-nil-fields) :code $ quote $ let - legacy $ unsafe-coerce - normalize-chat-state $ {} (:answer nil) (:loading? false) (:done? true) - :messages $ [] - :model :gemini - :thinking nil - , 'app.schema/ChatState - missing-done $ unsafe-coerce - normalize-chat-state $ {} (:answer |) (:loading? false) - :messages $ [] - :model :gemini - :thinking | - , 'app.schema/ChatState - invalid-done $ unsafe-coerce - normalize-chat-state $ {} (:answer |) (:loading? false) (:done? nil) - :messages $ [] - :model :gemini - :thinking | - , 'app.schema/ChatState + legacy $ normalize-chat-state $ {} (:answer nil) (:loading? false) (:done? true) + :messages $ [] + :model :gemini + :thinking nil + missing-done $ normalize-chat-state $ {} (:answer |) (:loading? false) + :messages $ [] + :model :gemini + :thinking | + invalid-done $ normalize-chat-state $ {} (:answer |) (:loading? false) (:done? nil) + :messages $ [] + :model :gemini + :thinking | is $ = | $ :answer legacy is $ = | $ :thinking legacy is $ :done? missing-done is $ :done? invalid-done + :tags $ #{} :regression :unit 'on-fill $ %{} 'CodeEntry (:doc |) :code $ quote $ defn on-fill (cursor state on-submit) %{} respo.schema/RespoListener (:name :on-fill) @@ -1604,41 +1420,24 @@ let submit? $ option:unwrap-or (get info :submit?) true text $ option:unwrap-or (get info :text) | - do - dispatch! (:: :states cursor) (assoc state :content text) - if submit? - on-submit text (:search? state) (:think? state) dispatch! - , nil + dispatch! (:: :states cursor) (assoc state :content text) + if submit? + on-submit text (:search? state) (:think? state) dispatch! + , nil :examples $ [] :schema $ :: 'Fn $ {} (:return 'Dynamic) :args $ [] 'Dynamic 'app.schema/MessageBoxState 'Dynamic - 'pattern-spaced-code $ %{} 'CodeEntry (:doc |) - :code $ quote $ def pattern-spaced-code - noted "|temp fix of nested code block" $ &raw-code "|/\\n\\s+```/g" - :examples $ [] - :schema $ :: 'Dynamic 'pick-model $ %{} 'CodeEntry (:doc |) :code $ quote $ defn pick-model (variant) - case-default variant |gemini-3.5-flash - :gemini-3.5-flash-lite |gemini-3.1-flash-lite - :gemini-3.6-flash |gemini-3.5-flash - :gemini-3.7-flash |gemini-3.7-flash - :gemini-3.8-flash |gemini-3.8-flash - :gemini-3.5-flash-lite |gemini-3.1-flash-lite - :gemini-3.1-flash-lite-preview |gemini-3.1-flash-lite - :gemini-pro |gemini-3.1-pro-preview - :gemma |gemma-3-27b-it + case-default variant |gemini-3.5-flash (:gemini-3.5-flash-lite |gemini-3.1-flash-lite) (:gemini-3.6-flash |gemini-3.5-flash) (:gemini-3.7-flash |gemini-3.7-flash) (:gemini-3.8-flash |gemini-3.8-flash) (:gemini-3.5-flash-lite |gemini-3.1-flash-lite) (:gemini-3.1-flash-lite-preview |gemini-3.1-flash-lite) (:gemini-pro |gemini-3.1-pro-preview) (:gemma |gemma-3-27b-it) :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) :args $ [] 'Tag 'required-key! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn required-key! (storage-key prompt-text) let - stored $ js/localStorage.getItem storage-key - key $ if - js-present-dynamic? stored - unsafe-coerce stored 'String - , | + stored $ browser/storage-get-or storage-key | + key stored if (blank? key) raise $ new js/Error prompt-text , key @@ -1660,8 +1459,7 @@ assoc store :sessions $ append sessions updated-session , store :examples $ [] - :schema $ :: 'Fn $ {} - :return 'app.schema/Store + :schema $ :: 'Fn $ {} (:return 'app.schema/Store) :args $ [] 'app.schema/Store 'app.schema/ChatState 'stream-text $ %{} 'CodeEntry (:doc |) :code $ quote $ defn stream-text (value) @@ -1673,46 +1471,41 @@ if (string? value) value | :examples $ [] :schema $ :: 'Fn $ {} (:return 'String) - :args $ [] 'T + :args $ [] 'Dynamic :features $ #{} :js-ffi - :generics $ [] 'T :tests $ [] %{} 'TestEntry (:name |keeps-string) :code $ quote $ is = |hello $ stream-text |hello :tags $ #{} :unit - %{} 'TestEntry - :name |ignores-non-string + %{} 'TestEntry (:name |ignores-non-string) :code $ quote $ is = | $ stream-text nil :tags $ #{} :regression :unit 'style-a-toggler $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-a-toggler {} - |& $ {} (:cursor :pointer) - :background-color :white - :color :black + |& $ {} (:cursor :pointer) (:background-color :white) (:color :black) "|.focus-within &" $ {} $ :color :black :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-abort-close $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-abort-close {} $ |& $ {} (:vertical-align :middle) (:font-size 10) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-app-global $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-app-global {} str "|& ." style-code-block {} $ :max-width |90vw - |& $ {} (:color |#999) - :transition-duration |300ms + |& $ {} (:color |#999) (:transition-duration |300ms) :background-color $ hsl 0 0 98 :touch-action :none |&:hover $ {} (:color |#777) :background-color $ hsl 0 0 100 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-archive-close $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-archive-close {} @@ -1721,7 +1514,7 @@ :transition-duration |200ms |&:hover $ {} $ :color (hsl 0 0 20) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-archive-header $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-archive-header {} $ |& $ {} (:padding "|12px 16px") @@ -1732,7 +1525,7 @@ :align-items :center :justify-content :space-between :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-archive-row $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-archive-row {} $ |& $ {} (:padding |12px) @@ -1741,22 +1534,22 @@ :justify-content :space-between :align-items :center :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-checkbox $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-checkbox {} $ |& $ {} (:cursor :pointer) (:user-select :none) (:font-size 12) (:line-height |28px) (:vertical-align :middle) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-clear $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-clear {} $ |& $ {} (:opacity 0.4) (:padding "|4px 8px") (:display :inline-block) (:height |24px) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-code-content $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-code-content {} $ |& $ {} (:line-height |1.5) (:font-size 13) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-delete-button $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-delete-button {} @@ -1771,25 +1564,23 @@ |&:active $ {} (:opacity 1) :color $ hsl 0 90 40 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-fill $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-fill {} - |& $ {} (:cursor :pointer) (:user-select :none) (:display :inline-flex) (:align-items :center) (:justify-content :center) - :transition-duration |200ms + |& $ {} (:cursor :pointer) (:user-select :none) (:display :inline-flex) (:align-items :center) (:justify-content :center) (:transition-duration |200ms) :color $ hsl 0 0 80 :margin "|0 4px 0 8px" |&:hover $ {} :color $ hsl 0 0 40 :transform "|scale(1.06)" :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-focus-box $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-focus-box - {} $ |& $ {} (:width |100%) (:border-radius 12) (:min-height 40) (:max-height 40) (:padding "|9px 12px") (:cursor :text) (:overflow :hidden) (:white-space :pre) (:text-overflow :ellipsis) - :background-color :transparent + {} $ |& $ {} (:width |100%) (:border-radius 12) (:min-height 40) (:max-height 40) (:padding "|9px 12px") (:cursor :text) (:overflow :hidden) (:white-space :pre) (:text-overflow :ellipsis) (:background-color :transparent) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-focus-link $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-focus-link {} @@ -1799,12 +1590,12 @@ :padding "|4px 0" |&:hover $ {} $ :text-decoration :underline :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-gap12 $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-gap12 {} $ |& $ {} (:gap 12) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-history-button $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-history-button {} $ |& $ {} (:font-size |20px) @@ -1816,7 +1607,7 @@ :justify-content :center |&:hover $ {} $ :color (hsl 200 80 50) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-history-count $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-history-count {} $ |& $ {} @@ -1824,44 +1615,40 @@ :font-size |12px :display :inline-block :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-image $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-image {} $ |& $ {} (:max-width |100%) (:align-self :flex-start) (:border-radius |6px) :border $ str "|1px solid " $ hsl 0 0 90 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-md-content $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-md-content {} $ "|& .md-p" $ {} (:margin "|16px 0") (:line-height |1.6) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-actions $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-actions {} $ |& $ {} (:margin-top 6) (:justify-content :flex-end) (:width |100%) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-area $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-area {} $ |& $ {} (:flex 2) (:overflow :scroll) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-assistant $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-assistant {} $ |& $ {} (:align-self :flex-start) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-box $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-box {} - |& $ {} (:width |100%) (:max-width 1200) (:right |50%) (:padding |8px) (:margin :auto) - :transition-duration |300ms - ; :transform "|translate(50%,0)" - :transition-property |height - |&:focus-within $ {} (:opacity 1) - ; :transform "|translate(50%,0)" - :examples $ [] - :schema $ :: 'Dynamic + |& $ {} (:width |100%) (:max-width 1200) (:right |50%) (:padding |8px) (:margin :auto) (:transition-duration |300ms) (; :transform "|translate(50%,0)") (:transition-property |height) + |&:focus-within $ {} (:opacity 1) (; :transform "|translate(50%,0)") + :examples $ [] + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-box-panel $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-box-panel {} @@ -1872,22 +1659,17 @@ :background-color $ hsl 0 0 100 0.9 :box-shadow $ str "|0 0px 8px " $ hsl 0 0 0 0.3 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-item $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-item {} $ |& $ {} (:line-height |1.6) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-list $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-list - {} $ |& $ {} (:flex 2) - :padding "|40px 16px 20vh 16px" - :width |100% - :max-width 1200 - :margin :auto - :position :relative - :examples $ [] - :schema $ :: 'Dynamic + {} $ |& $ {} (:flex 2) (:padding "|40px 16px 20vh 16px") (:width |100%) (:max-width 1200) (:margin :auto) (:position :relative) + :examples $ [] + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-role $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-role {} $ |& $ {} (:font-size 12) @@ -1895,12 +1677,12 @@ :margin-bottom 6 :padding-right |16px :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-text $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-text {} $ |& $ {} (:white-space :pre-wrap) (:line-height |1.6) (:margin 0) (:padding-right |16px) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-message-user $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-message-user {} @@ -1917,7 +1699,7 @@ :border-radius |2px |&::-webkit-scrollbar-track $ {} $ :background-color :transparent :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-more $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-more {} @@ -1931,12 +1713,12 @@ |&:hover $ {} $ :box-shadow str "|1px 1px 4px " $ hsl 0 0 0 0.2 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-reply-actions $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-reply-actions {} $ |& $ {} (:margin-top 6) (:justify-content :flex-start) (:width |100%) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-reply-button $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-reply-button {} @@ -1950,7 +1732,7 @@ |&:hover $ {} $ :box-shadow str "|1px 1px 4px " $ hsl 0 0 0 0.2 :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-session-item $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-session-item {} $ |& $ {} (:padding |12px) @@ -1961,34 +1743,31 @@ :gap |12px |:hover $ {} $ :background-color (hsl 0 0 96) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-sessions-list $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-sessions-list {} $ |& $ {} (:flex |1) (:overflow-y :auto) (:min-width |300px) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-submit $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-submit {} $ |& $ {} :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-textbox $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-textbox {} - |& $ {} (:border-radius 12) (:height "|max(100px,15vh)") (:width |100%) - :transition-duration |320ms - :border :none - :background-color :transparent + |& $ {} (:border-radius 12) (:height "|max(100px,15vh)") (:width |100%) (:transition-duration |320ms) (:border :none) (:background-color :transparent) |&.focus-within $ {} (:height "|max(240px,32vh)") (:border :none) (:box-shadow :none) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-textbox-compact $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-textbox-compact {} |& $ {} (:height 40) (:min-height 40) (:max-height 40) (:overflow :hidden) |&.focus-within $ {} (:height "|max(240px,32vh)") (:min-height |unset) (:max-height |unset) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'style-thinking $ %{} 'CodeEntry (:doc |) :code $ quote $ defstyle style-thinking {} @@ -2002,21 +1781,17 @@ :border $ str "|1px solid " $ hsl 0 0 90 "|& .md-p" $ {} $ :margin "|4px 0" :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Map 'Tag 'Dynamic 'submit-message! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn submit-message! (cursor state prompt-text search? think? model d!) - hint-fn $ {} (:async true) - :args $ [] 'List 'app.schema/ChatState 'String 'Bool 'Bool 'Tag 'Dynamic let - state1 $ unsafe-coerce - assoc state :messages $ append-user-message (:messages state) prompt-text - , 'app.schema/ChatState + state1 $ assoc state :messages $ append-user-message (:messages state) prompt-text *text $ atom | *thinking-text $ atom | model $ :model state d! cursor state1 try - do $ case-default model + case-default model js-await $ call-genai-msg! model cursor state1 prompt-text search? think? d! *text *thinking-text :gemini-3.1-flash-lite-preview $ js-await $ call-genai-msg! model cursor state1 prompt-text search? think? d! *text *thinking-text :gemini-pro $ js-await $ call-genai-msg! model cursor state1 prompt-text search? true d! *text *thinking-text @@ -2038,35 +1813,32 @@ :openrouter/deepseek/deepseek-chat-v3.1 $ js-await $ call-openrouter! cursor state1 prompt-text |deepseek/deepseek-chat-v3.1 true d! *text :deepseek-v4-pro $ js-await $ call-openrouter! cursor state1 prompt-text |deepseek-v4-pro true d! *text :deepseek-v4-flash $ js-await $ call-openrouter! cursor state1 prompt-text |deepseek-v4-flash true d! *text - fn (e) - do - js/console.error :msg-buffer-request-failed e - let - err-text $ str |Failed-to-load: e - d! cursor $ -> state1 (assoc :answer err-text) (assoc :loading? false) (assoc :done? true) - assoc :messages $ upsert-assistant-message (:messages state1) err-text | + fn (e) (js/console.error :msg-buffer-request-failed e) + let + err-text $ str |Failed-to-load: e + d! cursor $ -> state1 (assoc :answer err-text) (assoc :loading? false) (assoc :done? true) + assoc :messages $ upsert-assistant-message (:messages state1) err-text | :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Dynamic) - :args $ [] 'List 'app.schema/ChatState 'String 'Bool 'Bool 'Tag 'Dynamic + :schema $ :: 'Fn $ {} (:async true) (:return 'Dynamic) + :args $ [] (:: 'List 'Dynamic) 'app.schema/ChatState 'String 'Bool 'Bool 'Tag 'Dynamic :features $ #{} :js-ffi 'toggle-focus-within! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn toggle-focus-within! (respo-event active?) let raw-event $ option:unwrap-or (get respo-event :event) js/undefined - when - js-present-dynamic? raw-event + when (js-present-dynamic? raw-event) let event $ unsafe-coerce raw-event FocusEventHost target0 $ .-target event - when (js-present? target0) + when (js-present-dynamic? target0) let target $ unsafe-coerce target0 FocusTargetHost parent0 $ .-parentElement target - when (js-present? parent0) + when (js-present-dynamic? parent0) let parent $ unsafe-coerce parent0 FocusTargetHost box0 $ .-parentElement parent - when (js-present? box0) + when (js-present-dynamic? box0) let box $ unsafe-coerce box0 FocusTargetHost class-list $ .-classList target @@ -2091,21 +1863,23 @@ :code $ quote $ defn upsert-assistant-message (messages content thinking) let size $ count messages - option:fold (last messages) - fn () $ conj messages $ %{} ChatMessage (:role :assistant) (:content content) (:thinking thinking) + option:fold + assert-type (last messages) (:: 'Option 'app.schema/ChatMessage) + fn () + hint-fn $ {} $ :return (:: 'List 'app.schema/ChatMessage) + conj messages $ ChatMessage :role :assistant :content content :thinking thinking fn (last-msg) - hint-fn $ {} $ :args - [] 'app.schema/ChatMessage + hint-fn $ {} + :args $ [] 'app.schema/ChatMessage + :return $ :: 'List 'app.schema/ChatMessage if = :assistant $ :role last-msg assoc messages (dec size) -> last-msg (assoc :content content) (assoc :thinking thinking) - conj messages $ %{} ChatMessage (:role :assistant) (:content content) (:thinking thinking) + conj messages $ ChatMessage :role :assistant :content content :thinking thinking :examples $ [] :schema $ :: 'Fn $ {} - :args $ [] - :: 'List 'app.schema/ChatMessage - , 'String 'String + :args $ [] (:: 'List 'app.schema/ChatMessage) 'String 'String :return $ :: 'List 'app.schema/ChatMessage :ns $ %{} 'NsEntry (:doc |) :code $ quote $ ns app.comp.container @@ -2126,6 +1900,8 @@ respo-ui.util :refer $ tab-echo! app.schema :refer $ Store ChatState ChatSession ChatMessage MessageBoxState store calcit.test :refer $ [] is + js-ffi.browser :as browser + js-ffi.shared :as ffi 'app.config $ %{} 'FileEntry :defs $ {} 'Site $ %{} 'CodeEntry (:doc |) @@ -2133,21 +1909,29 @@ :examples $ [] :schema $ :: 'StructDef 'chrome-extension? $ %{} 'CodeEntry (:doc |) - :code $ quote $ def chrome-extension? + :code $ quote $ def chrome-extension? (detect-chrome-extension?) + :examples $ [] + :schema $ :: 'Bool + 'detect-chrome-extension? $ %{} 'CodeEntry (:doc |) + :code $ quote $ defn detect-chrome-extension? () let - runtime $ .?-runtime js/window.chrome + chrome $ unsafe-coerce js/window.chrome 'Dynamic + runtime $ .?-runtime chrome and (js-present? runtime) - js-present? $ .?-id runtime + let + typed-runtime $ unsafe-coerce runtime 'Dynamic + js-present? $ .?-id typed-runtime :examples $ [] - :schema $ :: 'Bool + :schema $ :: 'Fn $ {} (:return 'Bool) + :args $ [] + :features $ #{} :js-ffi 'dev? $ %{} 'CodeEntry (:doc |) :code $ quote $ def dev? = |dev $ option:unwrap-or (get-env |mode) |release :examples $ [] :schema $ :: 'Bool 'site $ %{} 'CodeEntry (:doc |) - :code $ quote $ %{} Site (:storage-key |msg-buffer) - :archive-key |msg-buffer-archive + :code $ quote $ Site :storage-key |msg-buffer :archive-key |msg-buffer-archive :examples $ [] :schema $ :: 'app.config/Site :ns $ %{} 'NsEntry (:doc |) @@ -2159,6 +1943,22 @@ -> reel-schema/reel (assoc :base schema/store) (assoc :store schema/store) :examples $ [] :schema $ :: 'Ref + 'ChromeDisconnectEventHost $ %{} 'CodeEntry (:doc |) + :code $ quote $ deftrait ChromeDisconnectEventHost + .add-listener! $ :: 'Fn $ {} (:return 'Unit) + :args $ [] 'app.main/ChromeDisconnectEventHost $ :: 'Fn + {} (:return 'Unit) + :args $ [] 'Dynamic + :examples $ [] + :ffi $ {} (:backend :js) (:kind :external-object) (:target :browser) + :names $ {} $ :add-listener! |addListener + :schema $ :: 'Trait + 'ChromePortHost $ %{} 'CodeEntry (:doc |) + :code $ quote $ deftrait ChromePortHost + :onDisconnect $ :: 'JsNullish 'app.main/ChromeDisconnectEventHost + :examples $ [] + :ffi $ {} (:backend :js) (:kind :external-object) (:target :browser) + :schema $ :: 'Trait 'DocumentHost $ %{} 'CodeEntry (:doc |) :code $ quote $ deftrait DocumentHost :visibilityState $ :: 'JsNullish 'String @@ -2177,25 +1977,28 @@ 'connect-to-worker! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn connect-to-worker! () when config/chrome-extension? $ let - port $ js/chrome.runtime.connect $ js-object (:name |mySidepanel) - when (js-present? port) - let - on-disconnect $ .?-onDisconnect port - when (js-present? on-disconnect) - .!addListener on-disconnect $ fn (event) - do - println |Worker-disconnected-retrying - js/setTimeout connect-to-worker! 500 - :examples $ [] - :schema $ :: 'Dynamic + port $ unsafe-coerce + js/chrome.runtime.connect $ js-object $ :name |mySidepanel + , 'app.main/ChromePortHost + let + on-disconnect $ .-onDisconnect port + when (js-present? on-disconnect) + let + listener $ assert-type on-disconnect 'app.main/ChromeDisconnectEventHost + listener .add-listener! $ fn (event) (println |Worker-disconnected-retrying) (js/setTimeout connect-to-worker! 500) &unit + , &unit + :examples $ [] + :schema $ :: 'Fn $ {} (:return 'Unit) + :args $ [] + :features $ #{} :js-ffi 'dispatch! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn dispatch! (op) - when - and config/dev? $ not= op :states - js/console.log |Dispatch: op + when config/dev? $ js/console.log |Dispatch: op reset! *reel $ reel-updater updater @*reel op :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'Unit) + :args $ [] 'Enum + :features $ #{} :js-ffi 'extension-action $ %{} 'CodeEntry (:doc |) :code $ quote $ defn extension-action (message) js-nullish->option $ .-action message @@ -2203,7 +2006,7 @@ :schema $ :: 'Fn $ {} :args $ [] 'app.main/ExtensionMessageHost :features $ #{} :js-ffi - :return $ :: 'Option 'String + :return $ :: 'calcit.core/Option 'String 'hydrate-storage-later! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn hydrate-storage-later! () js/setTimeout @@ -2211,15 +2014,16 @@ raw $ js/localStorage.getItem $ :storage-key config/site when (js-present? raw) let - t-start $ unsafe-coerce (js/Date.now) 'Number - serialized $ unsafe-coerce raw 'String + t-start $ ffi/now-ms + serialized $ assert-type raw 'String dispatch! $ :: :hydrate-storage $ parse-cirru-edn serialized - println |Hydrated-in $ - - unsafe-coerce (js/Date.now) 'Number - , t-start + println |Hydrated-in $ - (ffi/now-ms) t-start , 0 + , &unit :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'Unit) + :args $ [] + :features $ #{} :js-ffi 'listen-extension! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn listen-extension! () js/chrome.runtime.onMessage.addListener $ fn (raw-message sender respond!) @@ -2303,15 +2107,16 @@ :args $ [] :features $ #{} :js-ffi 'mount-target $ %{} 'CodeEntry (:doc |) - :code $ quote $ def mount-target - js/document.querySelector |.app + :code $ quote $ def mount-target (js/document.querySelector |.app) :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'JsNullish 'Dynamic 'persist-storage! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn persist-storage! () - println "|Saved at" $ .!toISOString $ new js/Date + println "|Saved at" $ :iso $ ffi/date-now-snapshot js/localStorage.setItem (:storage-key config/site) - format-cirru-edn $ option:unwrap-or (get @*reel :store) schema/store + format-cirru-edn $ option:unwrap-or + assert-type (get @*reel :store) (:: 'Option 'app.schema/Store) + , schema/store :examples $ [] :schema $ :: 'Fn $ {} (:return 'Dynamic) :args $ [] @@ -2320,10 +2125,7 @@ :code $ quote $ defn reload! () if = false $ unsafe-coerce (js/Boolean build-errors) 'Bool - do (remove-watch *reel :changes) - remove-watch *archived-sessions :changes - remove-watch *viewing-archive-session :changes - clear-cache! + do (remove-watch *reel :changes) (remove-watch *archived-sessions :changes) (remove-watch *viewing-archive-session :changes) (clear-cache!) add-watch *reel :changes $ fn (reel prev) (render-app!) add-watch *archived-sessions :changes $ fn (s prev) (render-app!) add-watch *viewing-archive-session :changes $ fn (s prev) (render-app!) @@ -2331,7 +2133,9 @@ hud! |ok~ |Ok hud! |error $ unsafe-coerce build-errors 'Dynamic :examples $ [] - :schema $ :: 'Dynamic + :schema $ :: 'Fn $ {} (:return 'Unit) + :args $ [] + :features $ #{} :js-ffi 'render-app! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn render-app! () let @@ -2360,9 +2164,7 @@ let deepseek-key $ js/localStorage.getItem |deepseek-key when (js-present? deepseek-key) - js/chrome.runtime.sendMessage $ js-object - :action |sync-deepseek-key - :key deepseek-key + js/chrome.runtime.sendMessage $ js-object (:action |sync-deepseek-key) (:key deepseek-key) js/chrome.storage.local.set $ js-object $ :deepseekKey deepseek-key :examples $ [] :schema $ :: 'Fn $ {} (:return 'Dynamic) @@ -2382,6 +2184,7 @@ |./calcit.build-errors :default build-errors |bottom-tip :default hud! respo.controller.client :refer $ send-to-component! + js-ffi.shared :as ffi 'app.schema $ %{} 'FileEntry :defs $ {} 'ChatMessage $ %{} 'CodeEntry (:doc |) @@ -2391,7 +2194,7 @@ 'ChatSession $ %{} 'CodeEntry (:doc |) :code $ quote $ defstruct ChatSession (:id 'String) (:created-at 'Number) :messages $ :: 'List 'app.schema/ChatMessage - :model 'Dynamic + :model 'Tag :preview 'String :is-history? 'Bool :examples $ [] @@ -2422,45 +2225,30 @@ :return 'app.schema/ChatSession :features $ #{} :js-ffi :generics $ [] 'T - if (struct? raw) - unsafe-coerce raw 'app.schema/ChatSession - decode-map-as raw 'app.schema/ChatSession + if (struct? raw) (assert-type raw 'app.schema/ChatSession) (decode-map-as raw 'app.schema/ChatSession) :examples $ [] - :schema $ :: 'Fn $ {} - :return 'app.schema/ChatSession + :schema $ :: 'Fn $ {} (:return 'app.schema/ChatSession) :args $ [] 'T :features $ #{} :js-ffi :generics $ [] 'T - :tests $ [] $ %{} 'TestEntry - :name |accepts-typed-and-map + :tests $ [] $ %{} 'TestEntry (:name |accepts-typed-and-map) :code $ quote $ let - typed $ %{} ChatSession (:id |typed) (:created-at 1) + typed $ ChatSession :id |typed :created-at 1 :messages ([]) :model :gemini :preview |Typed :is-history? true + typed-result $ normalize-chat-session typed + decoded $ normalize-chat-session $ {} (:id |decoded) (:created-at 2) :messages $ [] - :model :gemini - :preview |Typed - :is-history? true - typed-result $ assert-type - normalize-chat-session typed - , 'app.schema/ChatSession - decoded $ assert-type - normalize-chat-session $ {} (:id |decoded) (:created-at 2) - :messages $ [] - :model :anthropic - :preview |Decoded - :is-history? false - , 'app.schema/ChatSession + :model :anthropic + :preview |Decoded + :is-history? false is $ = |typed $ :id typed-result is $ = |decoded $ :id decoded is $ = :anthropic $ :model decoded :tags $ #{} :regression :unit 'store $ %{} 'CodeEntry (:doc |) :code $ quote $ def store - %{} Store - :states $ {} $ :cursor ([]) - :sessions $ [] - :current-session-id nil - :model nil - :archived-count 0 + Store :states + {} $ :cursor $ [] + , :sessions ([]) :current-session-id nil :model nil :archived-count 0 :examples $ [] :schema $ :: 'app.schema/Store :ns $ %{} 'NsEntry (:doc |) @@ -2475,27 +2263,28 @@ match op (:states cursor s) assoc store :states $ assoc-in (:states store) - concat cursor $ [] :data + concat + assert-type cursor $ :: 'List 'Tag + [] :data , s (:states-merge cursor s changes) let - path $ concat cursor $ [] :data + path $ concat + assert-type cursor $ :: 'List 'Tag + [] :data state $ option:unwrap-or get-in (:states store) path , s updated-states $ assoc-in (:states store) path $ if or (map? state) (struct? state) - noted |merge-base-latest-state $ merge state changes - do - js/console.warn |unknown-state-to-merge state - , state + noted |merge-base-latest-state $ merge-dynamic + assert-type state $ :: 'Map 'Tag 'Dynamic + assert-type changes $ :: 'Map 'Tag 'Dynamic + do (eprintln |unknown-state-to-merge state) state assoc store :states updated-states (:hydrate-storage data) - if (struct? data) - unsafe-coerce data 'app.schema/Store - decode-map-as - dissoc data :current-chapter-id - , 'app.schema/Store + if (struct? data) (assert-type data 'app.schema/Store) + decode-map-as (dissoc data :current-chapter-id) 'app.schema/Store (:change-model) if = (:model store) :anthropic @@ -2503,28 +2292,27 @@ assoc store :model :anthropic (:save-session state) let - store1 $ save-current-session store state + store1 $ save-current-session store $ assert-type state 'app.schema/ChatState assoc store1 :current-session-id nil - (:session session-id id) - assoc store :current-session-id id + (:session session-id id) (assoc store :current-session-id id) (:load-session cursor state session) let - typed-session $ assert-type - app.schema/normalize-chat-session session - , 'app.schema/ChatSession + typed-session $ assert-type (app.schema/normalize-chat-session session) 'app.schema/ChatSession store1 $ assoc store :states $ assoc-in (:states store) - concat cursor $ [] :data + concat + assert-type cursor $ :: 'List 'Tag + [] :data -> state assoc :messages $ :messages typed-session assoc :done? true assoc store1 :current-session-id $ :id typed-session (:remove-session id) - assoc store :sessions $ filter - or (:sessions store) ([]) + assoc store :sessions $ filter (:sessions store) fn (s) - hint-fn $ {} $ :args - [] 'app.schema/ChatSession - not $ = (:id s) id + hint-fn $ {} + :args $ [] 'app.schema/ChatSession + :return 'Bool + not $ = (:id s) (assert-type id 'String) (:archive-sessions new-count) -> store assoc :sessions $ [] @@ -2538,8 +2326,7 @@ assoc :current-session-id nil _ $ do (eprintln "|unknown op:" op) store :examples $ [] - :schema $ :: 'Fn $ {} - :return 'app.schema/Store + :schema $ :: 'Fn $ {} (:return 'app.schema/Store) :args $ [] 'app.schema/Store 'Enum 'String 'Number :ns $ %{} 'NsEntry (:doc |) :code $ quote $ ns app.updater diff --git a/config/calcit-quality.cirru b/config/calcit-quality.cirru index c52c212..65caf90 100644 --- a/config/calcit-quality.cirru +++ b/config/calcit-quality.cirru @@ -10,12 +10,12 @@ :unresolved 1 :unsafeCoerce 0 |app.comp.container/*archived-sessions $ {} (:codeDynamic 0) - :codeNil 0 + :codeNil 1 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 + :schemaDynamic 0 :typeNone 0 - :typeNotFull 1 + :typeNotFull 0 :unresolved 1 :unsafeCoerce 0 |app.comp.container/*gen-ai-new $ {} (:codeDynamic 0) @@ -46,14 +46,23 @@ :unresolved 1 :unsafeCoerce 0 |app.comp.container/*viewing-archive-session $ {} (:codeDynamic 0) - :codeNil 0 + :codeNil 1 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 + :schemaDynamic 0 :typeNone 0 - :typeNotFull 1 + :typeNotFull 0 :unresolved 1 :unsafeCoerce 0 + |app.comp.container/abort-dynamic! $ {} (:codeDynamic 0) + :codeNil 0 + :declaredOptional 0 + :deprecatedCalls 0 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 1 + :unresolved 0 + :unsafeCoerce 1 |app.comp.container/call-anthropic-msg! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -62,7 +71,7 @@ :typeNone 0 :typeNotFull 1 :unresolved 0 - :unsafeCoerce 5 + :unsafeCoerce 4 |app.comp.container/call-flash-imagen-msg! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -71,7 +80,7 @@ :typeNone 0 :typeNotFull 1 :unresolved 0 - :unsafeCoerce 16 + :unsafeCoerce 15 |app.comp.container/call-genai-msg! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -80,7 +89,7 @@ :typeNone 0 :typeNotFull 1 :unresolved 0 - :unsafeCoerce 7 + :unsafeCoerce 5 |app.comp.container/call-imagen-4-msg! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -89,7 +98,7 @@ :typeNone 0 :typeNotFull 1 :unresolved 0 - :unsafeCoerce 10 + :unsafeCoerce 9 |app.comp.container/call-openrouter! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -98,43 +107,34 @@ :typeNone 0 :typeNotFull 1 :unresolved 0 - :unsafeCoerce 9 + :unsafeCoerce 8 |app.comp.container/clear-image-cache! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 0 + :unresolved 0 :unsafeCoerce 1 |app.comp.container/comp-abort $ {} (:codeDynamic 0) :codeNil 1 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 2 - :unsafeCoerce 1 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 0 + :unresolved 1 + :unsafeCoerce 0 |app.comp.container/comp-container $ {} (:codeDynamic 0) - :codeNil 9 + :codeNil 12 :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 0 :typeNone 0 :typeNotFull 1 - :unresolved 9 - :unsafeCoerce 7 - |app.comp.container/comp-fill $ {} (:codeDynamic 0) - :codeNil 0 - :declaredOptional 0 - :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 0 + :unresolved 12 + :unsafeCoerce 5 |app.comp.container/comp-message-box $ {} (:codeDynamic 0) :codeNil 3 :declaredOptional 0 @@ -152,15 +152,6 @@ :typeNone 0 :typeNotFull 1 :unresolved 1 - :unsafeCoerce 2 - |app.comp.container/create-session $ {} (:codeDynamic 0) - :codeNil 0 - :declaredOptional 0 - :deprecatedCalls 0 - :schemaDynamic 0 - :typeNone 0 - :typeNotFull 0 - :unresolved 0 :unsafeCoerce 1 |app.comp.container/decode-genai-chunk $ {} (:codeDynamic 0) :codeNil 0 @@ -184,29 +175,29 @@ :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 0 + :unresolved 0 :unsafeCoerce 2 - |app.comp.container/get-deepinfra-key! $ {} (:codeDynamic 0) + |app.comp.container/js-present-dynamic? $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 + :schemaDynamic 0 + :typeNone 0 :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 0 - |app.comp.container/js-present-dynamic? $ {} (:codeDynamic 0) + :unresolved 0 + :unsafeCoerce 1 + |app.comp.container/make-genai-tools $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 + :schemaDynamic 0 :typeNone 0 :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 1 + :unresolved 0 + :unsafeCoerce 0 |app.comp.container/messages->anthropic $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -261,30 +252,21 @@ :typeNotFull 1 :unresolved 4 :unsafeCoerce 0 - |app.comp.container/pattern-spaced-code $ {} (:codeDynamic 0) - :codeNil 0 - :declaredOptional 0 - :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 0 - |app.comp.container/required-key! $ {} (:codeDynamic 0) + |app.comp.container/stream-text $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 0 :typeNone 0 - :typeNotFull 0 + :typeNotFull 1 :unresolved 0 - :unsafeCoerce 1 + :unsafeCoerce 0 |app.comp.container/style-a-toggler $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -293,7 +275,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -302,7 +284,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -311,7 +293,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -320,7 +302,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -329,7 +311,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -338,7 +320,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -347,7 +329,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -356,7 +338,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -365,7 +347,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -374,7 +356,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -383,7 +365,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -392,7 +374,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -401,7 +383,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -410,7 +392,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -419,7 +401,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -428,7 +410,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -437,7 +419,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -446,7 +428,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -455,7 +437,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -464,7 +446,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -473,7 +455,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -482,7 +464,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -491,7 +473,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -500,7 +482,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -509,7 +491,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -518,7 +500,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -527,7 +509,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -536,7 +518,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -545,7 +527,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -554,7 +536,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -563,7 +545,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -572,7 +554,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -581,7 +563,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -590,7 +572,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -599,7 +581,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -608,7 +590,7 @@ :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 @@ -620,7 +602,7 @@ :typeNone 0 :typeNotFull 1 :unresolved 0 - :unsafeCoerce 1 + :unsafeCoerce 0 |app.comp.container/toggle-focus-within! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -630,42 +612,33 @@ :typeNotFull 1 :unresolved 0 :unsafeCoerce 4 - |app.main/*reel $ {} (:codeDynamic 0) + |app.config/detect-chrome-extension? $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 + :schemaDynamic 0 :typeNone 0 - :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 0 - |app.main/connect-to-worker! $ {} (:codeDynamic 0) - :codeNil 0 - :declaredOptional 0 - :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 0 - |app.main/dispatch! $ {} (:codeDynamic 0) + :typeNotFull 0 + :unresolved 0 + :unsafeCoerce 2 + |app.main/*reel $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 :schemaDynamic 1 - :typeNone 1 + :typeNone 0 :typeNotFull 1 :unresolved 1 :unsafeCoerce 0 - |app.main/hydrate-storage-later! $ {} (:codeDynamic 0) + |app.main/connect-to-worker! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 - :unsafeCoerce 3 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 0 + :unresolved 0 + :unsafeCoerce 1 |app.main/listen-extension! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 @@ -688,10 +661,10 @@ :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 + :schemaDynamic 0 + :typeNone 0 :typeNotFull 1 - :unresolved 1 + :unresolved 0 :unsafeCoerce 0 |app.main/persist-storage! $ {} (:codeDynamic 0) :codeNil 0 @@ -706,19 +679,19 @@ :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 0 + :unresolved 0 :unsafeCoerce 2 |app.main/render-app! $ {} (:codeDynamic 0) :codeNil 0 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 1 - :typeNone 1 - :typeNotFull 1 - :unresolved 1 + :schemaDynamic 0 + :typeNone 0 + :typeNotFull 0 + :unresolved 0 :unsafeCoerce 2 |app.main/sync-gemini-key! $ {} (:codeDynamic 0) :codeNil 0 @@ -729,15 +702,6 @@ :typeNotFull 1 :unresolved 0 :unsafeCoerce 0 - |app.schema/normalize-chat-session $ {} (:codeDynamic 0) - :codeNil 0 - :declaredOptional 0 - :deprecatedCalls 0 - :schemaDynamic 0 - :typeNone 0 - :typeNotFull 0 - :unresolved 0 - :unsafeCoerce 1 |app.schema/store $ {} (:codeDynamic 0) :codeNil 2 :declaredOptional 0 @@ -755,16 +719,16 @@ :typeNone 0 :typeNotFull 0 :unresolved 3 - :unsafeCoerce 1 + :unsafeCoerce 0 :metrics $ {} (:codeDynamic 0) - :codeNil 20 + :codeNil 25 :declaredOptional 0 :deprecatedCalls 0 - :schemaDynamic 61 - :typeNone 49 - :typeNotFull 75 - :unresolved 81 - :unsafeCoerce 99 + :schemaDynamic 46 + :typeNone 0 + :typeNotFull 65 + :unresolved 71 + :unsafeCoerce 85 :scope $ {} (:includeDependencies false) :namespace nil :namespacePrefix nil diff --git a/deps.cirru b/deps.cirru index 24885bb..a607865 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,8 +1,9 @@ -{} (:calcit-version |0.14.16) - :version |0.0.6 - :dependencies $ {} (|Respo/alerts.calcit |0.10.33) - |Respo/reel.calcit |0.6.22 - |Respo/respo-markdown.calcit |0.4.35 - |Respo/respo-ui.calcit |0.7.21 - |Respo/respo.calcit |0.16.101 +{} (:calcit-version |0.15.3) + :version |0.0.7 + :dependencies $ {} (|Respo/alerts.calcit |0.10.39) + |Respo/reel.calcit |0.6.27 + |Respo/respo-markdown.calcit |0.4.39 + |Respo/respo-ui.calcit |0.7.25 + |Respo/respo.calcit |0.16.104 + |calcit-lang/js-ffi |0.1.19 diff --git a/package.json b/package.json index 5181e27..0cdef44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "@calcit/procs": "0.14.16", + "@calcit/procs": "0.15.3", "@google/genai": "^2.20.0", "@tiye/main-fonts": "0.0.1", "axios": "^1.15.0", @@ -14,8 +14,8 @@ "vite": "8.2.2" }, "scripts": { - "build": "rm -rfv dist && calcit calcit.cirru --compat-types js && yarn vite build --base ./ && rm -rfv extension/dist && cp -vr dist extension/" + "build": "rm -rfv dist && calcit calcit.cirru js && yarn vite build --base ./ && rm -rfv extension/dist && cp -vr dist extension/" }, - "version": "0.0.6", + "version": "0.0.7", "packageManager": "yarn@4.12.0" } diff --git a/yarn.lock b/yarn.lock index 05cd295..2af1866 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,14 +5,14 @@ __metadata: version: 8 cacheKey: 10c0 -"@calcit/procs@npm:0.14.16": - version: 0.14.16 - resolution: "@calcit/procs@npm:0.14.16" +"@calcit/procs@npm:0.15.3": + version: 0.15.3 + resolution: "@calcit/procs@npm:0.15.3" dependencies: "@calcit/ternary-tree": "npm:0.0.26" "@cirru/parser.ts": "npm:^0.0.9" "@cirru/writer.ts": "npm:^0.1.9" - checksum: 10c0/627197e5b42d6eb550eab24c2fd83b9cddfdc0cd00b9bd8a462e53f211267d36514fecbe77f722b5d591ca7e2d0c49bbd28f7e0d9cf0652eee202ed8f4f6794e + checksum: 10c0/cea489617187a8b45a615544c8184d8a49e0def66986ff4c5b25157e331c68138ab399881424ce36b1dfc59a548bc0c167f26a46cd919e18b28ade022f850844 languageName: node linkType: hard @@ -1503,7 +1503,7 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@calcit/procs": "npm:0.14.16" + "@calcit/procs": "npm:0.15.3" "@google/genai": "npm:^2.20.0" "@tiye/main-fonts": "npm:0.0.1" axios: "npm:^1.15.0"