github-issue-tower-defence-management 1.86.0 → 1.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.eslintrc.cjs +5 -1
  2. package/.github/workflows/console-ui.yml +47 -0
  3. package/.prettierignore +3 -0
  4. package/CHANGELOG.md +15 -0
  5. package/README.md +42 -0
  6. package/bin/adapter/entry-points/console/ui-dist/assets/index-DFxrSRH4.css +1 -0
  7. package/bin/adapter/entry-points/console/ui-dist/assets/index-DcOZ02ON.js +49 -0
  8. package/bin/adapter/entry-points/console/ui-dist/index.html +13 -0
  9. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +54 -12
  10. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  11. package/bin/adapter/entry-points/handlers/inTmuxByHumanDataWriter.js +67 -0
  12. package/bin/adapter/entry-points/handlers/inTmuxByHumanDataWriter.js.map +1 -0
  13. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +306 -0
  14. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  15. package/bin/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.js +91 -0
  16. package/bin/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.js.map +1 -0
  17. package/package.json +22 -2
  18. package/scripts/copyConsoleUiDist.mjs +35 -0
  19. package/src/adapter/entry-points/console/ui/.storybook/main.ts +12 -0
  20. package/src/adapter/entry-points/console/ui/.storybook/preview.ts +15 -0
  21. package/src/adapter/entry-points/console/ui/biome.json +47 -0
  22. package/src/adapter/entry-points/console/ui/components.json +20 -0
  23. package/src/adapter/entry-points/console/ui/index.html +12 -0
  24. package/src/adapter/entry-points/console/ui/src/components/ui/badge.stories.tsx +35 -0
  25. package/src/adapter/entry-points/console/ui/src/components/ui/badge.tsx +28 -0
  26. package/src/adapter/entry-points/console/ui/src/components/ui/button.stories.tsx +34 -0
  27. package/src/adapter/entry-points/console/ui/src/components/ui/button.tsx +50 -0
  28. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.stories.tsx +44 -0
  29. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.tsx +58 -0
  30. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.stories.tsx +34 -0
  31. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.tsx +32 -0
  32. package/src/adapter/entry-points/console/ui/src/features/console/fixtures.ts +47 -0
  33. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleList.ts +65 -0
  34. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleToken.ts +64 -0
  35. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +19 -0
  36. package/src/adapter/entry-points/console/ui/src/features/console/types.ts +69 -0
  37. package/src/adapter/entry-points/console/ui/src/index.css +31 -0
  38. package/src/adapter/entry-points/console/ui/src/lib/utils.ts +4 -0
  39. package/src/adapter/entry-points/console/ui/src/main.tsx +15 -0
  40. package/src/adapter/entry-points/console/ui/src/vite-env.d.ts +1 -0
  41. package/src/adapter/entry-points/console/ui/tsconfig.json +24 -0
  42. package/src/adapter/entry-points/console/ui/vite.config.ts +19 -0
  43. package/src/adapter/entry-points/console/ui-dist/assets/index-DFxrSRH4.css +1 -0
  44. package/src/adapter/entry-points/console/ui-dist/assets/index-DcOZ02ON.js +49 -0
  45. package/src/adapter/entry-points/console/ui-dist/index.html +13 -0
  46. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +26 -0
  47. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +266 -0
  48. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +103 -0
  49. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +630 -0
  50. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +492 -0
  51. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +51 -0
  52. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +285 -0
  53. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +182 -0
  54. package/tsconfig.build.json +7 -1
  55. package/tsconfig.json +6 -1
  56. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  57. package/types/adapter/entry-points/handlers/inTmuxByHumanDataWriter.d.ts +16 -0
  58. package/types/adapter/entry-points/handlers/inTmuxByHumanDataWriter.d.ts.map +1 -0
  59. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +18 -1
  60. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  61. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +47 -0
  62. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
  63. package/types/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.d.ts +57 -0
  64. package/types/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.d.ts.map +1 -0
package/.eslintrc.cjs CHANGED
@@ -20,7 +20,11 @@ module.exports = {
20
20
  'unused-imports',
21
21
  ],
22
22
  root: true,
