taskchef 8.6.2 → 8.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +8 -4
- package/assets/README.md +19 -0
- package/assets/taskchef-app-rounded.png +0 -0
- package/assets/taskchef-avatar-square.png +0 -0
- package/assets/taskchef-avatar-square.svg +8 -0
- package/assets/taskchef-sidebar-dark.svg +6 -0
- package/assets/taskchef-sidebar-light.svg +6 -0
- package/assets/taskchef-sidebar.svg +6 -0
- package/docs/advanced-guide.md +20 -29
- package/docs/dashboard-ui.md +15 -0
- package/docs/diagrams/taskchef-next-reply-lookup.drawio +38 -0
- package/docs/diagrams/taskchef-next-reply-lookup.drawio.png +0 -0
- package/docs/diagrams/taskchef-next-storage.drawio +41 -0
- package/docs/diagrams/taskchef-next-storage.drawio.png +0 -0
- package/docs/images/taskchef-pr-icon-comparison.jpg +0 -0
- package/docs/images/taskchef-unified-status-popup.jpg +0 -0
- package/docs/mcp-app.md +278 -6
- package/docs/spec.md +24 -69
- package/docs/taskchef-next-github.md +206 -0
- package/docs/taskchef-next-status-research.md +102 -0
- package/docs/taskchef-next-storage.md +167 -0
- package/docs/workflows.md +4 -2
- package/node_modules/@napi-rs/keyring/LICENSE +21 -0
- package/node_modules/@napi-rs/keyring/README.md +45 -0
- package/node_modules/@napi-rs/keyring/index.d.ts +232 -0
- package/node_modules/@napi-rs/keyring/index.js +706 -0
- package/node_modules/@napi-rs/keyring/keytar.d.ts +53 -0
- package/node_modules/@napi-rs/keyring/keytar.js +26 -0
- package/node_modules/@napi-rs/keyring/package.json +126 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/README.md +3 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/keyring.darwin-arm64.node +0 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/package.json +32 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/README.md +3 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/keyring.darwin-x64.node +0 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/package.json +32 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/keyring.linux-arm64-gnu.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/keyring.linux-arm64-musl.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/keyring.linux-x64-gnu.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/keyring.linux-x64-musl.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/package.json +35 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/README.md +3 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/keyring.win32-arm64-msvc.node +0 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/package.json +32 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/README.md +3 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/keyring.win32-x64-msvc.node +0 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/package.json +32 -0
- package/node_modules/@types/debug/LICENSE +21 -0
- package/node_modules/@types/debug/README.md +69 -0
- package/node_modules/@types/debug/index.d.ts +50 -0
- package/node_modules/@types/debug/package.json +58 -0
- package/node_modules/@types/mdast/LICENSE +21 -0
- package/node_modules/@types/mdast/README.md +15 -0
- package/node_modules/@types/mdast/index.d.ts +1123 -0
- package/node_modules/@types/mdast/package.json +47 -0
- package/node_modules/@types/ms/LICENSE +21 -0
- package/node_modules/@types/ms/README.md +82 -0
- package/node_modules/@types/ms/index.d.ts +63 -0
- package/node_modules/@types/ms/package.json +26 -0
- package/node_modules/@types/unist/LICENSE +21 -0
- package/node_modules/@types/unist/README.md +15 -0
- package/node_modules/@types/unist/index.d.ts +119 -0
- package/node_modules/@types/unist/package.json +60 -0
- package/node_modules/character-entities/index.d.ts +6 -0
- package/node_modules/character-entities/index.js +2132 -0
- package/node_modules/character-entities/license +22 -0
- package/node_modules/character-entities/package.json +78 -0
- package/node_modules/character-entities/readme.md +152 -0
- package/node_modules/decode-named-character-reference/index.d.ts +13 -0
- package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.js +32 -0
- package/node_modules/decode-named-character-reference/index.js +19 -0
- package/node_modules/decode-named-character-reference/license +22 -0
- package/node_modules/decode-named-character-reference/package.json +90 -0
- package/node_modules/decode-named-character-reference/readme.md +136 -0
- package/node_modules/dequal/dist/index.js +86 -0
- package/node_modules/dequal/dist/index.min.js +1 -0
- package/node_modules/dequal/dist/index.mjs +84 -0
- package/node_modules/dequal/index.d.ts +1 -0
- package/node_modules/dequal/license +21 -0
- package/node_modules/dequal/lite/index.d.ts +1 -0
- package/node_modules/dequal/lite/index.js +31 -0
- package/node_modules/dequal/lite/index.min.js +1 -0
- package/node_modules/dequal/lite/index.mjs +29 -0
- package/node_modules/dequal/package.json +57 -0
- package/node_modules/dequal/readme.md +112 -0
- package/node_modules/devlop/lib/default.js +9 -0
- package/node_modules/devlop/lib/development.d.ts +84 -0
- package/node_modules/devlop/lib/development.js +238 -0
- package/node_modules/devlop/license +22 -0
- package/node_modules/devlop/package.json +80 -0
- package/node_modules/devlop/readme.md +360 -0
- package/node_modules/mdast-util-from-markdown/dev/index.d.ts +23 -0
- package/node_modules/mdast-util-from-markdown/dev/index.js +2 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts +53 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts.map +1 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.js +1348 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/types.d.ts +295 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/types.js +2 -0
- package/node_modules/mdast-util-from-markdown/index.d.ts +23 -0
- package/node_modules/mdast-util-from-markdown/index.js +2 -0
- package/node_modules/mdast-util-from-markdown/lib/index.d.ts +53 -0
- package/node_modules/mdast-util-from-markdown/lib/index.js +1177 -0
- package/node_modules/mdast-util-from-markdown/lib/types.d.ts +295 -0
- package/node_modules/mdast-util-from-markdown/lib/types.js +2 -0
- package/node_modules/mdast-util-from-markdown/license +22 -0
- package/node_modules/mdast-util-from-markdown/package.json +139 -0
- package/node_modules/mdast-util-from-markdown/readme.md +537 -0
- package/node_modules/mdast-util-to-string/index.d.ts +2 -0
- package/node_modules/mdast-util-to-string/index.js +5 -0
- package/node_modules/mdast-util-to-string/lib/index.d.ts +31 -0
- package/node_modules/mdast-util-to-string/lib/index.js +108 -0
- package/node_modules/mdast-util-to-string/license +22 -0
- package/node_modules/mdast-util-to-string/package.json +78 -0
- package/node_modules/mdast-util-to-string/readme.md +218 -0
- package/node_modules/micromark/dev/index.d.ts +82 -0
- package/node_modules/micromark/dev/index.d.ts.map +1 -0
- package/node_modules/micromark/dev/index.js +68 -0
- package/node_modules/micromark/dev/lib/compile.d.ts +16 -0
- package/node_modules/micromark/dev/lib/compile.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/compile.js +1162 -0
- package/node_modules/micromark/dev/lib/constructs.d.ts +73 -0
- package/node_modules/micromark/dev/lib/constructs.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/constructs.js +101 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.d.ts +46 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.js +718 -0
- package/node_modules/micromark/dev/lib/initialize/content.d.ts +4 -0
- package/node_modules/micromark/dev/lib/initialize/content.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/content.js +99 -0
- package/node_modules/micromark/dev/lib/initialize/document.d.ts +10 -0
- package/node_modules/micromark/dev/lib/initialize/document.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/document.js +449 -0
- package/node_modules/micromark/dev/lib/initialize/flow.d.ts +4 -0
- package/node_modules/micromark/dev/lib/initialize/flow.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/flow.js +86 -0
- package/node_modules/micromark/dev/lib/initialize/text.d.ts +8 -0
- package/node_modules/micromark/dev/lib/initialize/text.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/text.js +248 -0
- package/node_modules/micromark/dev/lib/parse.d.ts +10 -0
- package/node_modules/micromark/dev/lib/parse.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/parse.js +58 -0
- package/node_modules/micromark/dev/lib/postprocess.d.ts +9 -0
- package/node_modules/micromark/dev/lib/postprocess.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/postprocess.js +19 -0
- package/node_modules/micromark/dev/lib/preprocess.d.ts +13 -0
- package/node_modules/micromark/dev/lib/preprocess.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/preprocess.js +139 -0
- package/node_modules/micromark/dev/stream.d.ts +35 -0
- package/node_modules/micromark/dev/stream.d.ts.map +1 -0
- package/node_modules/micromark/dev/stream.js +270 -0
- package/node_modules/micromark/index.d.ts +82 -0
- package/node_modules/micromark/index.d.ts.map +1 -0
- package/node_modules/micromark/index.js +60 -0
- package/node_modules/micromark/lib/compile.d.ts +16 -0
- package/node_modules/micromark/lib/compile.d.ts.map +1 -0
- package/node_modules/micromark/lib/compile.js +1068 -0
- package/node_modules/micromark/lib/constructs.d.ts +73 -0
- package/node_modules/micromark/lib/constructs.d.ts.map +1 -0
- package/node_modules/micromark/lib/constructs.js +85 -0
- package/node_modules/micromark/lib/create-tokenizer.d.ts +46 -0
- package/node_modules/micromark/lib/create-tokenizer.d.ts.map +1 -0
- package/node_modules/micromark/lib/create-tokenizer.js +613 -0
- package/node_modules/micromark/lib/initialize/content.d.ts +4 -0
- package/node_modules/micromark/lib/initialize/content.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/content.js +79 -0
- package/node_modules/micromark/lib/initialize/document.d.ts +10 -0
- package/node_modules/micromark/lib/initialize/document.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/document.js +370 -0
- package/node_modules/micromark/lib/initialize/flow.d.ts +4 -0
- package/node_modules/micromark/lib/initialize/flow.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/flow.js +58 -0
- package/node_modules/micromark/lib/initialize/text.d.ts +8 -0
- package/node_modules/micromark/lib/initialize/text.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/text.js +216 -0
- package/node_modules/micromark/lib/parse.d.ts +10 -0
- package/node_modules/micromark/lib/parse.d.ts.map +1 -0
- package/node_modules/micromark/lib/parse.js +56 -0
- package/node_modules/micromark/lib/postprocess.d.ts +9 -0
- package/node_modules/micromark/lib/postprocess.d.ts.map +1 -0
- package/node_modules/micromark/lib/postprocess.js +18 -0
- package/node_modules/micromark/lib/preprocess.d.ts +13 -0
- package/node_modules/micromark/lib/preprocess.d.ts.map +1 -0
- package/node_modules/micromark/lib/preprocess.js +112 -0
- package/node_modules/micromark/license +22 -0
- package/node_modules/micromark/package.json +87 -0
- package/node_modules/micromark/readme.md +488 -0
- package/node_modules/micromark/stream.d.ts +35 -0
- package/node_modules/micromark/stream.d.ts.map +1 -0
- package/node_modules/micromark/stream.js +256 -0
- package/node_modules/micromark-core-commonmark/dev/index.d.ts +23 -0
- package/node_modules/micromark-core-commonmark/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/index.js +22 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.js +294 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.js +269 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.js +62 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.js +162 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.js +69 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.js +166 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.js +472 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.js +178 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.js +257 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts +7 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.js +185 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.js +295 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.js +65 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.js +229 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.js +938 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.js +783 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.js +658 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.js +119 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.js +71 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.js +34 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.js +306 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.js +67 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.js +213 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.js +120 -0
- package/node_modules/micromark-core-commonmark/index.d.ts +23 -0
- package/node_modules/micromark-core-commonmark/index.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/index.js +22 -0
- package/node_modules/micromark-core-commonmark/lib/attention.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/attention.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/attention.js +231 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.js +233 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.js +61 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.js +143 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.js +64 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.js +149 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.js +422 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.js +159 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.js +225 -0
- package/node_modules/micromark-core-commonmark/lib/content.d.ts +7 -0
- package/node_modules/micromark-core-commonmark/lib/content.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/content.js +163 -0
- package/node_modules/micromark-core-commonmark/lib/definition.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/definition.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/definition.js +254 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.js +60 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.js +191 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.js +827 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.js +678 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.js +561 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.js +110 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.js +63 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.js +33 -0
- package/node_modules/micromark-core-commonmark/lib/list.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/list.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/list.js +213 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.js +61 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.js +187 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.js +102 -0
- package/node_modules/micromark-core-commonmark/license +22 -0
- package/node_modules/micromark-core-commonmark/package.json +62 -0
- package/node_modules/micromark-core-commonmark/readme.md +171 -0
- package/node_modules/micromark-factory-destination/dev/index.d.ts +42 -0
- package/node_modules/micromark-factory-destination/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-destination/dev/index.js +255 -0
- package/node_modules/micromark-factory-destination/index.d.ts +42 -0
- package/node_modules/micromark-factory-destination/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-destination/index.js +206 -0
- package/node_modules/micromark-factory-destination/license +22 -0
- package/node_modules/micromark-factory-destination/package.json +57 -0
- package/node_modules/micromark-factory-destination/readme.md +234 -0
- package/node_modules/micromark-factory-label/dev/index.d.ts +37 -0
- package/node_modules/micromark-factory-label/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-label/dev/index.js +172 -0
- package/node_modules/micromark-factory-label/index.d.ts +37 -0
- package/node_modules/micromark-factory-label/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-label/index.js +148 -0
- package/node_modules/micromark-factory-label/license +22 -0
- package/node_modules/micromark-factory-label/package.json +60 -0
- package/node_modules/micromark-factory-label/readme.md +224 -0
- package/node_modules/micromark-factory-space/dev/index.d.ts +60 -0
- package/node_modules/micromark-factory-space/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-space/dev/index.js +122 -0
- package/node_modules/micromark-factory-space/index.d.ts +60 -0
- package/node_modules/micromark-factory-space/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-space/index.js +116 -0
- package/node_modules/micromark-factory-space/license +22 -0
- package/node_modules/micromark-factory-space/package.json +46 -0
- package/node_modules/micromark-factory-space/readme.md +225 -0
- package/node_modules/micromark-factory-title/dev/index.d.ts +36 -0
- package/node_modules/micromark-factory-title/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-title/dev/index.js +169 -0
- package/node_modules/micromark-factory-title/index.d.ts +36 -0
- package/node_modules/micromark-factory-title/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-title/index.js +158 -0
- package/node_modules/micromark-factory-title/license +22 -0
- package/node_modules/micromark-factory-title/package.json +58 -0
- package/node_modules/micromark-factory-title/readme.md +229 -0
- package/node_modules/micromark-factory-whitespace/dev/index.d.ts +22 -0
- package/node_modules/micromark-factory-whitespace/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-whitespace/dev/index.js +53 -0
- package/node_modules/micromark-factory-whitespace/index.d.ts +22 -0
- package/node_modules/micromark-factory-whitespace/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-whitespace/index.js +44 -0
- package/node_modules/micromark-factory-whitespace/license +22 -0
- package/node_modules/micromark-factory-whitespace/package.json +57 -0
- package/node_modules/micromark-factory-whitespace/readme.md +205 -0
- package/node_modules/micromark-util-character/dev/index.d.ts +195 -0
- package/node_modules/micromark-util-character/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-character/dev/index.js +252 -0
- package/node_modules/micromark-util-character/index.d.ts +195 -0
- package/node_modules/micromark-util-character/index.d.ts.map +1 -0
- package/node_modules/micromark-util-character/index.js +246 -0
- package/node_modules/micromark-util-character/license +22 -0
- package/node_modules/micromark-util-character/package.json +57 -0
- package/node_modules/micromark-util-character/readme.md +446 -0
- package/node_modules/micromark-util-chunked/dev/index.d.ts +41 -0
- package/node_modules/micromark-util-chunked/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-chunked/dev/index.js +89 -0
- package/node_modules/micromark-util-chunked/index.d.ts +41 -0
- package/node_modules/micromark-util-chunked/index.d.ts.map +1 -0
- package/node_modules/micromark-util-chunked/index.js +81 -0
- package/node_modules/micromark-util-chunked/license +22 -0
- package/node_modules/micromark-util-chunked/package.json +57 -0
- package/node_modules/micromark-util-chunked/readme.md +219 -0
- package/node_modules/micromark-util-classify-character/dev/index.d.ts +18 -0
- package/node_modules/micromark-util-classify-character/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-classify-character/dev/index.js +38 -0
- package/node_modules/micromark-util-classify-character/index.d.ts +18 -0
- package/node_modules/micromark-util-classify-character/index.d.ts.map +1 -0
- package/node_modules/micromark-util-classify-character/index.js +27 -0
- package/node_modules/micromark-util-classify-character/license +22 -0
- package/node_modules/micromark-util-classify-character/package.json +59 -0
- package/node_modules/micromark-util-classify-character/readme.md +205 -0
- package/node_modules/micromark-util-combine-extensions/index.d.ts +22 -0
- package/node_modules/micromark-util-combine-extensions/index.d.ts.map +1 -0
- package/node_modules/micromark-util-combine-extensions/index.js +143 -0
- package/node_modules/micromark-util-combine-extensions/license +22 -0
- package/node_modules/micromark-util-combine-extensions/package.json +52 -0
- package/node_modules/micromark-util-combine-extensions/readme.md +201 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts +16 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.js +42 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts +16 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.js +32 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/license +22 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/package.json +59 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/readme.md +184 -0
- package/node_modules/micromark-util-decode-string/dev/index.d.ts +14 -0
- package/node_modules/micromark-util-decode-string/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-string/dev/index.js +53 -0
- package/node_modules/micromark-util-decode-string/index.d.ts +14 -0
- package/node_modules/micromark-util-decode-string/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-string/index.js +45 -0
- package/node_modules/micromark-util-decode-string/license +22 -0
- package/node_modules/micromark-util-decode-string/package.json +63 -0
- package/node_modules/micromark-util-decode-string/readme.md +179 -0
- package/node_modules/micromark-util-edit-map/dev/index.d.ts +78 -0
- package/node_modules/micromark-util-edit-map/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-edit-map/dev/index.js +237 -0
- package/node_modules/micromark-util-edit-map/index.d.ts +78 -0
- package/node_modules/micromark-util-edit-map/index.d.ts.map +1 -0
- package/node_modules/micromark-util-edit-map/index.js +225 -0
- package/node_modules/micromark-util-edit-map/license +22 -0
- package/node_modules/micromark-util-edit-map/package.json +48 -0
- package/node_modules/micromark-util-edit-map/readme.md +234 -0
- package/node_modules/micromark-util-encode/index.d.ts +14 -0
- package/node_modules/micromark-util-encode/index.d.ts.map +1 -0
- package/node_modules/micromark-util-encode/index.js +33 -0
- package/node_modules/micromark-util-encode/license +22 -0
- package/node_modules/micromark-util-encode/package.json +47 -0
- package/node_modules/micromark-util-encode/readme.md +176 -0
- package/node_modules/micromark-util-html-tag-name/index.d.ts +30 -0
- package/node_modules/micromark-util-html-tag-name/index.d.ts.map +1 -0
- package/node_modules/micromark-util-html-tag-name/index.js +93 -0
- package/node_modules/micromark-util-html-tag-name/license +22 -0
- package/node_modules/micromark-util-html-tag-name/package.json +47 -0
- package/node_modules/micromark-util-html-tag-name/readme.md +193 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts +21 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.js +38 -0
- package/node_modules/micromark-util-normalize-identifier/index.d.ts +21 -0
- package/node_modules/micromark-util-normalize-identifier/index.d.ts.map +1 -0
- package/node_modules/micromark-util-normalize-identifier/index.js +33 -0
- package/node_modules/micromark-util-normalize-identifier/license +22 -0
- package/node_modules/micromark-util-normalize-identifier/package.json +58 -0
- package/node_modules/micromark-util-normalize-identifier/readme.md +187 -0
- package/node_modules/micromark-util-resolve-all/index.d.ts +22 -0
- package/node_modules/micromark-util-resolve-all/index.d.ts.map +1 -0
- package/node_modules/micromark-util-resolve-all/index.js +32 -0
- package/node_modules/micromark-util-resolve-all/license +22 -0
- package/node_modules/micromark-util-resolve-all/package.json +48 -0
- package/node_modules/micromark-util-resolve-all/readme.md +238 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts +36 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.js +124 -0
- package/node_modules/micromark-util-sanitize-uri/index.d.ts +36 -0
- package/node_modules/micromark-util-sanitize-uri/index.d.ts.map +1 -0
- package/node_modules/micromark-util-sanitize-uri/index.js +107 -0
- package/node_modules/micromark-util-sanitize-uri/license +22 -0
- package/node_modules/micromark-util-sanitize-uri/package.json +59 -0
- package/node_modules/micromark-util-sanitize-uri/readme.md +214 -0
- package/node_modules/micromark-util-subtokenize/dev/index.d.ts +12 -0
- package/node_modules/micromark-util-subtokenize/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/dev/index.js +284 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts +161 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.js +288 -0
- package/node_modules/micromark-util-subtokenize/index.d.ts +12 -0
- package/node_modules/micromark-util-subtokenize/index.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/index.js +228 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts +161 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.js +251 -0
- package/node_modules/micromark-util-subtokenize/license +22 -0
- package/node_modules/micromark-util-subtokenize/package.json +60 -0
- package/node_modules/micromark-util-subtokenize/readme.md +181 -0
- package/node_modules/micromark-util-symbol/lib/codes.d.ts +139 -0
- package/node_modules/micromark-util-symbol/lib/codes.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/codes.js +158 -0
- package/node_modules/micromark-util-symbol/lib/constants.d.ts +37 -0
- package/node_modules/micromark-util-symbol/lib/constants.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/constants.js +44 -0
- package/node_modules/micromark-util-symbol/lib/default.d.ts +5 -0
- package/node_modules/micromark-util-symbol/lib/default.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/default.js +4 -0
- package/node_modules/micromark-util-symbol/lib/types.d.ts +106 -0
- package/node_modules/micromark-util-symbol/lib/types.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/types.js +453 -0
- package/node_modules/micromark-util-symbol/lib/values.d.ts +102 -0
- package/node_modules/micromark-util-symbol/lib/values.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/values.js +109 -0
- package/node_modules/micromark-util-symbol/license +22 -0
- package/node_modules/micromark-util-symbol/package.json +43 -0
- package/node_modules/micromark-util-symbol/readme.md +168 -0
- package/node_modules/micromark-util-types/index.d.ts +1315 -0
- package/node_modules/micromark-util-types/index.js +2 -0
- package/node_modules/micromark-util-types/license +22 -0
- package/node_modules/micromark-util-types/package.json +37 -0
- package/node_modules/micromark-util-types/readme.md +151 -0
- package/node_modules/smol-toml/LICENSE +24 -0
- package/node_modules/smol-toml/README.md +605 -0
- package/node_modules/smol-toml/dist/date.d.ts +41 -0
- package/node_modules/smol-toml/dist/date.js +156 -0
- package/node_modules/smol-toml/dist/error.d.ts +37 -0
- package/node_modules/smol-toml/dist/error.js +67 -0
- package/node_modules/smol-toml/dist/extract.js +289 -0
- package/node_modules/smol-toml/dist/index.cjs +770 -0
- package/node_modules/smol-toml/dist/index.d.ts +44 -0
- package/node_modules/smol-toml/dist/index.js +34 -0
- package/node_modules/smol-toml/dist/parse.d.ts +73 -0
- package/node_modules/smol-toml/dist/parse.js +164 -0
- package/node_modules/smol-toml/dist/primitive.js +151 -0
- package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
- package/node_modules/smol-toml/dist/stringify.js +219 -0
- package/node_modules/smol-toml/dist/struct.js +156 -0
- package/node_modules/smol-toml/dist/util.d.ts +43 -0
- package/node_modules/smol-toml/dist/util.js +57 -0
- package/node_modules/smol-toml/package.json +68 -0
- package/node_modules/unist-util-stringify-position/index.d.ts +1 -0
- package/node_modules/unist-util-stringify-position/index.js +1 -0
- package/node_modules/unist-util-stringify-position/lib/index.d.ts +61 -0
- package/node_modules/unist-util-stringify-position/lib/index.js +84 -0
- package/node_modules/unist-util-stringify-position/license +22 -0
- package/node_modules/unist-util-stringify-position/package.json +80 -0
- package/node_modules/unist-util-stringify-position/readme.md +206 -0
- package/package.json +27 -8
- package/scripts/prepare-github-keyring.js +29 -0
- package/src/codex-app.js +20 -2
- package/src/codex-pricing.js +79 -0
- package/src/codex-projects.js +90 -0
- package/src/codex-session-scanner.js +748 -0
- package/src/codex-session-usage.js +332 -0
- package/src/dashboard/app.js +2 -2
- package/src/dashboard/dist/app.js +135 -60
- package/src/dashboard/dist/app.js.map +1 -1
- package/src/dashboard/dist/styles.css +1 -1
- package/src/dashboard/react/components/AnimatedUsage.test.tsx +78 -0
- package/src/dashboard/react/components/CardStatusIcons.test.tsx +146 -0
- package/src/dashboard/react/components/CardStatusIcons.tsx +138 -0
- package/src/dashboard/react/components/ImageGallery.tsx +22 -0
- package/src/dashboard/react/components/ModelSettings.test.tsx +4 -4
- package/src/dashboard/react/components/ModelSettings.tsx +1 -1
- package/src/dashboard/react/components/OpenChatButton.test.tsx +19 -0
- package/src/dashboard/react/components/OpenChatButton.tsx +2 -1
- package/src/dashboard/react/components/PrGlyph.tsx +35 -0
- package/src/dashboard/react/components/PullRequestBadges.tsx +18 -0
- package/src/dashboard/react/components/QueuedPrompts.test.tsx +42 -0
- package/src/dashboard/react/components/QueuedPrompts.tsx +34 -0
- package/src/dashboard/react/components/RelativeTime.test.tsx +100 -0
- package/src/dashboard/react/components/RelativeTime.tsx +46 -11
- package/src/dashboard/react/components/ReplyCover.test.tsx +30 -0
- package/src/dashboard/react/components/ReplyCover.tsx +33 -0
- package/src/dashboard/react/components/ReplyImagePreview.test.tsx +90 -0
- package/src/dashboard/react/components/ReplyImagePreview.tsx +34 -0
- package/src/dashboard/react/components/ReplyMarkdown.test.tsx +108 -0
- package/src/dashboard/react/components/ReplyMarkdown.tsx +112 -0
- package/src/dashboard/react/components/SavedUsageFormat.test.tsx +11 -0
- package/src/dashboard/react/components/SavedUsageFormat.tsx +68 -0
- package/src/dashboard/react/components/StatusBadge.tsx +2 -2
- package/src/dashboard/react/components/TaskBoard.test.tsx +256 -1
- package/src/dashboard/react/components/TaskBoard.tsx +136 -28
- package/src/dashboard/react/components/TaskCard.tsx +7 -4
- package/src/dashboard/react/components/TaskDetail.test.tsx +92 -1
- package/src/dashboard/react/components/TaskDetail.tsx +37 -10
- package/src/dashboard/react/components/TurnContent.test.tsx +44 -0
- package/src/dashboard/react/components/TurnContent.tsx +53 -0
- package/src/dashboard/react/components/UsagePanel.tsx +2 -2
- package/src/dashboard/react/image-cache.test.ts +23 -0
- package/src/dashboard/react/image-cache.ts +33 -0
- package/src/dashboard/react/presentation.test.tsx +31 -0
- package/src/dashboard/react/styles.css +126 -2
- package/src/dashboard/react/types.ts +55 -1
- package/src/dashboard/time.js +39 -0
- package/src/dashboard.js +4 -3
- package/src/data/codex-pricing.json +537 -0
- package/src/mcp-app/dist/index.html +216 -112
- package/src/mcp-app/react/AppearancePicker.test.tsx +50 -0
- package/src/mcp-app/react/AppearancePicker.tsx +56 -0
- package/src/mcp-app/react/GitHubConnection.test.tsx +78 -0
- package/src/mcp-app/react/GitHubConnection.tsx +78 -0
- package/src/mcp-app/react/NextNotificationCenter.test.tsx +121 -0
- package/src/mcp-app/react/NextNotificationCenter.tsx +94 -0
- package/src/mcp-app/react/NextTaskDetail.test.tsx +124 -0
- package/src/mcp-app/react/NextTaskDetail.tsx +147 -0
- package/src/mcp-app/react/ProjectPicker.tsx +49 -0
- package/src/mcp-app/react/main.test.tsx +499 -49
- package/src/mcp-app/react/main.tsx +359 -86
- package/src/mcp-app/react/styles.css +186 -9
- package/src/mcp-app/react/themes.test.ts +34 -0
- package/src/mcp-app/react/themes.ts +112 -0
- package/src/mcp-app.js +238 -120
- package/src/mcp.js +8 -1
- package/src/next-cache.js +16 -0
- package/src/next-github.js +273 -0
- package/src/next-notifications.js +90 -0
- package/src/recorded-usage-tracker.js +92 -0
- package/src/reply-image.js +65 -0
- package/src/usage-tracker.js +7 -6
- package/src/usage.js +7 -355
- package/src/user-prompt.js +55 -0
- package/src/version.js +2 -1
- package/node_modules/proper-lockfile/CHANGELOG.md +0 -108
- package/node_modules/which/CHANGELOG.md +0 -166
- package/src/resolve-ccusage.js +0 -17
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { act, cleanup, render, screen } from "@testing-library/react";
|
|
2
|
+
import { afterEach, beforeEach, expect, test, vi } from "vitest";
|
|
3
|
+
import type { SavedUsage } from "../types";
|
|
4
|
+
import { useAnimatedUsage } from "./SavedUsageFormat";
|
|
5
|
+
|
|
6
|
+
const motion = vi.hoisted(() => ({ reduced: false }));
|
|
7
|
+
vi.mock("@mantine/hooks", () => ({ useReducedMotion: () => motion.reduced }));
|
|
8
|
+
const usage = (tokens: number, cost: number): SavedUsage => ({
|
|
9
|
+
tokens: { input_tokens: tokens, cached_input_tokens: 0, cache_write_input_tokens: 0, output_tokens: 0, reasoning_output_tokens: 0, total_tokens: tokens },
|
|
10
|
+
samples: 1, costUsd: cost, partial: false, costPartial: false,
|
|
11
|
+
});
|
|
12
|
+
function Counter({ value, running = true, identity = "turn-1" }: { value?: SavedUsage; running?: boolean; identity?: string }) {
|
|
13
|
+
const displayed = useAnimatedUsage(value, running, identity);
|
|
14
|
+
return <output data-testid="counter">{displayed ? `${displayed.tokens.total_tokens}:${displayed.costUsd}` : "missing"}</output>;
|
|
15
|
+
}
|
|
16
|
+
let clock = 0, nextFrame = 0;
|
|
17
|
+
const frames = new Map<number, FrameRequestCallback>();
|
|
18
|
+
function advance(now: number) {
|
|
19
|
+
act(() => {
|
|
20
|
+
clock = now;
|
|
21
|
+
const pending = [...frames.values()];
|
|
22
|
+
frames.clear();
|
|
23
|
+
pending.forEach(callback => callback(now));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function displayed() { return screen.getByTestId("counter").textContent; }
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
clock = 0; nextFrame = 0; motion.reduced = false; frames.clear();
|
|
29
|
+
vi.spyOn(performance, "now").mockImplementation(() => clock);
|
|
30
|
+
vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => { frames.set(++nextFrame, callback); return nextFrame; });
|
|
31
|
+
vi.stubGlobal("cancelAnimationFrame", (id: number) => frames.delete(id));
|
|
32
|
+
});
|
|
33
|
+
afterEach(() => { cleanup(); vi.restoreAllMocks(); vi.unstubAllGlobals(); });
|
|
34
|
+
|
|
35
|
+
test("running usage catches up and continues from the displayed amount when another update arrives", () => {
|
|
36
|
+
const view = render(<Counter value={usage(1000, 2)} />);
|
|
37
|
+
expect(displayed()).toBe("0:0");
|
|
38
|
+
advance(400);
|
|
39
|
+
expect(displayed()).toBe("875:1.75");
|
|
40
|
+
view.rerender(<Counter value={usage(2000, 4)} />);
|
|
41
|
+
expect(displayed()).toBe("875:1.75");
|
|
42
|
+
advance(800);
|
|
43
|
+
expect(displayed()).toBe("1859:3.71875");
|
|
44
|
+
advance(1200);
|
|
45
|
+
expect(displayed()).toBe("2000:4");
|
|
46
|
+
expect(frames.size).toBe(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("late usage animates from zero and a new turn does not inherit the old counter", () => {
|
|
50
|
+
const view = render(<Counter />);
|
|
51
|
+
expect(displayed()).toBe("missing");
|
|
52
|
+
view.rerender(<Counter value={usage(1000, 2)} />);
|
|
53
|
+
advance(800);
|
|
54
|
+
expect(displayed()).toBe("1000:2");
|
|
55
|
+
view.rerender(<Counter value={usage(300, 0.5)} identity="turn-2" />);
|
|
56
|
+
expect(displayed()).toBe("0:0");
|
|
57
|
+
advance(1600);
|
|
58
|
+
expect(displayed()).toBe("300:0.5");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("completed chats and reduced motion show the exact total and cancel pending animation", () => {
|
|
62
|
+
const view = render(<Counter value={usage(1000, 2)} />);
|
|
63
|
+
advance(200);
|
|
64
|
+
view.rerender(<Counter value={usage(1000, 2)} running={false} />);
|
|
65
|
+
expect(displayed()).toBe("1000:2");
|
|
66
|
+
expect(frames.size).toBe(0);
|
|
67
|
+
motion.reduced = true;
|
|
68
|
+
view.rerender(<Counter value={usage(2000, 4)} />);
|
|
69
|
+
expect(displayed()).toBe("2000:4");
|
|
70
|
+
expect(frames.size).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("unmount cancels the counter's pending frame", () => {
|
|
74
|
+
const view = render(<Counter value={usage(1000, 2)} />);
|
|
75
|
+
expect(frames.size).toBe(1);
|
|
76
|
+
view.unmount();
|
|
77
|
+
expect(frames.size).toBe(0);
|
|
78
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { MantineProvider } from "@mantine/core";
|
|
2
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { afterEach, expect, test, vi } from "vitest";
|
|
4
|
+
import { nextRunLabel, prPresentation, PullRequestIcons, PullRequestInfo, CardStatusLine } from "./CardStatusIcons";
|
|
5
|
+
import type { PullRequestStatus } from "../types";
|
|
6
|
+
// Test TaskChef's rows and keyboard handlers without jsdom layout work.
|
|
7
|
+
// Browser QA separately exercises Mantine's real popup and positioning.
|
|
8
|
+
vi.mock("@mantine/core", async () => {
|
|
9
|
+
const actual = await vi.importActual<typeof import("@mantine/core")>("@mantine/core");
|
|
10
|
+
const React = await import("react");
|
|
11
|
+
const Open = React.createContext(false);
|
|
12
|
+
const Popover = Object.assign(
|
|
13
|
+
({opened, children}: {opened: boolean; children: React.ReactNode}) => <Open value={opened}>{children}</Open>,
|
|
14
|
+
{
|
|
15
|
+
Target: ({children}: {children: React.ReactNode}) => children,
|
|
16
|
+
Dropdown: React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>((props, ref) =>
|
|
17
|
+
React.useContext(Open) ? <div {...props} ref={ref} /> : null),
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
return {...actual, Popover};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(cleanup);
|
|
24
|
+
const url="https://github.com/example/repo/pull/12";
|
|
25
|
+
const pr:PullRequestStatus={url,state:"open",checks:"passed",title:"Improve search"};
|
|
26
|
+
test("cards keep PR status in an accessible icon without extra text",()=>{
|
|
27
|
+
render(<MantineProvider><PullRequestIcons pullRequests={[pr,{...pr,url:"https://github.com/example/repo/pull/13",state:"unknown"}]}/></MantineProvider>);
|
|
28
|
+
expect(screen.getByLabelText("#12 Improve search: Checks passed")).toBeInTheDocument();
|
|
29
|
+
expect(screen.getByLabelText("#13 Improve search: PR status not checked or unavailable")).toBeInTheDocument();
|
|
30
|
+
expect(screen.queryByText(/unavailable/)).not.toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
test("PR presentation follows Codex priority for drafts, conflicts, CI and merge readiness",()=>{
|
|
33
|
+
expect(prPresentation({...pr,state:"draft",checks:"failed"}).dot).toBeNull();
|
|
34
|
+
expect(prPresentation({...pr,hasMergeConflicts:true}).label).toBe("Merge conflicts");
|
|
35
|
+
expect(prPresentation({...pr,canMerge:true}).label).toBe("Ready to merge");
|
|
36
|
+
expect(prPresentation({...pr,checks:"pending"}).label).toBe("Checks pending");
|
|
37
|
+
expect(prPresentation({...pr,state:"merged",checks:"failed"}).label).toBe("Merged");
|
|
38
|
+
});
|
|
39
|
+
test("next run uses Today and Tomorrow and never invents missing times",()=>{
|
|
40
|
+
const now=new Date(2026,9,10,10);
|
|
41
|
+
expect(nextRunLabel(new Date(2026,9,10,15).toISOString(),now)).toMatch(/^Next run: Today at /);
|
|
42
|
+
expect(nextRunLabel(new Date(2026,9,11,9).toISOString(),now)).toMatch(/^Next run: Tomorrow at /);
|
|
43
|
+
expect(nextRunLabel(null,now)).toBe("Next run time unavailable");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
test("popup content keeps CI for merged and unknown PRs and links only supported URLs",()=>{
|
|
48
|
+
const {rerender}=render(<MantineProvider><PullRequestInfo pr={{...pr,state:"merged"}}/></MantineProvider>);
|
|
49
|
+
expect(screen.getByText("CI passed")).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByRole("link",{name:"#12 Improve search"})).toHaveAttribute("href",url);
|
|
51
|
+
rerender(<MantineProvider><PullRequestInfo pr={{...pr,state:"unknown",checks:"unknown",url:"javascript:alert(1)"}}/></MantineProvider>);
|
|
52
|
+
expect(screen.queryByText(/CI status unknown/)).not.toBeInTheDocument();
|
|
53
|
+
expect(screen.queryByRole("link")).not.toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
test("one status popup shows available lines, survives movement onto links, and closes with Escape", async () => {
|
|
58
|
+
render(<MantineProvider env="test"><CardStatusLine durationMs={905 * 60000} pullRequests={[{...pr, canMerge: true, checkedAt: new Date().toISOString()}]} scheduled nextRunAt={new Date(Date.now()+3600000).toISOString()}><span>Oct 7th</span></CardStatusLine></MantineProvider>);
|
|
59
|
+
const line = screen.getByLabelText("Turn status details");
|
|
60
|
+
fireEvent.mouseEnter(line);
|
|
61
|
+
expect(screen.getByText("Worked for 15h 5m")).toBeInTheDocument();
|
|
62
|
+
const link = screen.getByRole("link", {name:"#12 Improve search",hidden:true});
|
|
63
|
+
expect(link).toHaveAttribute("href",url);
|
|
64
|
+
expect(screen.queryByText("CI passed")).not.toBeInTheDocument();
|
|
65
|
+
expect(screen.getByText(/^Next run:/)).toBeInTheDocument();
|
|
66
|
+
expect(screen.queryByText(/Checked |Ready to merge/)).not.toBeInTheDocument();
|
|
67
|
+
fireEvent.focus(line);
|
|
68
|
+
fireEvent.keyDown(line,{key:"Tab"});
|
|
69
|
+
expect(link).toHaveFocus();
|
|
70
|
+
fireEvent.keyDown(link,{key:"Escape"});
|
|
71
|
+
expect(line).toHaveFocus();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("popup omits missing duration, CI and schedule time instead of unavailable lines", async () => {
|
|
75
|
+
render(<MantineProvider env="test"><CardStatusLine durationMs={null} scheduled nextRunAt={null} pullRequests={[{...pr,checks:"unknown"}]}><span>Oct 7th</span></CardStatusLine></MantineProvider>);
|
|
76
|
+
fireEvent.mouseEnter(screen.getByLabelText("Turn status details"));
|
|
77
|
+
expect(screen.getByRole("link",{name:"#12 Improve search",hidden:true})).toBeInTheDocument();
|
|
78
|
+
expect(screen.queryByText(/Worked for|Next run:|CI status|unavailable/)).not.toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
test("status line describes PR state to keyboard and screen-reader users", () => {
|
|
83
|
+
render(<MantineProvider><CardStatusLine pullRequests={[{...pr,state:"merged"},{...pr,url:"https://github.com/example/repo/pull/13",state:"draft"}]}><span>Oct 7th</span></CardStatusLine></MantineProvider>);
|
|
84
|
+
expect(screen.getByLabelText("Turn status details")).toHaveAccessibleDescription("#12 Improve search: Merged. #13 Improve search: Draft");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
test("Tab preserves the time control before handing focus to the PR link", () => {
|
|
89
|
+
render(<MantineProvider env="test"><CardStatusLine pullRequests={[pr]}><button>Oct 7th</button></CardStatusLine></MantineProvider>);
|
|
90
|
+
const line = screen.getByLabelText("Turn status details");
|
|
91
|
+
fireEvent.focus(line);
|
|
92
|
+
expect(fireEvent.keyDown(line, {key:"Tab"})).toBe(true);
|
|
93
|
+
const time = screen.getByRole("button", {name:"Oct 7th"});
|
|
94
|
+
time.focus();
|
|
95
|
+
fireEvent.keyDown(time, {key:"Tab"});
|
|
96
|
+
expect(screen.getByRole("link", {name:"#12 Improve search",hidden:true})).toHaveFocus();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("shows subagents and repository access action with number first", () => {
|
|
100
|
+
render(<MantineProvider><CardStatusLine subagentCount={155} pullRequests={[{...pr,state:"unknown",checks:"unknown",accessIssue:"denied"}]}><span>Oct 7th</span></CardStatusLine></MantineProvider>);
|
|
101
|
+
fireEvent.mouseEnter(screen.getByLabelText("Turn status details"));
|
|
102
|
+
expect(screen.queryByText("155 subagents")).toBeNull();
|
|
103
|
+
expect(screen.getByText(/Access denied to example\/repo/)).toBeInTheDocument();
|
|
104
|
+
expect(screen.getByRole("link",{name:"Grant access"})).toHaveAttribute("href","https://github.com/apps/taskchef/installations/new");
|
|
105
|
+
expect(screen.getByRole("link",{name:"#12 Improve search"})).toHaveClass("taskchef-status-pr-title");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("card and popup use latest turn usage and omit incomplete markers", () => {
|
|
109
|
+
const turnUsage = {tokens:{input_tokens:1000,cached_input_tokens:200,cache_write_input_tokens:0,output_tokens:100,reasoning_output_tokens:40,total_tokens:1100},costUsd:2.34,samples:1,partial:true,costPartial:true};
|
|
110
|
+
render(<MantineProvider env="test"><CardStatusLine turnUsage={turnUsage}><span>1h</span></CardStatusLine></MantineProvider>);
|
|
111
|
+
expect(screen.getByText("1.10K • $2.34")).toBeVisible();
|
|
112
|
+
fireEvent.focus(screen.getByLabelText("Turn status details"));
|
|
113
|
+
expect(screen.getByText("Turn usage: 1.10K • $2.34")).toBeVisible();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("missing turn records show plain placeholders without an explanation", () => {
|
|
117
|
+
const turnUsage={tokens:{input_tokens:0,cached_input_tokens:0,cache_write_input_tokens:0,output_tokens:0,reasoning_output_tokens:0,total_tokens:0},costUsd:0,samples:0,partial:true,costPartial:true};
|
|
118
|
+
render(<MantineProvider env="test"><CardStatusLine turnUsage={turnUsage}><span>1h</span></CardStatusLine></MantineProvider>);
|
|
119
|
+
expect(screen.getByText("— • —")).toBeVisible();
|
|
120
|
+
fireEvent.focus(screen.getByLabelText("Turn status details"));
|
|
121
|
+
expect(screen.getByText("Turn usage: — • —")).toBeVisible();
|
|
122
|
+
expect(screen.queryByText(/Some usage records/)).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("earlier-turn PR is labeled in the popup and latest-turn PRs take priority", () => {
|
|
126
|
+
const earlier={...pr,state:"merged" as const};
|
|
127
|
+
const view=render(<MantineProvider><CardStatusLine earlierPullRequest={earlier}><span>1h</span></CardStatusLine></MantineProvider>);
|
|
128
|
+
expect(screen.getByLabelText("#12 Improve search: Merged")).toBeInTheDocument();
|
|
129
|
+
fireEvent.mouseEnter(screen.getByLabelText("Turn status details"));
|
|
130
|
+
expect(screen.getByText("(Earlier turn PR)")).toBeInTheDocument();
|
|
131
|
+
expect(screen.getByRole("link",{name:"#12 Improve search",hidden:true})).toHaveAttribute("href",url);
|
|
132
|
+
view.rerender(<MantineProvider><CardStatusLine earlierPullRequest={earlier} pullRequests={[{...pr,url:"https://github.com/example/repo/pull/13",title:"Current change"}]}><span>1h</span></CardStatusLine></MantineProvider>);
|
|
133
|
+
expect(screen.queryByText("(Earlier turn PR)")).toBeNull();
|
|
134
|
+
expect(screen.queryByRole("link",{name:"#12 Improve search",hidden:true})).toBeNull();
|
|
135
|
+
expect(screen.getByRole("link",{name:"#13 Current change",hidden:true})).toBeInTheDocument();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("earlier PR note stays directly below the link before an access warning", () => {
|
|
139
|
+
render(<MantineProvider><CardStatusLine earlierPullRequest={{...pr,accessIssue:'denied'}}><span>1h</span></CardStatusLine></MantineProvider>);
|
|
140
|
+
fireEvent.mouseEnter(screen.getByLabelText('Turn status details'));
|
|
141
|
+
const link=screen.getByRole('link',{name:'#12 Improve search',hidden:true});
|
|
142
|
+
const note=screen.getByText('(Earlier turn PR)');
|
|
143
|
+
const warning=screen.getByText(/Access denied to/);
|
|
144
|
+
expect(link.compareDocumentPosition(note) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
145
|
+
expect(note.compareDocumentPosition(warning) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
146
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { PrOpenIcon, PrStatusIcon, PrDraftIcon, PrMergedIcon, PrClosedIcon } from "./PrGlyph";
|
|
2
|
+
import { Anchor, Box, Popover, Stack, Text, VisuallyHidden } from "@mantine/core";
|
|
3
|
+
import { IconAlertTriangle, IconHourglass, IconCircleCheck, IconCircleX, IconLoader, IconMinus, IconClock, IconChartBar } from "@tabler/icons-react";
|
|
4
|
+
import { useEffect, useId, useRef, useState, type CSSProperties, type ReactNode } from "react";
|
|
5
|
+
import { formatWorkedDuration } from "../../time.js";
|
|
6
|
+
import { turnUsageLabel, useAnimatedUsage } from "./SavedUsageFormat";
|
|
7
|
+
import type { SavedUsage, PullRequestStatus } from "../types";
|
|
8
|
+
|
|
9
|
+
export function nextRunLabel(value?: string | null, now = new Date()) {
|
|
10
|
+
if (!value || !Number.isFinite(new Date(value).getTime())) return "Next run time unavailable";
|
|
11
|
+
const date = new Date(value);
|
|
12
|
+
const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1);
|
|
13
|
+
const day = date.toDateString() === now.toDateString() ? "Today" : date.toDateString() === tomorrow.toDateString() ? "Tomorrow"
|
|
14
|
+
: date.toLocaleDateString(undefined, { month: "short", day: "numeric", ...(date.getFullYear() !== now.getFullYear() ? { year: "numeric" } : {}) });
|
|
15
|
+
return `Next run: ${day} at ${date.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" })}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function prPresentation(pr: PullRequestStatus) {
|
|
19
|
+
if (pr.state === "merged") return { Icon: PrMergedIcon, color: "#b28bdf", dot: null, label: "Merged" };
|
|
20
|
+
if (pr.state === "closed") return { Icon: PrClosedIcon, color: "#f0787e", dot: null, label: "Closed, not merged" };
|
|
21
|
+
if (pr.state === "draft") return { Icon: PrDraftIcon, color: "var(--taskchef-muted)", dot: null, label: "Draft" };
|
|
22
|
+
if (pr.accessIssue && pr.state === "unknown") return { Icon: IconAlertTriangle, color: "#eac54f", dot: null, label: "Repository access needed" };
|
|
23
|
+
if (pr.state === "unknown") return { Icon: PrOpenIcon, color: "var(--taskchef-muted)", dot: null, label: "PR status not checked or unavailable" };
|
|
24
|
+
if (pr.hasMergeConflicts || pr.checks === "failed") return { Icon: PrStatusIcon, color: "var(--taskchef-muted)", dot: "#e56b6f", label: pr.hasMergeConflicts ? "Merge conflicts" : "Checks failed" };
|
|
25
|
+
if (pr.canMerge || pr.checks === "passed") return { Icon: PrStatusIcon, color: "var(--taskchef-muted)", dot: "#39c681", label: pr.canMerge ? "Ready to merge" : "Checks passed" };
|
|
26
|
+
return { Icon: PrStatusIcon, color: "var(--taskchef-muted)", dot: "#eac54f", label: pr.checks === "none" ? "No checks" : pr.checks === "pending" ? "Checks pending" : "Open PR" };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function PullRequestIcons({ pullRequests = [] }: { pullRequests?: PullRequestStatus[] }) {
|
|
30
|
+
return <Box className="taskchef-card-pr-icons">{pullRequests.map(pr => {
|
|
31
|
+
const { Icon, color, dot, label } = prPresentation(pr);
|
|
32
|
+
return <span key={pr.url} className="taskchef-card-status-icon" aria-label={`${prTitle(pr)}: ${label}`} style={{ color, "--taskchef-pr-dot-color": dot ?? "currentColor" } as CSSProperties}>
|
|
33
|
+
<Icon size={15} stroke={1.5} aria-hidden />
|
|
34
|
+
{pr.accessIssue && pr.state !== "unknown" && <IconAlertTriangle className="taskchef-pr-access-warning" size={10} aria-hidden />}
|
|
35
|
+
</span>;
|
|
36
|
+
})}</Box>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function CardStatusLine({ children, pullRequests = [], earlierPullRequest, scheduled, nextRunAt, durationMs, startedAt, subagentCount, turnUsage, turnId, animateUsage }: {
|
|
40
|
+
subagentCount?: number;
|
|
41
|
+
turnUsage?: SavedUsage;
|
|
42
|
+
turnId?: string | null;
|
|
43
|
+
animateUsage?: boolean;
|
|
44
|
+
children: ReactNode;
|
|
45
|
+
pullRequests?: PullRequestStatus[];
|
|
46
|
+
earlierPullRequest?: PullRequestStatus;
|
|
47
|
+
scheduled?: boolean;
|
|
48
|
+
nextRunAt?: string | null;
|
|
49
|
+
durationMs?: number | null;
|
|
50
|
+
startedAt?: string | null;
|
|
51
|
+
}) {
|
|
52
|
+
const showEarlierPr = !pullRequests.length && !!earlierPullRequest;
|
|
53
|
+
const displayedPrs = showEarlierPr ? [earlierPullRequest!] : pullRequests;
|
|
54
|
+
const descriptionId = useId();
|
|
55
|
+
const [opened, setOpened] = useState(false);
|
|
56
|
+
const [now, setNow] = useState(Date.now);
|
|
57
|
+
const start = startedAt ? Date.parse(startedAt) : NaN;
|
|
58
|
+
const running = Number.isFinite(start);
|
|
59
|
+
const animatedUsage = useAnimatedUsage(turnUsage, animateUsage ?? running, turnId ?? startedAt);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!running) return;
|
|
62
|
+
setNow(Date.now());
|
|
63
|
+
const tick = window.setInterval(() => setNow(Date.now()), 60000);
|
|
64
|
+
return () => window.clearInterval(tick);
|
|
65
|
+
}, [running, startedAt]);
|
|
66
|
+
const duration = formatWorkedDuration(running ? Math.max(0, now - start) : durationMs);
|
|
67
|
+
const hasNextRun = scheduled && !!nextRunAt && Number.isFinite(Date.parse(nextRunAt));
|
|
68
|
+
const hasUsage = !!turnUsage && (!!turnUsage.samples || turnUsage.partial || turnUsage.costPartial);
|
|
69
|
+
const hasInfo = hasUsage || duration !== "—" || displayedPrs.length > 0 || hasNextRun;
|
|
70
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
71
|
+
const target = useRef<HTMLDivElement>(null);
|
|
72
|
+
const popup = useRef<HTMLDivElement>(null);
|
|
73
|
+
function show() { if (timer.current) clearTimeout(timer.current); if (hasInfo) { setNow(Date.now()); setOpened(true); } }
|
|
74
|
+
function leave() {
|
|
75
|
+
if (timer.current) clearTimeout(timer.current);
|
|
76
|
+
timer.current = setTimeout(() => {
|
|
77
|
+
if (!target.current?.contains(document.activeElement) && !popup.current?.contains(document.activeElement)) setOpened(false);
|
|
78
|
+
}, 180);
|
|
79
|
+
}
|
|
80
|
+
useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);
|
|
81
|
+
useEffect(() => { if (!hasInfo) setOpened(false); }, [hasInfo]);
|
|
82
|
+
return <Popover opened={opened && hasInfo} onChange={setOpened} position="top" withArrow withinPortal width={300} zIndex={400}>
|
|
83
|
+
<Popover.Target>
|
|
84
|
+
<Box ref={target} className="taskchef-card-stats" tabIndex={hasInfo ? 0 : undefined} aria-label="Turn status details" aria-describedby={displayedPrs.length ? descriptionId : undefined}
|
|
85
|
+
onMouseEnter={show} onMouseLeave={leave} onFocus={show} onBlur={leave} onClick={show}
|
|
86
|
+
onKeyDown={event => {
|
|
87
|
+
if (event.key === "Escape") { setOpened(false); event.stopPropagation(); }
|
|
88
|
+
if (event.key === "Tab" && !event.shiftKey && opened && popup.current?.querySelector("a")
|
|
89
|
+
&& (event.target !== target.current || !target.current?.querySelector('button:not(:disabled), [tabindex="0"]'))) {
|
|
90
|
+
event.preventDefault(); popup.current.querySelector("a")?.focus();
|
|
91
|
+
}
|
|
92
|
+
}}>
|
|
93
|
+
{children}
|
|
94
|
+
{hasUsage && <span className="taskchef-card-turn-usage">{turnUsageLabel(animatedUsage!)}</span>}
|
|
95
|
+
{scheduled && <span className="taskchef-card-status-icon" aria-label="Active schedule"><IconClock className="taskchef-schedule-clock" size={15} stroke={1.5} aria-hidden /></span>}
|
|
96
|
+
<PullRequestIcons pullRequests={displayedPrs} />
|
|
97
|
+
{displayedPrs.length > 0 && <VisuallyHidden id={descriptionId}>{showEarlierPr ? "Earlier turn PR. " : ""}{displayedPrs.map(pr => `${prTitle(pr)}: ${pr.state === "merged" ? "Merged" : pr.state === "closed" ? "Closed, not merged" : pr.state === "draft" ? "Draft" : pr.state === "unknown" ? "PR status not checked or unavailable" : pr.hasMergeConflicts ? "Open, merge conflicts" : "Open"}`).join(". ")}</VisuallyHidden>}
|
|
98
|
+
</Box>
|
|
99
|
+
</Popover.Target>
|
|
100
|
+
<Popover.Dropdown className="taskchef-status-tooltip" p={9} ref={popup} onMouseEnter={show} onMouseLeave={leave} onFocus={show} onBlur={leave}
|
|
101
|
+
onKeyDown={event => { if (event.key === "Escape") { setOpened(false); target.current?.focus(); setOpened(false); event.stopPropagation(); } }}>
|
|
102
|
+
<Stack gap={5}>
|
|
103
|
+
{duration !== "—" && <StatusRow icon={<IconHourglass size={15} stroke={1.5} />}>Worked for {duration}</StatusRow>}
|
|
104
|
+
{hasUsage && <StatusRow icon={<IconChartBar size={15} stroke={1.5} />}>Turn usage: {turnUsageLabel(animatedUsage!)}</StatusRow>}
|
|
105
|
+
{displayedPrs.map(pr => <PullRequestInfo key={pr.url} pr={pr} showChecks={false} earlierTurn={showEarlierPr} />)}
|
|
106
|
+
{hasNextRun && <StatusRow icon={<IconClock className="taskchef-schedule-clock" size={15} stroke={1.5} />}>{nextRunLabel(nextRunAt)}</StatusRow>}
|
|
107
|
+
</Stack>
|
|
108
|
+
</Popover.Dropdown>
|
|
109
|
+
</Popover>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function prTitle(pr: PullRequestStatus) {
|
|
113
|
+
const number = pr.url.match(/\/pull\/(\d+)/)?.[1];
|
|
114
|
+
const title = pr.title || pr.url.match(/github\.com\/(.+)\/pull\/\d+/)?.[1] || "Pull request";
|
|
115
|
+
return `${number ? `#${number} ` : ""}${title}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function StatusRow({ icon, children }: { icon: ReactNode; children: ReactNode }) {
|
|
119
|
+
return <Box className="taskchef-status-row"><span className="taskchef-status-row-icon" aria-hidden>{icon}</span><Box className="taskchef-status-row-content">{children}</Box></Box>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function PullRequestInfo({ pr, showAccess = true, showChecks = true, earlierTurn = false }: { pr: PullRequestStatus; showAccess?: boolean; showChecks?: boolean; earlierTurn?: boolean }) {
|
|
123
|
+
const title = prTitle(pr);
|
|
124
|
+
const safeLink = /^https:\/\/github\.com\/[A-Za-z0-9-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9]\d*\/?$/.test(pr.url);
|
|
125
|
+
const { Icon, color, dot } = prPresentation(pr);
|
|
126
|
+
const CiIcon = pr.checks === "passed" ? IconCircleCheck : pr.checks === "failed" ? IconCircleX : pr.checks === "pending" ? IconLoader : IconMinus;
|
|
127
|
+
const repo = safeLink ? pr.url.match(/github\.com\/(.+)\/pull\//)?.[1] : null;
|
|
128
|
+
return <Stack gap={5}>
|
|
129
|
+
<StatusRow icon={<span className="taskchef-popup-pr-icon" style={{ color, "--taskchef-pr-dot-color": dot ?? "currentColor" } as CSSProperties}><Icon size={15} stroke={1.5} /></span>}>
|
|
130
|
+
{safeLink ? <Anchor className="taskchef-status-pr-title" href={pr.url} target="_blank" rel="noopener noreferrer" title={title}>{title}</Anchor> : <Text className="taskchef-status-pr-title">{title}</Text>}
|
|
131
|
+
</StatusRow>
|
|
132
|
+
{earlierTurn && <StatusRow icon={null}><Text size="xs" c="dimmed">(Earlier turn PR)</Text></StatusRow>}
|
|
133
|
+
{showChecks && pr.checks !== "unknown" && <StatusRow icon={<CiIcon size={15} stroke={1.5} />}>{pr.checks === "passed" ? "CI passed" : pr.checks === "failed" ? "CI failed" : pr.checks === "pending" ? "CI pending" : "No CI checks"}</StatusRow>}
|
|
134
|
+
{showAccess && pr.accessIssue && safeLink && <Box className="taskchef-pr-access-alert">
|
|
135
|
+
<StatusRow icon={null}><Text size="xs">{pr.accessIssue === "denied" ? `Access denied to ${repo}.` : `PR #${pr.url.match(/\/pull\/(\d+)/)?.[1]} is unavailable. It may be private or removed.`} <Anchor size="xs" href="https://github.com/apps/taskchef/installations/new" target="_blank" rel="noopener noreferrer">{pr.accessIssue === "denied" ? "Grant access" : "Check access"}</Anchor></Text></StatusRow>
|
|
136
|
+
</Box>}
|
|
137
|
+
</Stack>;
|
|
138
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import type { Task } from "../types";
|
|
3
|
+
import { ReplyImagePreview } from "./ReplyImagePreview";
|
|
4
|
+
|
|
5
|
+
type Image = { url: string; alt: string };
|
|
6
|
+
export function ImageGallery({ task, images, loadImage, onLightboxChange }: { task: Task; images: Image[]; loadImage: (task: Task, url?: string) => Promise<string | null>; onLightboxChange?: (opened: boolean) => void }) {
|
|
7
|
+
const signature = JSON.stringify([task.id, task.turnId, images.map(image => image.url)]);
|
|
8
|
+
const [loaded, setLoaded] = useState<{ signature: string; sources: (string | null)[] } | null>(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
let cancelled = false;
|
|
11
|
+
void Promise.all(images.map(image => loadImage(task, image.url).catch(() => null))).then(sources => {
|
|
12
|
+
if (!cancelled) setLoaded({ signature, sources });
|
|
13
|
+
});
|
|
14
|
+
return () => { cancelled = true; };
|
|
15
|
+
}, [signature, loadImage]);
|
|
16
|
+
const sources = loaded?.signature === signature ? loaded.sources : null;
|
|
17
|
+
return <div className="taskchef-prompt-images" aria-busy={!sources}>
|
|
18
|
+
{images.map((image, index) => sources
|
|
19
|
+
? sources[index] && <ReplyImagePreview key={`${signature}:${index}`} task={task} url={image.url} alt={image.alt} loadImage={loadImage} source={sources[index]} onLightboxChange={onLightboxChange} />
|
|
20
|
+
: <span key={image.url + index} className="taskchef-image-placeholder" role="status" aria-label={`Loading ${image.alt}`} />)}
|
|
21
|
+
</div>;
|
|
22
|
+
}
|
|
@@ -19,18 +19,18 @@ vi.mock('@mantine/core', async () => {
|
|
|
19
19
|
|
|
20
20
|
afterEach(() => { cleanup(); vi.unstubAllGlobals(); vi.clearAllMocks(); });
|
|
21
21
|
|
|
22
|
-
test('shows the
|
|
22
|
+
test('shows the built-in TaskChef usage engine without an availability status', async () => {
|
|
23
23
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
24
24
|
ok: true,
|
|
25
25
|
json: async () => ({
|
|
26
26
|
profiles: [],
|
|
27
|
-
usageProvider: { provider: '
|
|
27
|
+
usageProvider: { provider: 'TaskChef', version: '8.6.2' },
|
|
28
28
|
}),
|
|
29
29
|
}));
|
|
30
30
|
render(<MantineProvider><ModelSettings /></MantineProvider>);
|
|
31
|
-
expect(await screen.findByText('
|
|
31
|
+
expect(await screen.findByText('TaskChef 8.6.2')).toBeInTheDocument();
|
|
32
32
|
expect(screen.getByText('Usage provider')).toBeInTheDocument();
|
|
33
|
-
expect(screen.getByText(/
|
|
33
|
+
expect(screen.getByText(/TaskChef’s accounting engine/)).toBeInTheDocument();
|
|
34
34
|
expect(screen.queryByText('available')).not.toBeInTheDocument();
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -99,7 +99,7 @@ export function ModelSettings() {
|
|
|
99
99
|
<Stack gap="xs">
|
|
100
100
|
<Title order={3} size="h5">Usage provider</Title>
|
|
101
101
|
<Text size="sm">{usageProvider.provider}{usageProvider.version ? ` ${usageProvider.version}` : ' version unavailable'}</Text>
|
|
102
|
-
<Text c="dimmed" size="sm">
|
|
102
|
+
<Text c="dimmed" size="sm">Reads saved Codex usage with TaskChef’s accounting engine.</Text>
|
|
103
103
|
</Stack>
|
|
104
104
|
</Paper>}
|
|
105
105
|
</Stack>;
|
|
@@ -21,3 +21,22 @@ test("uses the shared open-chat presentation and accessible task label", () => {
|
|
|
21
21
|
fireEvent.click(button);
|
|
22
22
|
expect(onClick).toHaveBeenCalledOnce();
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
test("opening a chat keeps its label and icon still while preventing another click", () => {
|
|
26
|
+
const click = vi.fn();
|
|
27
|
+
const view = render(<MantineProvider><OpenChatButton loading onClick={click} taskTitle="Example" /></MantineProvider>);
|
|
28
|
+
const button = screen.getByRole("button", {name:"Open chat for Example"});
|
|
29
|
+
expect(button).toBeDisabled();
|
|
30
|
+
expect(button).toHaveAttribute("aria-busy", "true");
|
|
31
|
+
expect(button).not.toHaveAttribute("data-loading");
|
|
32
|
+
expect(button.querySelector(".mantine-Button-loader")).toBeNull();
|
|
33
|
+
expect(button).toHaveTextContent("Open chat");
|
|
34
|
+
expect(button.querySelector("svg")).toHaveAttribute("aria-hidden", "true");
|
|
35
|
+
fireEvent.click(button);
|
|
36
|
+
expect(click).not.toHaveBeenCalled();
|
|
37
|
+
view.rerender(<MantineProvider><OpenChatButton onClick={click} taskTitle="Example" /></MantineProvider>);
|
|
38
|
+
expect(button).toBeEnabled();
|
|
39
|
+
expect(button).not.toHaveAttribute("aria-busy");
|
|
40
|
+
fireEvent.click(button);
|
|
41
|
+
expect(click).toHaveBeenCalledOnce();
|
|
42
|
+
});
|
|
@@ -15,7 +15,8 @@ export function OpenChatButton({
|
|
|
15
15
|
aria-label={`Open chat for ${taskTitle}`}
|
|
16
16
|
className="taskchef-open-chat"
|
|
17
17
|
leftSection={<IconArrowUpRight aria-hidden size={15} stroke={1.6} />}
|
|
18
|
-
|
|
18
|
+
disabled={loading}
|
|
19
|
+
aria-busy={loading || undefined}
|
|
19
20
|
onClick={onClick}
|
|
20
21
|
size="compact-sm"
|
|
21
22
|
variant="default"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SVGProps } from "react";
|
|
2
|
+
|
|
3
|
+
type Props = Omit<SVGProps<SVGSVGElement>, "stroke"> & { size?: number; stroke?: number };
|
|
4
|
+
type Shape = "open" | "status" | "draft" | "merged" | "closed";
|
|
5
|
+
|
|
6
|
+
// Drawn on a 20px grid to match Codex's branch layout and node sizes.
|
|
7
|
+
// The status dot replaces the right node; it is not an external badge.
|
|
8
|
+
function PrGlyph({ shape, size = 15, stroke: _stroke, ...props }: Props & { shape: Shape }) {
|
|
9
|
+
return <svg width={size} height={size} viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth={1.33} strokeLinecap="round" strokeLinejoin="round" {...props}>
|
|
10
|
+
<circle cx={5.42} cy={5} r={1.875} />
|
|
11
|
+
<path d="M5.42 6.875V13.125" />
|
|
12
|
+
<circle cx={5.42} cy={15} r={1.875} />
|
|
13
|
+
{shape === "merged" ? <>
|
|
14
|
+
<path d="M6.2 6.6C7.1 10.2 9.3 11.9 12.71 12.5" />
|
|
15
|
+
<circle cx={14.58} cy={12.5} r={1.875} />
|
|
16
|
+
</> : shape === "draft" ? <>
|
|
17
|
+
<circle cx={14.58} cy={5} r={1} fill="currentColor" stroke="none" />
|
|
18
|
+
<circle cx={14.58} cy={9.25} r={1} fill="currentColor" stroke="none" />
|
|
19
|
+
<circle cx={14.58} cy={15} r={1.875} />
|
|
20
|
+
</> : shape === "closed" ? <>
|
|
21
|
+
<path d="M12.92 3.33L16.25 6.67M16.25 3.33L12.92 6.67M14.58 9.58V13.125" />
|
|
22
|
+
<circle cx={14.58} cy={15} r={1.875} />
|
|
23
|
+
</> : <>
|
|
24
|
+
<path d="M11.67 3.33L10 5L11.67 6.67M10 5H13.33Q15 5 15 6.67V9.17" />
|
|
25
|
+
{shape === "status" ? <circle cx={15.14} cy={15.14} r={3.14} fill="var(--taskchef-pr-dot-color, currentColor)" stroke="none" />
|
|
26
|
+
: <path d="M15 12.5V16.67M12.92 14.58H17.08" />}
|
|
27
|
+
</>}
|
|
28
|
+
</svg>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function PrOpenIcon(props: Props) { return <PrGlyph {...props} shape="open" />; }
|
|
32
|
+
export function PrStatusIcon(props: Props) { return <PrGlyph {...props} shape="status" />; }
|
|
33
|
+
export function PrDraftIcon(props: Props) { return <PrGlyph {...props} shape="draft" />; }
|
|
34
|
+
export function PrMergedIcon(props: Props) { return <PrGlyph {...props} shape="merged" />; }
|
|
35
|
+
export function PrClosedIcon(props: Props) { return <PrGlyph {...props} shape="closed" />; }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Badge, Group } from "@mantine/core";
|
|
2
|
+
import { IconGitMerge, IconGitPullRequest, IconGitPullRequestDraft } from "@tabler/icons-react";
|
|
3
|
+
import type { PullRequestStatus } from "../types";
|
|
4
|
+
|
|
5
|
+
export function PullRequestBadges({ pullRequests = [] }: { pullRequests?: PullRequestStatus[] }) {
|
|
6
|
+
if (!pullRequests.length) return null;
|
|
7
|
+
return <Group gap={4} wrap="wrap" aria-label="Attached pull requests">
|
|
8
|
+
{pullRequests.map((pr) => {
|
|
9
|
+
const merged = pr.state === "merged";
|
|
10
|
+
const draft = pr.state === "draft";
|
|
11
|
+
const label = merged ? "Merged" : draft ? "Draft" : pr.state === "closed" ? "Closed, not merged" : pr.state === "unknown" ? "Status unavailable" : pr.checks === "passed" ? "Checks passed" : pr.checks === "none" ? "No checks" : pr.checks === "pending" ? "Checks pending" : pr.checks === "failed" ? "Checks failed" : "Checks unavailable";
|
|
12
|
+
const Icon = merged ? IconGitMerge : draft ? IconGitPullRequestDraft : IconGitPullRequest;
|
|
13
|
+
const color = merged ? "violet" : draft || pr.state === "unknown" ? "gray" : pr.state === "closed" || pr.checks === "failed" ? "red" : pr.checks === "passed" ? "green" : "yellow";
|
|
14
|
+
const safeLink = /^https:\/\/github\.com\/[A-Za-z0-9-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9]\d*\/?$/.test(pr.url);
|
|
15
|
+
return <Badge key={pr.url} component="a" href={safeLink ? pr.url : undefined} target="_blank" rel="noopener noreferrer" variant="light" color={color} size="xs" leftSection={<Icon size={12} aria-hidden />} title={`${pr.url}\n${label}${pr.error ? `: ${pr.error}` : ""}${pr.checkedAt ? `\nChecked ${new Date(pr.checkedAt).toLocaleString()}` : ""}`} style={{ cursor: "pointer", textTransform: "none", maxWidth: "100%" }}>#{pr.url.match(/\/pull\/(\d+)/)?.[1] ?? "PR"} · {label}</Badge>;
|
|
16
|
+
})}
|
|
17
|
+
</Group>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MantineProvider } from '@mantine/core';
|
|
2
|
+
import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import { afterEach, expect, test, vi } from 'vitest';
|
|
4
|
+
import { QueuedPrompts } from './QueuedPrompts';
|
|
5
|
+
import { fixtureTask } from '../fixtures';
|
|
6
|
+
afterEach(()=>{cleanup();vi.restoreAllMocks();});
|
|
7
|
+
const prompts=[{id:'first',text:'First follow-up',images:[]},{id:'second',text:'Second follow-up\n'+ 'More details. '.repeat(30),images:[]}];
|
|
8
|
+
test('queue preserves execution order and removes started prompts on refresh',()=>{
|
|
9
|
+
const task={...fixtureTask(),queuedPrompts:prompts};
|
|
10
|
+
const {rerender}=render(<MantineProvider><QueuedPrompts task={task} compact /></MantineProvider>);
|
|
11
|
+
expect(screen.getByText('2 queued')).toBeVisible();
|
|
12
|
+
expect(screen.getAllByRole('listitem')[0]).toHaveTextContent('First follow-up');
|
|
13
|
+
for (const item of screen.getAllByRole('listitem')) expect(item.firstElementChild).toHaveClass('taskchef-queue-icon');
|
|
14
|
+
rerender(<MantineProvider><QueuedPrompts task={{...task,queuedPrompts:prompts.slice(1)}} compact /></MantineProvider>);
|
|
15
|
+
expect(screen.getByText('1 queued')).toBeVisible(); expect(screen.queryByText('First follow-up')).toBeNull();
|
|
16
|
+
rerender(<MantineProvider><QueuedPrompts task={{...task,queuedPrompts:[]}} compact /></MantineProvider>);
|
|
17
|
+
expect(screen.queryByRole('region',{name:'Queued prompts'})).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
test('Details provides expansion and clickable queued image thumbnails', async()=>{
|
|
20
|
+
vi.spyOn(HTMLElement.prototype,'scrollHeight','get').mockReturnValue(200);
|
|
21
|
+
vi.spyOn(HTMLElement.prototype,'clientHeight','get').mockReturnValue(60);
|
|
22
|
+
const load=vi.fn().mockResolvedValue('data:image/png;base64,iVBORw0KGgo=');
|
|
23
|
+
const task={...fixtureTask(),queuedPrompts:[{...prompts[1],images:[{url:'taskchef-queue:second:0',alt:'Queued screenshot'}]}]};
|
|
24
|
+
render(<MantineProvider><QueuedPrompts task={task} loadImage={load}/></MantineProvider>);
|
|
25
|
+
expect(screen.getByText('Queued prompts (1)')).toBeVisible();
|
|
26
|
+
expect(screen.getByRole('listitem').firstElementChild).toHaveClass('taskchef-queue-icon');
|
|
27
|
+
fireEvent.click(screen.getByRole('button',{name:'Show more…'}));
|
|
28
|
+
expect(screen.getByRole('button',{name:'Show less'})).toHaveAttribute('aria-expanded','true');
|
|
29
|
+
const thumbnail=await screen.findByRole('button',{name:'Enlarge Queued screenshot'});
|
|
30
|
+
fireEvent.click(thumbnail);
|
|
31
|
+
await waitFor(()=>expect(screen.getByRole('dialog',{name:'Queued screenshot'})).toBeVisible());
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('short queued text offers expansion when narrow wrapping exceeds three rendered lines',()=>{
|
|
35
|
+
vi.spyOn(HTMLElement.prototype,'scrollHeight','get').mockReturnValue(100);
|
|
36
|
+
vi.spyOn(HTMLElement.prototype,'clientHeight','get').mockReturnValue(60);
|
|
37
|
+
const task={...fixtureTask(),queuedPrompts:[{id:'short',text:'A short request that wraps on a narrow screen.',images:[]}]};
|
|
38
|
+
render(<MantineProvider><QueuedPrompts task={task}/></MantineProvider>);
|
|
39
|
+
expect(screen.getByRole('button',{name:'Show more…'})).toBeVisible();
|
|
40
|
+
fireEvent.click(screen.getByRole('button',{name:'Show more…'}));
|
|
41
|
+
expect(screen.getByRole('button',{name:'Show less'})).toHaveAttribute('aria-expanded','true');
|
|
42
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CollapsedMarkdown } from "./TurnContent";
|
|
2
|
+
import type { Task } from "../types";
|
|
3
|
+
import { ReplyMarkdown } from "./ReplyMarkdown";
|
|
4
|
+
import { ImageGallery } from "./ImageGallery";
|
|
5
|
+
import { ReplyCover } from "./ReplyCover";
|
|
6
|
+
|
|
7
|
+
function QueueIcon() {
|
|
8
|
+
return <svg className="taskchef-queue-icon" width="16" height="16" viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
9
|
+
<path d="M3.33 3.33V11c0 3.2.8 4 4 4h9.34M14.58 12.92 16.67 15l-2.09 2.08M7.5 5h6.67M7.5 9.58h4.17" />
|
|
10
|
+
</svg>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function QueuedPrompts({ task, compact = false, loadImage, onLightboxChange, onOpen }: { task: Task; compact?: boolean; loadImage?: (task: Task, url?: string) => Promise<string | null>; onLightboxChange?: (opened: boolean) => void; onOpen?: () => void }) {
|
|
14
|
+
if (!task.queuedPrompts?.length) return null;
|
|
15
|
+
return <section className={`taskchef-queued-prompts${compact ? " is-compact" : ""}`} aria-label="Queued prompts">
|
|
16
|
+
<div className="taskchef-queued-heading">{compact ? `${task.queuedPrompts.length} queued` : `Queued prompts (${task.queuedPrompts.length})`}</div>
|
|
17
|
+
<ol>{task.queuedPrompts.map(prompt => compact
|
|
18
|
+
? <li key={prompt.id}>
|
|
19
|
+
<QueueIcon />
|
|
20
|
+
{loadImage && onOpen && prompt.images[0] && <ReplyCover task={task} image={prompt.images[0]} loadImage={loadImage} onOpen={onOpen} />}
|
|
21
|
+
<span><ReplyMarkdown text={prompt.text} compact hideRequestLabels renderImage={() => null} /></span>
|
|
22
|
+
</li>
|
|
23
|
+
: <QueuedPrompt key={prompt.id} task={task} prompt={prompt} loadImage={loadImage} onLightboxChange={onLightboxChange} />)}</ol>
|
|
24
|
+
</section>;
|
|
25
|
+
}
|
|
26
|
+
function QueuedPrompt({ task, prompt, loadImage, onLightboxChange }: { task: Task; prompt: NonNullable<Task['queuedPrompts']>[number]; loadImage?: (task: Task, url?: string) => Promise<string | null>; onLightboxChange?: (opened: boolean) => void }) {
|
|
27
|
+
return <li>
|
|
28
|
+
<QueueIcon />
|
|
29
|
+
<div className="taskchef-queue-content">
|
|
30
|
+
{loadImage && prompt.images.length > 0 && <ImageGallery task={task} images={prompt.images} loadImage={loadImage} onLightboxChange={onLightboxChange} />}
|
|
31
|
+
<CollapsedMarkdown text={prompt.text} />
|
|
32
|
+
</div>
|
|
33
|
+
</li>;
|
|
34
|
+
}
|