@webship.co/workspace 0.0.0-stage → 1.0.0-beta1
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/PLUGINS.md +249 -0
- package/README.md +379 -2
- package/WORKSPACES.md +69 -0
- package/actions/ai-items.js +293 -0
- package/actions/audio.js +48 -0
- package/actions/build-order.js +79 -0
- package/actions/checks.js +95 -0
- package/actions/claude-session.js +104 -0
- package/actions/commands-center.js +258 -0
- package/actions/composer.js +267 -0
- package/actions/ddev-addons.js +90 -0
- package/actions/ddev-mail.js +80 -0
- package/actions/ddev.js +237 -0
- package/actions/folders.js +165 -0
- package/actions/images-svg.js +33 -0
- package/actions/images.js +145 -0
- package/actions/navigation.js +190 -0
- package/actions/node.js +222 -0
- package/actions/notes.js +55 -0
- package/actions/plugins.js +36 -0
- package/actions/project-make.js +28 -0
- package/actions/rag.js +202 -0
- package/actions/row-actions.js +262 -0
- package/actions/screens.js +270 -0
- package/actions/testing.js +121 -0
- package/actions/video-edit.js +54 -0
- package/ai-inventory.js +182 -0
- package/ai-items.js +202 -0
- package/assistant-cast.js +176 -0
- package/assistant-suggestions.js +91 -0
- package/assistant.js +484 -0
- package/audio-voice.js +528 -0
- package/auth-google.js +260 -0
- package/build-ai.js +52 -0
- package/build-order-page.js +191 -0
- package/build-order.js +313 -0
- package/builder-args.js +117 -0
- package/changelog.js +302 -0
- package/chat-threads.js +134 -0
- package/checks.js +105 -0
- package/claude-args.js +282 -0
- package/claude-sessions.js +129 -0
- package/command-author.js +117 -0
- package/command-block.js +20 -0
- package/commands/defaults.yml +159 -0
- package/commands-page.js +460 -0
- package/commands.js +568 -0
- package/cucumber.js +43 -0
- package/ddev-addons-page.js +124 -0
- package/ddev-addons.js +154 -0
- package/ddev-custom-commands.js +96 -0
- package/ddev-domains.js +84 -0
- package/ddev-import.js +93 -0
- package/ddev-mail-page.js +167 -0
- package/ddev-mail.js +110 -0
- package/ddev-options.js +145 -0
- package/ddev-snapshots.js +126 -0
- package/ddev.js +145 -0
- package/docs/DEVELOPER-GUIDE.md +224 -0
- package/docs/README.md +252 -0
- package/docs/REFERENCE.md +684 -0
- package/docs/USER-GUIDE.md +697 -0
- package/docs/screenshots/assistant/avatar-board.png +0 -0
- package/docs/screenshots/assistant/avatar-coffee.png +0 -0
- package/docs/screenshots/assistant/avatar-colours.png +0 -0
- package/docs/screenshots/assistant/avatar-icons.png +0 -0
- package/docs/screenshots/assistant/avatar-moves.png +0 -0
- package/docs/screenshots/assistant/avatar-pick-cookieman.png +0 -0
- package/docs/screenshots/assistant/avatar-settings-voices.png +0 -0
- package/docs/screenshots/assistant/avatar-status.png +0 -0
- package/docs/screenshots/create-card/create-card.png +0 -0
- package/docs/screenshots/create-card/start-ai.png +0 -0
- package/docs/screenshots/guide-clone-address.png +0 -0
- package/docs/screenshots/guide-clone-worktree-off.png +0 -0
- package/docs/screenshots/guide-clone-worktree-on.png +0 -0
- package/docs/screenshots/guide-ddev-menu.png +0 -0
- package/docs/screenshots/guide-domains.png +0 -0
- package/docs/screenshots/guide-home.png +0 -0
- package/docs/screenshots/guide-ide-menu.png +0 -0
- package/docs/screenshots/guide-remove-refusal.png +0 -0
- package/docs/screenshots/guide-rows-worktree.png +0 -0
- package/docs/screenshots/guide-screens.png +0 -0
- package/docs/screenshots/guide-settings.png +0 -0
- package/docs/screenshots/guide-workspace-page.png +0 -0
- package/docs/screenshots/home/card-settings.png +0 -0
- package/docs/screenshots/home/drag-reorder.png +0 -0
- package/docs/screenshots/home/edit-card.png +0 -0
- package/docs/screenshots/images/claude-code-image.png +0 -0
- package/docs/screenshots/images/edit-with-ai.png +0 -0
- package/docs/screenshots/images/gallery.png +0 -0
- package/docs/screenshots/images/generate-description.png +0 -0
- package/docs/screenshots/images/generate-plan.png +0 -0
- package/docs/screenshots/images/providers.png +0 -0
- package/docs/screenshots/images/svg-form.png +0 -0
- package/docs/screenshots/images/svg-result.png +0 -0
- package/docs/screenshots/images/upload.png +0 -0
- package/docs/screenshots/new-folder/2-form.png +0 -0
- package/docs/screenshots/new-folder/8-ddev-options.png +0 -0
- package/docs/screenshots/new-folder/files-folder.png +0 -0
- package/docs/screenshots/projects/ddev-custom-commands.png +0 -0
- package/docs/screenshots/projects/state-chip-stop.png +0 -0
- package/docs/screenshots/readme/dashboard-dark.png +0 -0
- package/docs/screenshots/readme/dashboard-light.png +0 -0
- package/docs/screenshots/readme/dev-dark.png +0 -0
- package/docs/screenshots/readme/dev-light.png +0 -0
- package/docs/screenshots/readme/docs-dark.png +0 -0
- package/docs/screenshots/readme/docs-light.png +0 -0
- package/docs/screenshots/readme/icons/ai.svg +4 -0
- package/docs/screenshots/readme/icons/code.svg +4 -0
- package/docs/screenshots/readme/icons/docs.svg +4 -0
- package/docs/screenshots/readme/icons/services.svg +4 -0
- package/docs/screenshots/readme/icons/sites.svg +4 -0
- package/docs/screenshots/readme/test-report-dark.png +0 -0
- package/docs/screenshots/readme/test-report-light.png +0 -0
- package/docs/screenshots/readme/test-scenarios.png +0 -0
- package/docs/screenshots/readme/test-steps.png +0 -0
- package/docs/screenshots/readme/videos-dark.png +0 -0
- package/docs/screenshots/readme/videos-light.png +0 -0
- package/docs/screenshots/screens-menu.png +0 -0
- package/docs/screenshots/screens-new.png +0 -0
- package/docs/screenshots/settings/auth-google-lists.png +0 -0
- package/docs/screenshots/settings/delete-1-row.png +0 -0
- package/docs/screenshots/settings/delete-2-confirm.png +0 -0
- package/docs/screenshots/settings/delete-3-type-name.png +0 -0
- package/docs/screenshots/settings/delete-4-type-yes.png +0 -0
- package/event-log.js +59 -0
- package/files-browser.js +247 -0
- package/folders.js +121 -0
- package/format.js +73 -0
- package/git-info.js +81 -0
- package/github-auth.js +62 -0
- package/html.js +10 -0
- package/icons.js +83 -0
- package/images-ai.js +447 -0
- package/images-create.js +226 -0
- package/images-page.js +417 -0
- package/images-records.js +39 -0
- package/images-render.js +36 -0
- package/images-svg-core.js +214 -0
- package/images-svg-sanitize.js +188 -0
- package/images-svg.js +241 -0
- package/images.js +270 -0
- package/jobs.js +430 -0
- package/linger.js +21 -0
- package/lists.js +292 -0
- package/media-info.js +136 -0
- package/navigation-page.js +264 -0
- package/navigation.js +394 -0
- package/notes.js +0 -0
- package/package.json +47 -4
- package/palette-index.js +46 -0
- package/playwright.config.js +23 -0
- package/plugin-install.js +239 -0
- package/plugin-loader.js +466 -0
- package/plugin-manifest.js +83 -0
- package/plugin-settings.js +44 -0
- package/plugins/workspace-notes/index.js +59 -0
- package/plugins-page.js +103 -0
- package/project-make.js +241 -0
- package/project-name.js +210 -0
- package/public/assistant-avatar-moves.js +646 -0
- package/public/assistant-avatar-ui.js +1223 -0
- package/public/assistant-avatar.js +530 -0
- package/public/avatar/druplicon.vrm +0 -0
- package/public/css/base.css +260 -0
- package/public/css/components/ai-assistant.css +263 -0
- package/public/css/components/assistant-avatar.css +279 -0
- package/public/css/components/assistant-views.css +198 -0
- package/public/css/components/build-order.css +74 -0
- package/public/css/components/clone-as-a-git-worktree.css +30 -0
- package/public/css/components/command-palette.css +61 -0
- package/public/css/components/commands-center.css +139 -0
- package/public/css/components/confirm-dialog.css +40 -0
- package/public/css/components/create-card.css +8 -0
- package/public/css/components/ddev-menu.css +23 -0
- package/public/css/components/deep-chat.css +17 -0
- package/public/css/components/density.css +11 -0
- package/public/css/components/floating-assistant-workspace-pages.css +55 -0
- package/public/css/components/folders.css +5 -0
- package/public/css/components/full-height-left-assistant-sidebar-all-pages.css +39 -0
- package/public/css/components/full-height-right-actions-rail-pages-that-have-actions.css +267 -0
- package/public/css/components/grid-and-list.css +203 -0
- package/public/css/components/help-page.css +36 -0
- package/public/css/components/hero.css +16 -0
- package/public/css/components/images.css +70 -0
- package/public/css/components/layout-rhythm-and-spacing-polish.css +139 -0
- package/public/css/components/mic-on-any-field-that-wants-typing.css +416 -0
- package/public/css/components/modal-fit.css +113 -0
- package/public/css/components/page-width.css +23 -0
- package/public/css/components/plugins-page.css +9 -0
- package/public/css/components/project-cards.css +256 -0
- package/public/css/components/project-rows.css +390 -0
- package/public/css/components/reordering-the-workspace-cards.css +85 -0
- package/public/css/components/responsive-home-and-the-workspace-pages.css +457 -0
- package/public/css/components/running-commands-as-toasts.css +327 -0
- package/public/css/components/settings-editor-core-config-yml.css +828 -0
- package/public/css/components/short-results-as-a-toast-at-the-top.css +203 -0
- package/public/css/components/sound-player.css +84 -0
- package/public/css/components/suggest-agents-skills-commands.css +112 -0
- package/public/css/components/tab-modal.css +142 -0
- package/public/css/components/terminals.css +244 -0
- package/public/css/components/test-report-media-one-file-per-row-paged.css +144 -0
- package/public/css/components/top-toolbar-logo-breadcrumb.css +107 -0
- package/public/css/components/video-edit.css +6 -0
- package/public/css/components/web-ide-open-with.css +23 -0
- package/public/css/components/working-indicators.css +158 -0
- package/public/css/components/workspace-cards.css +79 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/public/htmx.min.js +1 -0
- package/public/logo.png +0 -0
- package/public/media-player.js +215 -0
- package/public/mic.js +255 -0
- package/public/narration.js +110 -0
- package/public/palette.js +278 -0
- package/public/sortable.js +251 -0
- package/public/suggest.js +337 -0
- package/public/terminal.js +903 -0
- package/public/themes/default/theme.css +267 -0
- package/public/themes/ember/theme.css +180 -0
- package/public/themes/press/theme.css +290 -0
- package/public/themes/slate/theme.css +182 -0
- package/public/touch-icon.png +0 -0
- package/public/ui.js +2721 -0
- package/public/vendor/ace/LICENSE.txt +24 -0
- package/public/vendor/ace/ace.js +23 -0
- package/public/vendor/ace/ext-language_tools.js +8 -0
- package/public/vendor/ace/ext-searchbox.js +8 -0
- package/public/vendor/ace/mode-css.js +8 -0
- package/public/vendor/ace/mode-html.js +8 -0
- package/public/vendor/ace/mode-javascript.js +8 -0
- package/public/vendor/ace/mode-markdown.js +8 -0
- package/public/vendor/ace/mode-sh.js +8 -0
- package/public/vendor/ace/mode-yaml.js +8 -0
- package/public/vendor/ace/theme-textmate.js +8 -0
- package/public/vendor/ace/theme-tomorrow_night.js +8 -0
- package/public/vendor/ace/worker-yaml.js +1 -0
- package/public/vendor/deep-chat.LICENSE +21 -0
- package/public/vendor/deep-chat.bundle.js +1 -0
- package/public/vendor/fonts/dm-serif-text-LICENSE.txt +93 -0
- package/public/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
- package/public/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
- package/public/vendor/fonts/figtree-LICENSE.txt +93 -0
- package/public/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
- package/public/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
- package/public/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
- package/public/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
- package/public/vendor/fonts/jetbrains-mono-LICENSE.txt +93 -0
- package/public/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/public/vendor/fonts/ubuntu-mono-LICENSE.txt +98 -0
- package/public/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
- package/public/vendor/tabler-LICENSE.txt +21 -0
- package/public/vendor/tabler-sprite.svg +1 -0
- package/public/vendor/three/LICENSE +21 -0
- package/public/vendor/three/addons/loaders/GLTFLoader.js +4860 -0
- package/public/vendor/three/addons/utils/BufferGeometryUtils.js +1501 -0
- package/public/vendor/three/addons/utils/SkeletonUtils.js +496 -0
- package/public/vendor/three/three.core.min.js +6 -0
- package/public/vendor/three/three.module.min.js +6 -0
- package/public/vendor/three-vrm/LICENSE +22 -0
- package/public/vendor/three-vrm/three-vrm.module.min.js +983 -0
- package/public/vendor/uikit-icons.min.js +1 -0
- package/public/vendor/uikit.min.css +1 -0
- package/public/vendor/uikit.min.js +1 -0
- package/public/vendor/webship-icons/ui/bookmark-outlined.svg +1 -0
- package/public/vendor/webship-icons/ui/headset.svg +1 -0
- package/public/vendor/webship-icons/ui/menu.svg +1 -0
- package/public/vendor/webship-icons/ui/pause.svg +1 -0
- package/public/vendor/webship-icons/ui/play.svg +1 -0
- package/public/vendor/webship-icons/ui/share.svg +1 -0
- package/public/vendor/webship-icons/ui/sound-off.svg +1 -0
- package/public/vendor/webship-icons/ui/sound-on.svg +1 -0
- package/public/vendor/webship-icons/ui/stop.svg +1 -0
- package/public/vendor/xterm/addon-fit.js +2 -0
- package/public/vendor/xterm/addon-unicode11.js +2 -0
- package/public/vendor/xterm/addon-web-links.js +2 -0
- package/public/vendor/xterm/xterm.css +218 -0
- package/public/vendor/xterm/xterm.js +2 -0
- package/rag.js +169 -0
- package/reader.js +132 -0
- package/repo-blobs.js +66 -0
- package/report-pdf.js +337 -0
- package/row-layout.js +1439 -0
- package/screenrc +37 -0
- package/screens.js +1063 -0
- package/scripts/code-server-start.sh +190 -0
- package/scripts/images-claude-finish.js +107 -0
- package/scripts/screen-host.sh +114 -0
- package/scripts/screen-runner.js +362 -0
- package/scripts/screen-then.js +47 -0
- package/scripts/update.sh +350 -0
- package/server.js +7923 -0
- package/settings-file.js +423 -0
- package/speech.js +297 -0
- package/tab-modal.js +75 -0
- package/test-reports.js +260 -0
- package/themes.js +381 -0
- package/transcripts.js +168 -0
- package/urls.js +13 -0
- package/validate.js +18 -0
- package/version-check.js +94 -0
- package/video-edit.js +588 -0
- package/waiting.js +85 -0
- package/workspace-card-tools.js +41 -0
- package/workspace-delete.js +139 -0
- package/workspace-types.js +85 -0
- package/workspaces.js +553 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/* The project card (.pcard): one anatomy, laid out by the card's own width, so it reads the same with
|
|
2
|
+
the actions rail open or collapsed, the assistant closed, beside or wide, and in a grid of two or three.
|
|
3
|
+
Every tool stays on the card and reachable: when a card is narrow the strip of icons wraps. */
|
|
4
|
+
|
|
5
|
+
/* ---------- Project card (.pcard): one anatomy, chosen by the card's own width ---------- */
|
|
6
|
+
|
|
7
|
+
#webship-workspace-projects {
|
|
8
|
+
--pcard-min: 17rem;
|
|
9
|
+
--pcard-gap: 0.75rem;
|
|
10
|
+
--pcard-pad: 1rem;
|
|
11
|
+
--pcard-radius: 0.75rem;
|
|
12
|
+
--pcard-tool: 2rem;
|
|
13
|
+
--pcard-chip: 1.5rem;
|
|
14
|
+
--pcard-title-lines: 3;
|
|
15
|
+
--pcard-cols: 1;
|
|
16
|
+
}
|
|
17
|
+
html[data-rows-view="grid-2"] #webship-workspace-projects { --pcard-cols: 2; }
|
|
18
|
+
html[data-rows-view="grid-3"] #webship-workspace-projects { --pcard-cols: 3; }
|
|
19
|
+
html[data-density="compact"] #webship-workspace-projects { --pcard-pad: 0.75rem; --pcard-tool: 1.75rem; }
|
|
20
|
+
|
|
21
|
+
html[data-rows-view^="grid"] #webship-workspace-projects {
|
|
22
|
+
display: grid;
|
|
23
|
+
gap: var(--pcard-gap);
|
|
24
|
+
align-items: stretch;
|
|
25
|
+
grid-template-columns: repeat(auto-fill, minmax(
|
|
26
|
+
max(min(100%, var(--pcard-min)), calc((100% - (var(--pcard-cols) - 1) * var(--pcard-gap)) / var(--pcard-cols))), 1fr));
|
|
27
|
+
}
|
|
28
|
+
html[data-rows-view="grid-1"] #webship-workspace-projects .project-row { max-width: 46rem; margin-inline: auto; width: 100%; }
|
|
29
|
+
|
|
30
|
+
/* the card */
|
|
31
|
+
.project-row.pcard {
|
|
32
|
+
--pcard-stripe: var(--line);
|
|
33
|
+
container: pcard / inline-size;
|
|
34
|
+
position: relative;
|
|
35
|
+
min-width: 0;
|
|
36
|
+
margin: 0;
|
|
37
|
+
padding: 0;
|
|
38
|
+
background: var(--surface-card);
|
|
39
|
+
border: 1px solid var(--line);
|
|
40
|
+
border-radius: var(--pcard-radius);
|
|
41
|
+
box-shadow: inset 0.25rem 0 0 var(--pcard-stripe);
|
|
42
|
+
}
|
|
43
|
+
.project-row.pcard:has(.state-chip:not(.state-chip-off)) { --pcard-stripe: var(--ok); }
|
|
44
|
+
.project-row.pcard:has(.state-chip-paused) { --pcard-stripe: var(--warning); }
|
|
45
|
+
.project-row.pcard:has(.agent-live) { --pcard-stripe: var(--brand-primary); }
|
|
46
|
+
.project-row.pcard:has(.agent-live.uk-label-warning) { --pcard-stripe: var(--warning); }
|
|
47
|
+
/* a container is a stacking context: keep an open menu above the next card */
|
|
48
|
+
.project-row.pcard:hover,
|
|
49
|
+
.project-row.pcard:focus-within,
|
|
50
|
+
.project-row.pcard:has(.uk-open) { z-index: 3; }
|
|
51
|
+
|
|
52
|
+
.pcard-body {
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
gap: 0.5rem;
|
|
57
|
+
height: 100%;
|
|
58
|
+
min-width: 0;
|
|
59
|
+
padding: var(--pcard-pad) var(--pcard-pad) var(--pcard-pad) calc(var(--pcard-pad) + 0.25rem);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* 1. name and state */
|
|
63
|
+
.pcard-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem; align-items: start; }
|
|
64
|
+
.pcard-title {
|
|
65
|
+
margin: 0;
|
|
66
|
+
min-width: 0;
|
|
67
|
+
font: 600 1rem / 1.3 var(--font-primary);
|
|
68
|
+
color: var(--text-default);
|
|
69
|
+
text-wrap: balance;
|
|
70
|
+
overflow-wrap: break-word;
|
|
71
|
+
hyphens: manual;
|
|
72
|
+
display: -webkit-box;
|
|
73
|
+
-webkit-box-orient: vertical;
|
|
74
|
+
-webkit-line-clamp: var(--pcard-title-lines);
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
}
|
|
77
|
+
.pcard-title[title] { cursor: help; }
|
|
78
|
+
.pcard-title .pcard-code { color: var(--text-subtle); font-weight: 500; }
|
|
79
|
+
.pcard-status { display: flex; flex-direction: column; align-items: flex-end; gap: 0.25rem; margin: 0; }
|
|
80
|
+
.pcard-status .project-title-agent:empty { display: none; }
|
|
81
|
+
.pcard-status .uk-label {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: 0.375rem;
|
|
85
|
+
height: var(--pcard-chip);
|
|
86
|
+
padding: 0 0.625rem;
|
|
87
|
+
border-radius: 62.4375rem;
|
|
88
|
+
font-size: 0.75rem;
|
|
89
|
+
line-height: 1;
|
|
90
|
+
white-space: nowrap;
|
|
91
|
+
text-transform: none;
|
|
92
|
+
letter-spacing: 0;
|
|
93
|
+
}
|
|
94
|
+
.pcard-status .state-chip.uk-label-success { background: var(--ok-soft); color: var(--ok-ink); }
|
|
95
|
+
.pcard-status .state-chip .state-dot { background: var(--ok); }
|
|
96
|
+
.pcard-status .state-chip-off .state-dot { background: transparent; box-shadow: inset 0 0 0 0.125rem var(--text-subtle); }
|
|
97
|
+
.pcard-status .state-chip-paused { background: var(--warning-soft); color: var(--warning-ink); border-color: transparent; }
|
|
98
|
+
.pcard-status .state-chip-paused .state-dot { background: var(--warning); box-shadow: none; border-radius: 0.125rem; }
|
|
99
|
+
/* The chip is the switch: hovering shows what a click does. */
|
|
100
|
+
.pcard-status .state-toggle { display: inline-grid; grid-template-columns: auto auto; border: 1px solid transparent; cursor: pointer; font: inherit; font-size: 0.75rem; }
|
|
101
|
+
/* Word and action share one cell, so the chip keeps its width under the pointer. */
|
|
102
|
+
.pcard-status .state-toggle :is(.state-word, .state-act) { grid-area: 1 / 2; }
|
|
103
|
+
.pcard-status .state-toggle.state-chip-stopped { border-color: var(--line); }
|
|
104
|
+
.pcard-status .state-toggle .state-act { visibility: hidden; }
|
|
105
|
+
.pcard-status .state-toggle:is(:hover, :focus-visible) .state-word { visibility: hidden; }
|
|
106
|
+
.pcard-status .state-toggle:is(:hover, :focus-visible) .state-act { visibility: visible; }
|
|
107
|
+
.pcard-status .state-toggle:is(:hover, :focus-visible) { border-color: currentColor; }
|
|
108
|
+
.pcard-status .state-toggle:focus-visible { outline: 2px solid var(--subtle-brand); outline-offset: 1px; }
|
|
109
|
+
.pcard-status .agent-live { background: var(--subtle-brand); color: var(--link); }
|
|
110
|
+
.pcard-status .agent-live.uk-label-warning { background: var(--warning); color: var(--brand-dark); }
|
|
111
|
+
|
|
112
|
+
/* 2. facts */
|
|
113
|
+
.pcard-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; min-width: 0; min-height: var(--pcard-chip); }
|
|
114
|
+
.pcard-meta .pcard-chip,
|
|
115
|
+
.pcard-meta .proj-alias,
|
|
116
|
+
.pcard-meta > .uk-label {
|
|
117
|
+
display: inline-flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
gap: 0.25rem;
|
|
120
|
+
box-sizing: border-box;
|
|
121
|
+
max-width: 100%;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
height: var(--pcard-chip);
|
|
124
|
+
margin: 0;
|
|
125
|
+
padding: 0 0.5rem;
|
|
126
|
+
border-radius: 0.375rem;
|
|
127
|
+
font-size: 0.75rem;
|
|
128
|
+
line-height: 1;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
overflow: hidden;
|
|
131
|
+
text-overflow: ellipsis;
|
|
132
|
+
text-transform: none;
|
|
133
|
+
}
|
|
134
|
+
.pcard-meta .uk-icon { flex: none; }
|
|
135
|
+
.pcard-meta .pcard-key { background: var(--subtle-brand); color: var(--link); font-family: var(--font-code); font-weight: 600; text-decoration: none; }
|
|
136
|
+
.pcard-meta .proj-alias { color: var(--text-subtle) !important; } /* an earlier component paints every alias link-blue with !important */
|
|
137
|
+
.pcard-meta > .uk-label-success { background: var(--ok-soft); color: var(--ok-ink); }
|
|
138
|
+
.pcard-chip-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
139
|
+
.pcard-meta .pcard-slug { font: 0.7rem / 1 var(--font-code); color: var(--text-subtle); background: var(--surface); }
|
|
140
|
+
.pcard-meta .project-note-corner { margin-inline-start: auto; gap: 0.25rem; }
|
|
141
|
+
.pcard-meta .project-note { inline-size: 1.5rem; block-size: 1.5rem; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
|
|
142
|
+
|
|
143
|
+
/* 3. addresses */
|
|
144
|
+
.pcard-addr { display: flex; align-items: center; gap: 0.375rem; min-width: 0; font: 0.75rem / 1.2 var(--font-code); }
|
|
145
|
+
.pcard-host { flex: 1 1 auto; display: flex; min-width: 0; overflow: hidden; color: var(--link) !important; }
|
|
146
|
+
.pcard-host-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
147
|
+
.pcard-host-tld { flex: none; }
|
|
148
|
+
.pcard-addr.is-stopped .pcard-host { color: var(--text-subtle) !important; }
|
|
149
|
+
.pcard-count {
|
|
150
|
+
flex: none;
|
|
151
|
+
box-sizing: border-box;
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
height: var(--pcard-chip);
|
|
156
|
+
min-width: 2rem;
|
|
157
|
+
padding: 0 0.5rem;
|
|
158
|
+
border: 1px solid var(--line);
|
|
159
|
+
border-radius: 62.4375rem;
|
|
160
|
+
background: transparent;
|
|
161
|
+
color: var(--link);
|
|
162
|
+
font: 600 0.75rem / 1 var(--font-primary);
|
|
163
|
+
white-space: nowrap;
|
|
164
|
+
}
|
|
165
|
+
.pcard-count:hover { background: var(--subtle-brand); }
|
|
166
|
+
.pcard-addr.is-stopped .pcard-count { color: var(--text-subtle); }
|
|
167
|
+
.pcard-count[data-clash]::after { content: ""; width: 0.5rem; height: 0.5rem; margin-inline-start: 0.25rem; border-radius: 50%; background: var(--warning); }
|
|
168
|
+
.pcard-domains { min-width: 16rem; max-width: min(24rem, 92vw); padding: 0.25rem; }
|
|
169
|
+
.pcard-domains ul { max-height: 15rem; overflow: auto; margin: 0; padding: 0; list-style: none; }
|
|
170
|
+
.pcard-domain {
|
|
171
|
+
display: flex;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 0.5rem;
|
|
174
|
+
min-height: 2rem;
|
|
175
|
+
padding: 0.25rem 0.5rem;
|
|
176
|
+
border-radius: 0.375rem;
|
|
177
|
+
font: 0.75rem / 1.2 var(--font-code);
|
|
178
|
+
white-space: nowrap;
|
|
179
|
+
}
|
|
180
|
+
.pcard-domain > .host { overflow: hidden; text-overflow: ellipsis; }
|
|
181
|
+
.pcard-domain.is-clash { color: var(--warning-ink); cursor: help; }
|
|
182
|
+
.pcard-domain.is-off { color: var(--text-subtle); }
|
|
183
|
+
|
|
184
|
+
/* 4. the bar */
|
|
185
|
+
.pcard-bar { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-top: auto; padding-top: 0.5rem; border-top: 1px solid var(--line); min-width: 0; }
|
|
186
|
+
.pcard-bar .pcard-launch { flex: none; margin: 0; }
|
|
187
|
+
.pcard-tools { flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 0.125rem 0.25rem; margin-inline-start: 0; min-width: 0; }
|
|
188
|
+
.pcard-tools .act-with-status { display: inline-flex; flex-direction: row; position: relative; }
|
|
189
|
+
.pcard-tools .act-menu { margin: 0; }
|
|
190
|
+
.pcard-tools .uk-button {
|
|
191
|
+
box-sizing: border-box;
|
|
192
|
+
inline-size: auto;
|
|
193
|
+
min-inline-size: var(--pcard-tool);
|
|
194
|
+
block-size: var(--pcard-tool);
|
|
195
|
+
min-height: 0;
|
|
196
|
+
padding: 0 calc(var(--pcard-tool-label, 0rem) * 0.67);
|
|
197
|
+
display: inline-flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
justify-content: center;
|
|
200
|
+
gap: calc(var(--pcard-tool-label, 0rem) * 0.42);
|
|
201
|
+
font-size: var(--pcard-tool-label, 0rem); /* 0 draws the icon alone, and the word is still the button's name */
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
letter-spacing: 0;
|
|
204
|
+
text-transform: none;
|
|
205
|
+
line-height: 1;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
border: 0;
|
|
208
|
+
border-radius: 0.5rem;
|
|
209
|
+
background: transparent;
|
|
210
|
+
color: var(--text-default);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.pcard-tools .uk-button:hover { background: var(--subtle-brand); color: var(--link); }
|
|
214
|
+
.pcard-tools .uk-button [uk-icon*="chevron-down"] { display: none; }
|
|
215
|
+
.pcard-tools .uk-button .uk-icon, .pcard-tools .uk-button .tabler-icon { flex: none; }
|
|
216
|
+
.pcard-tools .uk-badge { position: absolute; inset-block-start: 0; inset-inline-end: 0; min-width: 1rem; height: 1rem; font-size: 0.625rem; line-height: 1rem; }
|
|
217
|
+
.pcard-tools .screens-hand { font-size: 0.75rem; }
|
|
218
|
+
/* captions are tooltips only (ui.js syncRowCaptions copies each into its button's title) */
|
|
219
|
+
.pcard .act-with-status > .act-status {
|
|
220
|
+
position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
|
|
221
|
+
clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
/* A wide card is the same card, wider: the words need the room a one-row layout would take. */
|
|
226
|
+
|
|
227
|
+
/* focus: the same ring the rest of the dashboard uses */
|
|
228
|
+
.pcard :is(a, button):focus-visible { outline: 0; box-shadow: 0 0 0 0.1875rem var(--focus-ring); }
|
|
229
|
+
|
|
230
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
231
|
+
.pcard-tools .uk-button, .pcard-count { transition: background-color 0.12s ease, color 0.12s ease; }
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* The list of addresses is a menu, so it keeps the interface type: the address column's code type is for
|
|
235
|
+
the addresses themselves. */
|
|
236
|
+
.pcard-domains .uk-nav-header { font-family: var(--font-primary); font-size: 0.75rem; text-transform: none; letter-spacing: 0; }
|
|
237
|
+
|
|
238
|
+
/* Words or icons on the tool buttons: a style sets the default (--pcard-tool-label in the theme), and the
|
|
239
|
+
switch beside the layout controls is that browser's own answer, kept like the density. */
|
|
240
|
+
html[data-tool-labels="words"] #webship-workspace-projects { --pcard-tool-label: 0.75rem; }
|
|
241
|
+
html[data-tool-labels="icons"] #webship-workspace-projects { --pcard-tool-label: 0rem; }
|
|
242
|
+
|
|
243
|
+
/* SCREENS sits beside the running state: what is running on the site and in its sessions, read together. */
|
|
244
|
+
.pcard-state-line { display: inline-flex; align-items: center; gap: 0.375rem; }
|
|
245
|
+
.pcard-screens .act-with-status { display: inline-flex; }
|
|
246
|
+
.pcard-screens .act-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
247
|
+
.pcard-screens .uk-button {
|
|
248
|
+
height: var(--pcard-chip); min-height: 0; padding: 0 0.5rem; display: inline-flex; align-items: center; gap: 0.25rem;
|
|
249
|
+
font-size: 0.75rem; line-height: 1; border-radius: 62.4375rem; text-transform: none; letter-spacing: 0;
|
|
250
|
+
}
|
|
251
|
+
.pcard-screens .uk-button [uk-icon*="chevron"] { display: none; }
|
|
252
|
+
.pcard-screens .uk-badge { min-width: 1rem; height: 1rem; font-size: 0.625rem; line-height: 1rem; }
|
|
253
|
+
/* In the head it is the icon and its count, so the name keeps its room; the word is still its name. */
|
|
254
|
+
.pcard-screens .uk-button { font-size: 0; gap: 0.2rem; padding: 0 0.4rem; }
|
|
255
|
+
.pcard-screens .uk-button .uk-badge, .pcard-screens .uk-button .screens-hand { font-size: 0.625rem; }
|
|
256
|
+
.pcard-screens .uk-button .screens-hand { font-size: 0.75rem; }
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/* ---------- Project rows ---------- */
|
|
2
|
+
|
|
3
|
+
.project-actions {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-wrap: wrap;
|
|
6
|
+
gap:0.375rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* Two-step remove arming */
|
|
10
|
+
.arm-step.arm { background: var(--danger-armed) !important; color: var(--text-inverse) !important; }
|
|
11
|
+
|
|
12
|
+
/* HTMX request-in-flight indicator */
|
|
13
|
+
.htmx-request { opacity: .6; pointer-events: none; }
|
|
14
|
+
|
|
15
|
+
/* A job toast polls itself every second, so this faded it once a second for the whole run — the
|
|
16
|
+
toast looked half-transparent and its buttons went dead between polls. A poll is not a click
|
|
17
|
+
the user is waiting on, so it gets no in-flight styling. Same for the stack it lives in and the
|
|
18
|
+
flash toasts, which must stay solid and clickable while anything else is loading. */
|
|
19
|
+
.job-toast.htmx-request,
|
|
20
|
+
.job-toasts.htmx-request,
|
|
21
|
+
.flash-toast.htmx-request {
|
|
22
|
+
opacity: 1;
|
|
23
|
+
pointer-events: auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* The checks a project declares, listed inside the Tests menu.
|
|
27
|
+
*
|
|
28
|
+
* The pair is the point: the canonical name on the left is what you type (`--only js`), the code on
|
|
29
|
+
* the right is the script the package itself declared for it. Seeing them together is how you know
|
|
30
|
+
* the mapping is right, so they are laid out as two columns rather than run together as a sentence. */
|
|
31
|
+
.checks-sub .checks-alias {
|
|
32
|
+
display: inline-block;
|
|
33
|
+
min-width: 4.5rem;
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.checks-sub .checks-script {
|
|
38
|
+
font-family: var(--font-code);
|
|
39
|
+
font-size: 0.72rem;
|
|
40
|
+
background: transparent;
|
|
41
|
+
color: var(--text-subtle);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* A check that cannot run is listed, not hidden — a gap you can see is a gap you can fix. It is not
|
|
45
|
+
a link, so it takes the row's padding from here rather than from UIKit's anchor rule. */
|
|
46
|
+
.checks-sub .checks-alias-off {
|
|
47
|
+
display: block;
|
|
48
|
+
padding: 0.25rem 0;
|
|
49
|
+
opacity: .8;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* A button drawn as its icon alone, or as its label alone.
|
|
53
|
+
Both halves stay in the markup — a screen reader still reads the name and the title still answers a
|
|
54
|
+
hover — so this is presentation only.
|
|
55
|
+
|
|
56
|
+
A row button's label is a bare TEXT NODE, not a span, so no selector can reach it: `SCREENS` sits
|
|
57
|
+
between the icon and the badge with nothing around it. Collapsing the button's font-size collapses
|
|
58
|
+
the text node and nothing else, and the children that are real elements get their size back. It does
|
|
59
|
+
NOT take the text out of the accessibility tree, which is the whole reason to do it this way rather
|
|
60
|
+
than wrapping ten buttons' labels in spans and hoping the eleventh gets one too. */
|
|
61
|
+
.row-show-icon .uk-button { font-size: 0; gap: 0; }
|
|
62
|
+
.row-show-icon .uk-button > *,
|
|
63
|
+
.row-show-icon .uk-badge { font-size: 0.8125rem; }
|
|
64
|
+
.row-show-icon .uk-badge { font-size: 0.625rem; }
|
|
65
|
+
.row-show-text [uk-icon],
|
|
66
|
+
.row-show-text .uk-icon,
|
|
67
|
+
.row-show-text .tabler-icon { display: none; }
|
|
68
|
+
/* The picker on a button card, beside the row it sits in. */
|
|
69
|
+
.row-show { max-width: 8rem; }
|
|
70
|
+
|
|
71
|
+
/* The state of a project, all of it at the right-hand end of its name line: what is WORKING on it,
|
|
72
|
+
then what DDEV says about it. One glance answers both — "is anything happening here, and is the
|
|
73
|
+
site up" — where splitting them to opposite ends made the eye cross the name to collect one. */
|
|
74
|
+
.project-title-status {
|
|
75
|
+
margin-left: auto;
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: .35rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* An agent that has stopped to ASK something raises its hand, on the row, with nothing opened: the
|
|
82
|
+
whole point of a detached session is that nobody is watching it, so a question nobody sees is a run
|
|
83
|
+
that is not happening. It pulses rather than just colouring — an amber label among labels is easy
|
|
84
|
+
to scan past on a page of twenty rows. */
|
|
85
|
+
.screens-hand.is-waiting,
|
|
86
|
+
.agent-live[title*="ask you"],
|
|
87
|
+
.screens-waiting { animation: hand-wave 1.6s ease-in-out infinite; }
|
|
88
|
+
|
|
89
|
+
@keyframes hand-wave {
|
|
90
|
+
0%, 100% { opacity: 1; }
|
|
91
|
+
50% { opacity: .55; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.screens-hand.is-waiting { margin-left: .25rem; }
|
|
95
|
+
|
|
96
|
+
/* Somebody who does not want the movement keeps the colour, which carries the same message. */
|
|
97
|
+
@media (prefers-reduced-motion: reduce) {
|
|
98
|
+
.screens-hand.is-waiting,
|
|
99
|
+
.agent-live[title*="ask you"],
|
|
100
|
+
.screens-waiting { animation: none; }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* The agent badge gets its own colour, and not the green the DDEV state uses: two green pills side by
|
|
104
|
+
side say one thing twice, and they answer different questions — green is "the site is up", brand
|
|
105
|
+
blue is "something is working on it". Two classes, so it wins over uk-label-success without
|
|
106
|
+
!important. Amber still takes over when it has stopped to ask you something: that is the one state
|
|
107
|
+
worth shouting, and it must not be drowned out by a colour it shares with the ordinary case. */
|
|
108
|
+
.project-title-status .agent-live {
|
|
109
|
+
background: var(--brand-primary);
|
|
110
|
+
color: var(--text-inverse);
|
|
111
|
+
}
|
|
112
|
+
.project-title-status .agent-live.uk-label-warning {
|
|
113
|
+
background: var(--warning);
|
|
114
|
+
color: var(--brand-dark); /* the same pair base.css gives every warning label */
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* The robot takes the label's own colour and sits on its text baseline: an icon that keeps its
|
|
118
|
+
stroke colour would be brand blue on a green label, and one aligned to the box rides high. */
|
|
119
|
+
.agent-live .tabler-icon {
|
|
120
|
+
color: currentColor;
|
|
121
|
+
vertical-align: -0.15em;
|
|
122
|
+
margin-right: .15rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* The note on a row: its icon is quiet until there is something to read, and the folder's own name
|
|
126
|
+
sits beside a custom one in code type — it is what `cd` and every command here take, so it is data
|
|
127
|
+
rather than a label. */
|
|
128
|
+
/* The card's bottom-right cluster: the workitem an agent here is working, then the pen for the note.
|
|
129
|
+
It is the LAST item of the last row of controls, pushed right — not a floating corner. A floating one
|
|
130
|
+
has to be paid for with reserved padding on every action row, and that padding pushed `More` onto a
|
|
131
|
+
line of its own while the row still had room for it. Pushed right it costs nothing until the row is
|
|
132
|
+
genuinely full, and then it wraps like every other control. */
|
|
133
|
+
.project-note-corner { margin-left: auto; display: inline-flex; align-items: center; gap: .5rem; }
|
|
134
|
+
.project-plugin-badges:empty { display: none; }
|
|
135
|
+
.project-plugin-badges { display: inline-flex; align-items: center; gap: .4rem; }
|
|
136
|
+
|
|
137
|
+
/* Quiet until there is something to read is the TOKEN's job, not an opacity: fading a colour that was
|
|
138
|
+
checked for contrast is how every contrast failure here started, and tests/unit/accessibility.test.js
|
|
139
|
+
refuses it. --text-subtle is the checked quiet ink; the brand blue says a note is waiting. */
|
|
140
|
+
.project-note-corner .project-note { padding: .15rem .3rem; line-height: 1; color: var(--text-subtle); }
|
|
141
|
+
.project-row:hover .project-note-corner .project-note,
|
|
142
|
+
.project-note-corner .project-note:focus-visible { color: var(--link); }
|
|
143
|
+
.project-note-corner .project-note.has-note { color: var(--link); }
|
|
144
|
+
.project-real-name { font-size: .7rem; color: var(--text-subtle); }
|
|
145
|
+
.project-title-row .project-title[title] { cursor: help; }
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
/* ---------- Browsing a folder of files (docs, designs) ---------- */
|
|
149
|
+
|
|
150
|
+
/* Where you are in the tree, and the way back out. Above the controls, because it says what the list
|
|
151
|
+
IS: searching inside one folder and searching the workspace are different questions. */
|
|
152
|
+
.folder-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin-bottom: .5rem; font-size: .85rem; }
|
|
153
|
+
.folder-crumbs .crumb { color: var(--link); text-decoration: none; }
|
|
154
|
+
.folder-crumbs .crumb:hover { text-decoration: underline; }
|
|
155
|
+
.folder-crumbs .crumb.is-here { color: var(--text-default); font-weight: 600; }
|
|
156
|
+
.folder-crumbs .crumb-sep { color: var(--text-subtle); }
|
|
157
|
+
|
|
158
|
+
.fx-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
|
|
159
|
+
.fx-summary { margin: .15rem 0 0; }
|
|
160
|
+
|
|
161
|
+
/* THE LIST.
|
|
162
|
+
*
|
|
163
|
+
* A folder is read down its left edge — you are looking for a name — so the names start at one x and
|
|
164
|
+
* the size and date line up in columns of their own. Thirty cards of different heights, each with its
|
|
165
|
+
* name wrapped around a row of buttons, is the shape this had and the reason nothing could be found in
|
|
166
|
+
* it. One card around the whole list, hairlines between the rows.
|
|
167
|
+
*
|
|
168
|
+
* It is ONE grid cell when the page itself is in a grid (`grid-column: 1 / -1`): a list wrapper that
|
|
169
|
+
* became a cell is how the screens list ended up squashed into the right-hand half. */
|
|
170
|
+
.file-list {
|
|
171
|
+
grid-column: 1 / -1;
|
|
172
|
+
border: 1px solid var(--line);
|
|
173
|
+
border-radius: .6rem;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
background: var(--surface);
|
|
176
|
+
}
|
|
177
|
+
.file-list .fx-row + .fx-row { border-top: 1px solid var(--line); }
|
|
178
|
+
|
|
179
|
+
.fx-row {
|
|
180
|
+
/* border-box, or the declared min-height is the CONTENT height and every row is 16px taller than
|
|
181
|
+
it says — the same trap that grew the terminal windows by 18px on every drag. */
|
|
182
|
+
box-sizing: border-box;
|
|
183
|
+
display: grid;
|
|
184
|
+
grid-template-columns: 1.6rem minmax(0, 1fr) 6rem 12.5rem auto;
|
|
185
|
+
align-items: center;
|
|
186
|
+
gap: .75rem;
|
|
187
|
+
padding: .35rem .85rem;
|
|
188
|
+
min-height: 2.6rem;
|
|
189
|
+
color: inherit;
|
|
190
|
+
text-decoration: none;
|
|
191
|
+
}
|
|
192
|
+
.file-list, .file-list * { box-sizing: border-box; }
|
|
193
|
+
.fx-row:hover { background: var(--surface-muted); }
|
|
194
|
+
.fx-icon { display: inline-flex; color: var(--text-subtle); }
|
|
195
|
+
.fx-folder .fx-icon { color: var(--brand-primary); }
|
|
196
|
+
.fx-name {
|
|
197
|
+
font-weight: 600; min-width: 0;
|
|
198
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
199
|
+
}
|
|
200
|
+
.fx-folder .fx-name { color: var(--link); }
|
|
201
|
+
.fx-formats { margin-left: .5rem; font-weight: 400; font-size: .68rem; color: var(--text-subtle); letter-spacing: .03em; }
|
|
202
|
+
.fx-size, .fx-date { font-size: .78rem; color: var(--text-subtle); white-space: nowrap; }
|
|
203
|
+
.fx-size { text-align: right; }
|
|
204
|
+
.fx-actions { display: inline-flex; align-items: center; gap: .3rem; justify-self: end; }
|
|
205
|
+
.fx-go { color: var(--text-subtle); transition: transform .12s ease; display: inline-flex; }
|
|
206
|
+
.fx-folder:hover .fx-go { transform: translateX(3px); }
|
|
207
|
+
.fx-folder:hover .fx-name { text-decoration: underline; }
|
|
208
|
+
|
|
209
|
+
/* One control per format, and it is the one that OPENS the file. Everything else — the other formats,
|
|
210
|
+
a new tab, and the delete — is behind the row's own menu, so a folder of thirty files reads as
|
|
211
|
+
thirty names rather than as a wall of buttons. */
|
|
212
|
+
.file-list .fx-open { padding: 0 .6rem; height: 26px; line-height: 24px; font-size: .75rem; }
|
|
213
|
+
.file-list .fx-more { padding: 0 .25rem; color: var(--text-subtle); line-height: 1; }
|
|
214
|
+
.file-list .fx-more:hover { color: var(--link); }
|
|
215
|
+
|
|
216
|
+
/* A destructive item inside the menu keeps the danger ink, not a danger BUTTON: it is one line in a
|
|
217
|
+
list of lines, and it still goes through the confirm dialog before anything is deleted. */
|
|
218
|
+
.file-list .uk-dropdown-nav .uk-text-danger { color: var(--danger); }
|
|
219
|
+
|
|
220
|
+
/* THE GRID, one to six across. A folder of screenshots is a contact sheet, and a list of names is the
|
|
221
|
+
wrong shape for it — so the same rows become cards, with the columns stacked. */
|
|
222
|
+
html[data-files-view^="grid"] .file-list {
|
|
223
|
+
display: grid;
|
|
224
|
+
gap: .6rem;
|
|
225
|
+
border: 0;
|
|
226
|
+
background: none;
|
|
227
|
+
overflow: visible;
|
|
228
|
+
}
|
|
229
|
+
html[data-files-view="grid-1"] .file-list { grid-template-columns: 1fr; }
|
|
230
|
+
html[data-files-view="grid-2"] .file-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
231
|
+
html[data-files-view="grid-3"] .file-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
232
|
+
html[data-files-view="grid-4"] .file-list { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
233
|
+
html[data-files-view="grid-5"] .file-list { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
|
234
|
+
html[data-files-view="grid-6"] .file-list { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
|
235
|
+
html[data-files-view^="grid"] .file-list .fx-row {
|
|
236
|
+
grid-template-columns: 1.6rem minmax(0, 1fr);
|
|
237
|
+
grid-template-areas: "thumb thumb" "icon name" "meta meta" "actions actions";
|
|
238
|
+
align-content: start;
|
|
239
|
+
gap: .35rem .6rem;
|
|
240
|
+
border: 1px solid var(--line);
|
|
241
|
+
border-radius: .6rem;
|
|
242
|
+
background: var(--surface);
|
|
243
|
+
padding: .7rem .8rem;
|
|
244
|
+
}
|
|
245
|
+
html[data-files-view^="grid"] .file-list .fx-row + .fx-row { border-top: 1px solid var(--line); }
|
|
246
|
+
html[data-files-view^="grid"] .fx-icon { grid-area: icon; }
|
|
247
|
+
html[data-files-view^="grid"] .fx-name { grid-area: name; white-space: normal; overflow-wrap: anywhere; }
|
|
248
|
+
html[data-files-view^="grid"] .fx-size { grid-area: meta; text-align: left; }
|
|
249
|
+
/* Two facts on one line in a card: the date is the second half of the same meta line, not a column. */
|
|
250
|
+
html[data-files-view^="grid"] .fx-date { grid-area: meta; justify-self: end; }
|
|
251
|
+
html[data-files-view^="grid"] .fx-actions { grid-area: actions; justify-self: start; margin-top: .2rem; }
|
|
252
|
+
/* The chevron is what tells a LIST row that it opens something. A card is already one target with a
|
|
253
|
+
heading in link ink, so on a card it is a mark on a line of its own saying nothing. */
|
|
254
|
+
html[data-files-view^="grid"] .fx-folder .fx-actions { display: none; }
|
|
255
|
+
/* A card is as tall as what is in it. Stretched to match its neighbours, a folder card beside a row
|
|
256
|
+
of thumbnails becomes a tall empty box: the row's height is set by the picture and a folder has
|
|
257
|
+
nothing to fill it with. */
|
|
258
|
+
html[data-files-view^="grid"] .file-list { align-items: start; }
|
|
259
|
+
|
|
260
|
+
/* The picture itself, in the grid views only: in a list a row is one line of text, and a thumbnail
|
|
261
|
+
there would set the row height for every file whether or not it has one. A fixed 4:3 box so a
|
|
262
|
+
column of cards does not jump about as portrait and landscape shots load. */
|
|
263
|
+
.fx-thumb { display: none; }
|
|
264
|
+
html[data-files-view^="grid"] .fx-thumb {
|
|
265
|
+
display: block; grid-area: thumb; margin-bottom: .2rem;
|
|
266
|
+
aspect-ratio: 4 / 3; overflow: hidden; border-radius: .4rem; background: var(--surface-muted);
|
|
267
|
+
}
|
|
268
|
+
html[data-files-view^="grid"] .fx-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
269
|
+
/* With the picture there, the kind icon beside the name is saying it twice. */
|
|
270
|
+
html[data-files-view^="grid"] .fx-row.has-thumb .fx-icon { display: none; }
|
|
271
|
+
html[data-files-view^="grid"] .fx-row.has-thumb { grid-template-areas: "thumb thumb" "name name" "meta meta" "actions actions"; }
|
|
272
|
+
|
|
273
|
+
/* A file's kind icon in the old card rows (the ai-* workspaces still draw those). */
|
|
274
|
+
.file-row .file-name { display: inline-flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
|
|
275
|
+
.file-row .file-name > .tabler-icon { color: var(--text-subtle); flex: none; }
|
|
276
|
+
|
|
277
|
+
/* A text file read in the modal. Monospace, wrapped, and bounded by the modal's own height so the
|
|
278
|
+
page behind it does not grow a second scrollbar. */
|
|
279
|
+
.review-text {
|
|
280
|
+
max-height: 60vh; overflow: auto; margin: 0;
|
|
281
|
+
background: var(--surface-muted); border-radius: .5rem; padding: .75rem 1rem;
|
|
282
|
+
font-family: var(--font-code); font-size: .8rem; line-height: 1.55;
|
|
283
|
+
white-space: pre-wrap; word-break: break-word;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* A recording's poster carries a play mark, so a still is never mistaken for a picture. */
|
|
287
|
+
html[data-files-view^="grid"] .fx-thumb.is-video { position: relative; }
|
|
288
|
+
html[data-files-view^="grid"] .fx-thumb .fx-play {
|
|
289
|
+
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
|
290
|
+
color: var(--text-inverse);
|
|
291
|
+
background: color-mix(in srgb, var(--brand-dark) 30%, transparent);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* A screen in the row's menu: its name on the left, what it is doing on the right.
|
|
295
|
+
UIkit is linked after this stylesheet and paints .uk-label as a 21px UPPERCASE block, which in a
|
|
296
|
+
menu row shouts louder than the name you are reading — so the state is out-specified down to a
|
|
297
|
+
quiet pill the size of the line it sits on. */
|
|
298
|
+
.uk-dropdown-nav a .screen-item-state { float: right; display: inline-flex; gap: .3rem; align-items: center; }
|
|
299
|
+
.uk-dropdown-nav a .screen-item-state .uk-label {
|
|
300
|
+
height: 17px; display: inline-flex; align-items: center; border-radius: 500px;
|
|
301
|
+
padding: 0 7px; font-size: 10px; line-height: 1; text-transform: none; letter-spacing: .01em;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* One snapshot per line: its name, what it is, and what can be done with it. The name leads because
|
|
305
|
+
that is what you are looking for; the engine and version sit with the date and size, because they
|
|
306
|
+
are the fact that decides whether Restore is even offered. */
|
|
307
|
+
.snap-list .snap-line { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; padding: .15rem 0; }
|
|
308
|
+
.snap-list .snap-name { font-weight: 600; display: inline-flex; align-items: center; gap: .4rem; }
|
|
309
|
+
.snap-list .snap-meta { margin-left: auto; }
|
|
310
|
+
.snap-list .snap-actions { display: inline-flex; align-items: center; gap: .35rem; }
|
|
311
|
+
.snap-new { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
|
|
312
|
+
.snap-new .uk-input { max-width: 22rem; }
|
|
313
|
+
|
|
314
|
+
/* One add-on per line: its name, then what can be done with it, with the reason underneath where
|
|
315
|
+
there is one. The reason is the point of a recommendation, so it gets its own line rather than
|
|
316
|
+
being squeezed into the name's. */
|
|
317
|
+
.addon-list .addon-line { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
|
|
318
|
+
.addon-list .addon-name { font-weight: 600; }
|
|
319
|
+
.addon-list .addon-actions { margin-left: auto; display: inline-flex; gap: .35rem; }
|
|
320
|
+
.addon-list .addon-why { margin: .1rem 0 .2rem; }
|
|
321
|
+
.addon-search { display: flex; gap: .5rem; align-items: center; }
|
|
322
|
+
.addon-search .uk-input { max-width: 26rem; }
|
|
323
|
+
.addon-pager { display: flex; gap: .75rem; align-items: center; }
|
|
324
|
+
|
|
325
|
+
/* One dump per line, laid out like the snapshots list beside it: the file, then when and how big,
|
|
326
|
+
then the one thing that can be done with it. */
|
|
327
|
+
.import-list .import-line { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
|
|
328
|
+
.import-list .import-name { font-weight: 600; word-break: break-all; }
|
|
329
|
+
.import-list .import-meta { margin-left: auto; }
|
|
330
|
+
|
|
331
|
+
/* A mailbox: subject on the left, when and how big on the right, who it was for underneath. Unread
|
|
332
|
+
leads with a dot rather than bold type — a list where half the rows are bold is a list nobody can
|
|
333
|
+
scan, and the dot is the same mark Mailpit itself uses. */
|
|
334
|
+
.mail-list .mail-line { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
|
|
335
|
+
.mail-list .mail-subject { font-weight: 600; color: var(--text-default); text-decoration: none; }
|
|
336
|
+
.mail-list .mail-subject:hover { color: var(--link); text-decoration: underline; }
|
|
337
|
+
.mail-list .mail-dot { color: var(--link); font-size: .7rem; vertical-align: middle; }
|
|
338
|
+
.mail-list .mail-meta { margin-left: auto; white-space: nowrap; }
|
|
339
|
+
.mail-list .mail-who { margin: .05rem 0 .1rem; overflow-wrap: anywhere; }
|
|
340
|
+
.mail-list .mail-drop { color: var(--text-subtle); padding: 0 .25rem; line-height: 1; }
|
|
341
|
+
.mail-list .mail-drop:hover { color: var(--danger); }
|
|
342
|
+
.mail-tools { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; margin: .35rem 0 .6rem; }
|
|
343
|
+
.mail-search { display: flex; gap: .4rem; align-items: center; }
|
|
344
|
+
.mail-search .uk-input { max-width: 20rem; }
|
|
345
|
+
.mail-bulk { display: inline-flex; gap: .4rem; align-items: center; margin-left: auto; }
|
|
346
|
+
|
|
347
|
+
/* A recording or sound with no picture of its own: the kind icon on a brand tint, so a grid of them has
|
|
348
|
+
cards all the way down. */
|
|
349
|
+
html[data-files-view^="grid"] .fx-thumb.is-default {
|
|
350
|
+
display: flex; align-items: center; justify-content: center;
|
|
351
|
+
color: var(--brand-primary); background: linear-gradient(135deg, var(--subtle-brand), var(--subtle-accent));
|
|
352
|
+
}
|
|
353
|
+
html[data-files-view^="grid"] .fx-row:has(.fx-thumb.is-default) .fx-icon { display: none; }
|
|
354
|
+
html[data-files-view^="grid"] .fx-row:has(.fx-thumb.is-default) { grid-template-areas: "thumb thumb" "name name" "meta meta" "actions actions"; }
|
|
355
|
+
|
|
356
|
+
/* A recording's or sound's picture plays it, like its Play button. */
|
|
357
|
+
.fx-thumb[hx-get] { cursor: pointer; }
|
|
358
|
+
html[data-files-view^="grid"] .fx-thumb[hx-get]:hover, html[data-files-view^="grid"] .fx-thumb[hx-get]:focus-visible { filter: brightness(0.92); outline: 2px solid var(--brand-primary); outline-offset: 2px; }
|
|
359
|
+
|
|
360
|
+
/* In a narrow card the size, length and date no longer share a line: they stacked over each other. */
|
|
361
|
+
html[data-files-view="grid-4"] .file-list .fx-row,
|
|
362
|
+
html[data-files-view="grid-5"] .file-list .fx-row,
|
|
363
|
+
html[data-files-view="grid-6"] .file-list .fx-row { grid-template-areas: "thumb thumb" "icon name" "size size" "date date" "actions actions"; }
|
|
364
|
+
html[data-files-view="grid-4"] .file-list .fx-row.has-thumb,
|
|
365
|
+
html[data-files-view="grid-5"] .file-list .fx-row.has-thumb,
|
|
366
|
+
html[data-files-view="grid-6"] .file-list .fx-row.has-thumb,
|
|
367
|
+
html[data-files-view="grid-4"] .file-list .fx-row:has(.fx-thumb.is-default),
|
|
368
|
+
html[data-files-view="grid-5"] .file-list .fx-row:has(.fx-thumb.is-default),
|
|
369
|
+
html[data-files-view="grid-6"] .file-list .fx-row:has(.fx-thumb.is-default) { grid-template-areas: "thumb thumb" "name name" "size size" "date date" "actions actions"; }
|
|
370
|
+
html[data-files-view="grid-4"] .fx-size, html[data-files-view="grid-5"] .fx-size, html[data-files-view="grid-6"] .fx-size { grid-area: size; }
|
|
371
|
+
html[data-files-view="grid-4"] .fx-date, html[data-files-view="grid-5"] .fx-date, html[data-files-view="grid-6"] .fx-date { grid-area: date; justify-self: start; }
|
|
372
|
+
html[data-files-view^="grid"] .fx-actions { flex-wrap: wrap; }
|
|
373
|
+
html[data-files-view^="grid"] .fx-actions .uk-button { white-space: nowrap; }
|
|
374
|
+
|
|
375
|
+
/* The state of a DDEV project: a dot drawn by CSS (an emoji is drawn by each machine's own font) and, when it
|
|
376
|
+
is stopped, a quiet outline. Stopped used to take the same brand blue as "agent working" and was the loudest
|
|
377
|
+
chip on its card; Running is green and the agent keeps the blue. */
|
|
378
|
+
.state-chip { display: inline-flex; align-items: center; gap: 0.375rem; }
|
|
379
|
+
.state-dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; flex: none; }
|
|
380
|
+
.uk-label.state-chip-off {
|
|
381
|
+
background: transparent;
|
|
382
|
+
color: var(--text-subtle);
|
|
383
|
+
border: 1px solid var(--line);
|
|
384
|
+
}
|
|
385
|
+
/* An attached session is somebody looking at it, not something to act on: amber is kept for "waiting for you". */
|
|
386
|
+
.uk-label.screen-state-attached {
|
|
387
|
+
background: transparent;
|
|
388
|
+
color: var(--text-default);
|
|
389
|
+
border: 1px solid var(--line);
|
|
390
|
+
}
|