23
- ignorePatterns: fs.readFileSync('.gitignore', 'utf8').split('\n'),
23
+ ignorePatterns: [
24
+ ...fs.readFileSync('.gitignore', 'utf8').split('\n'),
25
+ 'src/adapter/entry-points/console/ui/**',
26
+ 'src/adapter/entry-points/console/ui-dist/**',
27
+ ],
24
28
  rules: {
25
29
  '@typescript-eslint/require-await': 'off',
26
30
  '@typescript-eslint/no-non-null-assertion': 'error',
@@ -0,0 +1,47 @@
1
+ name: Console UI
2
+
3
+ on:
4
+ push:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ concurrency:
10
+ group: console-ui-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ console-ui:
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - uses: step-security/harden-runner@v2
19
+ with:
20
+ egress-policy: audit
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v6
23
+
24
+ - uses: actions/setup-node@v6
25
+ with:
26
+ node-version: 24
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Biome check (console UI)
32
+ run: npx biome check src/adapter/entry-points/console/ui
33
+
34
+ - name: Type check (console UI)
35
+ run: npx tsc --noEmit -p src/adapter/entry-points/console/ui/tsconfig.json
36
+
37
+ - name: Build console UI (Vite)
38
+ run: npm run build:console-ui
39
+
40
+ - name: Build Storybook (console UI)
41
+ run: npm run console-ui:build-storybook
42
+
43
+ - name: Upload Storybook static
44
+ uses: actions/upload-artifact@v7
45
+ with:
46
+ name: console-ui-storybook
47
+ path: src/adapter/entry-points/console/ui/storybook-static
package/.prettierignore CHANGED
@@ -25,3 +25,6 @@ CODEOWNERS
25
25
  types
26
26
  bin
27
27
  .npmrc
28
+ src/adapter/entry-points/console/ui
29
+ src/adapter/entry-points/console/ui-dist
30
+ src/adapter/entry-points/console/ui/storybook-static
package/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ # [1.88.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.87.0...v1.88.0) (2026-06-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * generate in-tmux-by-human per-project and index JSON files in the scheduled daemon cycle ([#841](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/841)) ([704701f](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/704701f4bcb33b2963962388889cdd6aa7b88838)), closes [#840](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/840)
7
+
8
+ # [1.87.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.86.0...v1.87.0) (2026-06-18)
9
+
10
+
11
+ ### Features
12
+
13
+ * **console:** scaffold React console UI with build bundling and minimal tab view ([#849](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/849)) ([2b270d5](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/2b270d5af8eaaa143135830efbe5089a32bda671))
14
+ * **issue:** add IssueRepository read methods for Console read APIs ([#848](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/848)) ([4d93fc7](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/4d93fc7aab261ca916df230221e2ab70f8ea5cc8))
15
+
1
16
  # [1.86.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.85.0...v1.86.0) (2026-06-18)
2
17
 
3
18
 
package/README.md CHANGED
@@ -212,6 +212,10 @@ awLogDirectoryPath?: string # Optional: Directory path where aw log files named
212
212
  awLogStaleThresholdMinutes?: number # Optional: Minutes since last aw log mtime after which a Preparation issue is considered orphaned even when pgrep still returns 0. Requires awLogDirectoryPath
213
213
  labelsAsLlmAgentName?: string[] # Optional: List of issue labels that are themselves agent names. When an issue carries any label that is included in this list, that label name is used as the agent name. Selection precedence is: (1) explicit `llm-agent:` label, (2) labelsAsLlmAgentName entry match, (3) `category:` label, (4) defaultLlmAgentName, (5) defaultAgentName
214
214
  consoleDataOutputDir?: string # Optional: Base output directory for the per-project Console list.json files written each schedule cycle. When unset, Console list generation is skipped
215
+ inTmuxDataOutputDir?: string # Optional: Base output directory for the in-tmux-by-human per-project and index JSON files written each schedule cycle. When unset, in-tmux-by-human generation is skipped
216
+ inTmuxConsoleBaseUrl?: string # Optional: Console base URL used to build the tdpmConsoleUrl in the v3/v4 in-tmux-by-human files (for example https://console.example.com). When unset, the v3 and v4 files are skipped
217
+ inTmuxConsoleToken?: string # Optional: Token embedded in the ?k= query string of the v4 in-tmux-by-human files. When unset, the v4 per-project file and index.v4.json are skipped
218
+ inTmuxProjectOrder?: string[] # Optional: Ordered list of project codes used to build the in-tmux-by-human index files. When unset or empty, the index files are skipped
215
219
  changeTargetPathAliases?: # Optional: Map of short alias keys to full repository-root-relative directory paths. Allows `change-target:<alias>` labels to reference deeply nested paths that exceed GitHub's 50-character label limit. When a `change-target:` label's value matches a key in this map, it is expanded to the corresponding full path before confinement checking. Values with leading or trailing slashes are normalized automatically. Example below
216
220
  adapter-interfaces: src/domain/usecases/adapter-interfaces
217
221
  ```
@@ -406,6 +410,44 @@ The `triage` tab omits `statusOptions`, adds `storyOptions` (all story field opt
406
410
  - `storyColors`: Map from story name to its color. Object value (`{ "color": ... }`) for `prs`/`unread`/`failed-preparation`; plain string value for `triage`.
407
411
  - `items`: Selected issues, stable-sorted by their story's position in `storyOrder` (unknown stories sorted last). No item carries a `body` field.
408
412
 
413
+ ## In-Tmux-by-Human Data
414
+
415
+ When `inTmuxDataOutputDir` is configured, each schedule cycle also writes the in-tmux-by-human data files for the current project, generated from the same in-memory project and issue data already loaded for the cycle (no additional GitHub API calls). Each file is written atomically (written to a `.tmp` file then renamed) so external readers never see a partial write. When `inTmuxDataOutputDir` is unset the generation is skipped, and any error during generation is logged and swallowed so the schedule cycle is never affected.
416
+
417
+ ### Item Selection
418
+
419
+ An issue is selected when its status equals `In Tmux by human` (exact match), it is open, and its assignees include the project manager. Selected issues are grouped by their story value (a null story maps to the empty string). Groups are ordered by the project story option display order; a group whose story is not among the story options is placed at the tail, ordered by the story string. Within a group, issues keep their input order.
420
+
421
+ ### Files
422
+
423
+ For the current project code `{pjcode}` (the configured `projectName`):
424
+
425
+ ```
426
+ {inTmuxDataOutputDir}/{pjcode}.json # v1: [{ story, urls: string[] }]
427
+ {inTmuxDataOutputDir}/{pjcode}.v2.json # v2: [{ story, urls: [{ url, title }] }]
428
+ {inTmuxDataOutputDir}/{pjcode}.v3.json # v3: { version, overviewUrl, tdpmConsoleUrl, groups: [{ story, urls: [{ url, title }] }] }
429
+ {inTmuxDataOutputDir}/{pjcode}.v4.json # v4: { version, overviewUrl, tdpmConsoleUrl, newIssueUrl, groups: [{ story, sessions: [{ name, description }] }] }
430
+ ```
431
+
432
+ and the cross-project index files:
433
+
434
+ ```
435
+ {inTmuxDataOutputDir}/index.json # { projects: string[] }
436
+ {inTmuxDataOutputDir}/index.v2.json # { version: 2, projects: string[] }
437
+ {inTmuxDataOutputDir}/index.v3.json # { version: 3, projects: string[] }
438
+ {inTmuxDataOutputDir}/index.v4.json # { version: 4, projects: [{ name, path }] }
439
+ ```
440
+
441
+ The index files list every project in `inTmuxProjectOrder` whose `{name}.json` already exists in the output directory, so successive per-project schedule cycles incrementally build the same shared index. In `index.v4.json`, each `path` is `/{basename of inTmuxDataOutputDir}/{name}.v4.json?k={token}`.
442
+
443
+ ### Field Descriptions
444
+
445
+ - `overviewUrl`: The GitHub Project board URL, taken from the project `url`.
446
+ - `tdpmConsoleUrl`: `{inTmuxConsoleBaseUrl}/projects/{pjcode}/prs`. The v4 variant appends `?k={token}`. The v3 and v4 files are skipped when `inTmuxConsoleBaseUrl` is unset.
447
+ - `newIssueUrl` (v4 only): `https://github.com/{org}/{workingReport.repo}/issues/new?assignees={manager}`, derived from existing config values.
448
+ - `groups`: Story groups. The v3 `groups` carry a `urls` array using the v2 `{ url, title }` entry shape. The v4 `groups` use tmux terminology: each group carries a `sessions` array and each session is `{ name, description }` where `name` is the GitHub issue URL and `description` is the issue title.
449
+ - Token handling: The v4 per-project file and `index.v4.json` are skipped when `inTmuxConsoleToken` is unset. The token value is never written to source code, tests, or documentation; it is supplied through configuration only.
450
+
409
451
  ## Token Rotation Order File
410
452
 
411
453
  After each schedule cycle where Claude OAuth token rotation is active, TDPM writes the computed rotation order to:
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-border:#e5e5e5;--color-input:#e5e5e5;--color-ring:#0a0a0a;--color-background:#fff;--color-foreground:#0a0a0a;--color-primary:#171717;--color-primary-foreground:#fafafa;--color-secondary:#f5f5f5;--color-secondary-foreground:#171717;--color-muted-foreground:#737373;--color-accent:#f5f5f5;--color-accent-foreground:#171717;--color-destructive:#ef4444}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.flex{display:flex}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.max-w-3xl{max-width:var(--container-3xl)}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-2{gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.border-input{border-color:var(--color-input)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-destructive{color:var(--color-destructive)}.text-foreground{color:var(--color-foreground)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.underline-offset-2{text-underline-offset:2px}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--color-primary) 90%,transparent)}}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--color-secondary) 80%,transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}}body{background-color:var(--color-background);color:var(--color-foreground);margin:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}