feat: rework of the full CSS machine to tailwind and modular CSS
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
577
TAILWIND.md
Normal file
577
TAILWIND.md
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
# Tailwind And Phoenix Asset Tooling For bDS2 Desktop
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This document describes the target styling architecture for a Tailwind-integrated Phoenix LiveView desktop app in this repository.
|
||||||
|
|
||||||
|
It is written as a handoff for a coding agent that will perform the implementation.
|
||||||
|
|
||||||
|
This is not a migration guide from a public web app. It is a target-state guide for a Phoenix LiveView desktop shell with local assets, dense editor surfaces, overlays, resizable panes, and desktop-specific titlebar behavior.
|
||||||
|
|
||||||
|
## Verified Current State
|
||||||
|
|
||||||
|
The current app does not use the default Phoenix asset pipeline.
|
||||||
|
|
||||||
|
- CSS is served directly from `priv/ui/app.css`.
|
||||||
|
- JS is served directly from `priv/ui/live.js`.
|
||||||
|
- Static assets are served from `priv/ui` in `lib/bds/desktop/endpoint.ex`.
|
||||||
|
- The root layout links `/assets/app.css` in `lib/bds/desktop/layouts.ex`, but that path is currently backed by `priv/ui/app.css`, not by generated Phoenix assets.
|
||||||
|
- There is no `:tailwind` or `:esbuild` setup in `mix.exs`.
|
||||||
|
|
||||||
|
Relevant files:
|
||||||
|
|
||||||
|
- `lib/bds/desktop/endpoint.ex`
|
||||||
|
- `lib/bds/desktop/layouts.ex`
|
||||||
|
- `mix.exs`
|
||||||
|
- `priv/ui/app.css`
|
||||||
|
- `priv/ui/live.js`
|
||||||
|
|
||||||
|
## Official Phoenix Facts
|
||||||
|
|
||||||
|
The implementation target should follow Phoenix defaults where they fit the desktop shell.
|
||||||
|
|
||||||
|
Official Phoenix references:
|
||||||
|
|
||||||
|
- Phoenix Asset Management: https://hexdocs.pm/phoenix/asset_management.html
|
||||||
|
- Phoenix Components and HEEx: https://hexdocs.pm/phoenix/components.html
|
||||||
|
- Phoenix.Component reference: https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html
|
||||||
|
|
||||||
|
Facts from Phoenix docs and generator templates:
|
||||||
|
|
||||||
|
- Phoenix v1.7+ defaults to Tailwind for CSS and esbuild for JS.
|
||||||
|
- The default CSS source entrypoint is `assets/css/app.css`.
|
||||||
|
- The default generated CSS output is `priv/static/assets/css/app.css`.
|
||||||
|
- The default generated JS output is `priv/static/assets/js/app.js`.
|
||||||
|
- Phoenix promotes function components and HEEx as the main rendering model.
|
||||||
|
- Phoenix does not prescribe per-component CSS modules. Tailwind-first HEEx plus shared source CSS is a valid Phoenix-default shape.
|
||||||
|
|
||||||
|
## Target Outcome
|
||||||
|
|
||||||
|
The target outcome is a solid Phoenix-default asset setup with Tailwind as the main styling system and a generated production stylesheet.
|
||||||
|
|
||||||
|
The target should look like this:
|
||||||
|
|
||||||
|
- Tailwind source lives in `assets/css/app.css` and imported CSS modules.
|
||||||
|
- The final stylesheet is generated into `priv/static/assets/css/app.css`.
|
||||||
|
- LiveView JS entrypoint lives in `assets/js/app.js` and builds to `priv/static/assets/js/app.js`.
|
||||||
|
- The desktop endpoint serves static assets from `priv/static`.
|
||||||
|
- Layouts reference the generated CSS and JS outputs.
|
||||||
|
- HEEx markup carries most layout, spacing, typography, responsive, and state classes.
|
||||||
|
- Authored CSS remains for global tokens, app-region behavior, pseudo-elements, scrollbars, Monaco integration, hard selectors, and overlay mechanics.
|
||||||
|
|
||||||
|
## Architecture Rules
|
||||||
|
|
||||||
|
### 1. Tailwind Owns The Common Case
|
||||||
|
|
||||||
|
Use Tailwind utility classes directly in HEEx for:
|
||||||
|
|
||||||
|
- flex and grid layout
|
||||||
|
- spacing
|
||||||
|
- typography
|
||||||
|
- borders and radii
|
||||||
|
- colors and opacity
|
||||||
|
- active, selected, disabled, and hover states
|
||||||
|
- responsive breakpoints
|
||||||
|
- overflow and truncation
|
||||||
|
|
||||||
|
Do not preserve large semantic wrapper classes when they only encode simple layout decisions.
|
||||||
|
|
||||||
|
Good examples to move into HEEx classes:
|
||||||
|
|
||||||
|
- tab rows
|
||||||
|
- button rows
|
||||||
|
- editor header layout
|
||||||
|
- metadata field layout
|
||||||
|
- sidebar list row spacing
|
||||||
|
- status bar item alignment
|
||||||
|
|
||||||
|
### 2. Authored CSS Owns The Desktop-Specific Case
|
||||||
|
|
||||||
|
Keep authored CSS source files for:
|
||||||
|
|
||||||
|
- `app-region` and `-webkit-app-region`
|
||||||
|
- pseudo-elements used for icons, handles, and active markers
|
||||||
|
- custom scrollbars
|
||||||
|
- Monaco/editor iframe or host integration
|
||||||
|
- absolute overlay stacks and backdrops
|
||||||
|
- drag and drop affordances
|
||||||
|
- complex attribute selectors
|
||||||
|
- hard-to-read repeated combinations that should become shared component classes
|
||||||
|
|
||||||
|
Do not force these into giant utility strings if readability drops.
|
||||||
|
|
||||||
|
### 3. Keep A Thin Semantic CSS Layer
|
||||||
|
|
||||||
|
It is acceptable to keep a small number of semantic component classes when they encode a repeated desktop UI primitive.
|
||||||
|
|
||||||
|
Examples of valid semantic classes in the target state:
|
||||||
|
|
||||||
|
- `.window-titlebar`
|
||||||
|
- `.resizable-panel-divider`
|
||||||
|
- `.overlay-root`
|
||||||
|
- `.monaco-host`
|
||||||
|
- `.panel-entry`
|
||||||
|
- `.btn-base`, `.btn-theme-primary`, `.btn-theme-danger`
|
||||||
|
|
||||||
|
These classes should be implemented in Tailwind source CSS using `@layer components` and should remain small, stable, and reusable.
|
||||||
|
|
||||||
|
### 4. Tokens Must Be Centralized
|
||||||
|
|
||||||
|
The current stylesheet relies heavily on VS Code-like CSS variables. Preserve that idea.
|
||||||
|
|
||||||
|
The new `assets/css/app.css` must define the design tokens once, using Tailwind v4 `@theme` plus any required raw CSS custom properties for runtime-driven values.
|
||||||
|
|
||||||
|
Examples from the current app that must survive:
|
||||||
|
|
||||||
|
- shell/background colors
|
||||||
|
- tab active/inactive colors
|
||||||
|
- status bar colors
|
||||||
|
- focus border color
|
||||||
|
- input background and border colors
|
||||||
|
- sidebar width
|
||||||
|
- assistant width
|
||||||
|
- font family and font size
|
||||||
|
|
||||||
|
### 5. Desktop Layout Constraints Must Stay Intact
|
||||||
|
|
||||||
|
The styling rewrite must preserve these runtime constraints:
|
||||||
|
|
||||||
|
- the app occupies full window width and height
|
||||||
|
- the shell uses `overflow: hidden` at the top level
|
||||||
|
- the main workbench uses `min-height: 0` and `min-width: 0` correctly
|
||||||
|
- resizable sidebars remain width-variable
|
||||||
|
- titlebar remains draggable except for interactive controls
|
||||||
|
- overlays render above the shell without breaking keyboard focus or pointer behavior
|
||||||
|
|
||||||
|
## Proposed Asset Layout
|
||||||
|
|
||||||
|
Use the standard Phoenix asset layout.
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/
|
||||||
|
css/
|
||||||
|
app.css
|
||||||
|
shell.css
|
||||||
|
sidebar.css
|
||||||
|
tabs.css
|
||||||
|
editor.css
|
||||||
|
forms.css
|
||||||
|
overlays.css
|
||||||
|
panel.css
|
||||||
|
assistant.css
|
||||||
|
menu_editor.css
|
||||||
|
media_editor.css
|
||||||
|
utilities.css
|
||||||
|
js/
|
||||||
|
app.js
|
||||||
|
...
|
||||||
|
priv/
|
||||||
|
static/
|
||||||
|
assets/
|
||||||
|
css/
|
||||||
|
js/
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended responsibilities:
|
||||||
|
|
||||||
|
- `assets/css/app.css`: Tailwind import, `@source`, tokens, base layer, imports
|
||||||
|
- `assets/css/shell.css`: app shell, titlebar, activity bar, pane shells, status bar
|
||||||
|
- `assets/css/sidebar.css`: sidebar filters, search, chips, calendar tree, load more
|
||||||
|
- `assets/css/tabs.css`: workbench tabs and editor tabs
|
||||||
|
- `assets/css/editor.css`: common editor frame, toolbar, meta column, shared form shell
|
||||||
|
- `assets/css/forms.css`: shared input, textarea, tag chip, picker, inline action primitives
|
||||||
|
- `assets/css/overlays.css`: overlay root, modal backdrop, dialog shells, gallery/lightbox
|
||||||
|
- `assets/css/panel.css`: panel tabs, panel entry cards, tasks, output, git log
|
||||||
|
- `assets/css/assistant.css`: assistant sidebar and chat-specific shared surfaces
|
||||||
|
- `assets/css/menu_editor.css`: menu tree, drag/drop indicators, picker lists
|
||||||
|
- `assets/css/media_editor.css`: media preview, linked post picker, detail forms
|
||||||
|
- `assets/css/utilities.css`: a very small set of custom utilities that are truly reused
|
||||||
|
|
||||||
|
## Proposed Phoenix Asset Tooling
|
||||||
|
|
||||||
|
The implementation should introduce the standard Phoenix aliases and configs.
|
||||||
|
|
||||||
|
### mix.exs
|
||||||
|
|
||||||
|
Add:
|
||||||
|
|
||||||
|
- `{:tailwind, "~> 0.3", runtime: Mix.env() == :dev}`
|
||||||
|
- `{:esbuild, "~> 0.10", runtime: Mix.env() == :dev}`
|
||||||
|
|
||||||
|
Add aliases similar to:
|
||||||
|
|
||||||
|
- `assets.setup`
|
||||||
|
- `assets.build`
|
||||||
|
- `assets.deploy`
|
||||||
|
|
||||||
|
### Versioning (mandatory)
|
||||||
|
|
||||||
|
The Elixir `:tailwind` wrapper still defaults to Tailwind v3. The plan in this document assumes **Tailwind v4** syntax (`@import "tailwindcss"`, `@theme`, `@source`, `@layer components`). Pin both tools explicitly in `config/config.exs`:
|
||||||
|
|
||||||
|
- `config :tailwind, version: "4.1.14"` (or current 4.1.x)
|
||||||
|
- `config :esbuild, version: "0.25.4"` (or current 0.25.x)
|
||||||
|
|
||||||
|
Without an explicit v4 pin the build will silently install Tailwind v3 and v4 directives will not resolve.
|
||||||
|
|
||||||
|
### No Node.js policy
|
||||||
|
|
||||||
|
The Elixir `:tailwind` and `:esbuild` wrappers download self-contained binaries and do **not** require Node.js. The implementation MUST stay Node-free unless a third-party Tailwind plugin is later required (in which case the custom `assets/build.js` route from the Phoenix asset_management guide is used). No `package.json` is added under `assets/`.
|
||||||
|
|
||||||
|
### config/config.exs
|
||||||
|
|
||||||
|
Configure Tailwind input and output paths.
|
||||||
|
|
||||||
|
Target output:
|
||||||
|
|
||||||
|
- `assets/css/app.css` -> `priv/static/assets/css/app.css`
|
||||||
|
|
||||||
|
Configure esbuild for:
|
||||||
|
|
||||||
|
- `assets/js/app.js` -> `priv/static/assets/js/app.js`
|
||||||
|
|
||||||
|
esbuild profile must include `--bundle --target=es2022 --outdir=../priv/static/assets/js --external:/fonts/* --external:/images/*` and `nodePaths: [Mix.Project.build_path() <> "/../../deps"]` so `phoenix`, `phoenix_html`, and `phoenix_live_view` resolve from `deps/` without an `npm install`.
|
||||||
|
|
||||||
|
### config/dev.exs
|
||||||
|
|
||||||
|
Add Phoenix watchers for:
|
||||||
|
|
||||||
|
- Tailwind `--watch`
|
||||||
|
- esbuild `--watch`
|
||||||
|
|
||||||
|
### No phx.digest in desktop builds
|
||||||
|
|
||||||
|
This is a desktop app served through an embedded WebView, not a public web app behind a CDN.
|
||||||
|
|
||||||
|
- Do NOT run `mix phx.digest` as part of `assets.deploy`.
|
||||||
|
- Output filenames stay stable (`app.css`, `app.js`) so the layout can link them by fixed path.
|
||||||
|
- `assets.deploy` for this repo is: `tailwind default --minify`, `esbuild default --minify`. Nothing else.
|
||||||
|
|
||||||
|
### endpoint/layout changes
|
||||||
|
|
||||||
|
Update the desktop endpoint and root layout to serve and link generated assets from `priv/static/assets` instead of `priv/ui`.
|
||||||
|
|
||||||
|
Specifically:
|
||||||
|
|
||||||
|
- Replace the existing `Plug.Static` for `/assets` with `from: {:bds, "priv/static/assets"}` and `only` listing the generated `css` and `js` directories.
|
||||||
|
- Drop the `/vendor/phoenix` and `/vendor/live_view` `Plug.Static` blocks; those scripts are now bundled by esbuild from `deps/`.
|
||||||
|
- Add a dedicated `Plug.Static` for `/monaco` pointing at `priv/ui/monaco` (or move it to `priv/static/monaco`). Monaco is a prebuilt vendor drop and MUST NOT be passed through esbuild.
|
||||||
|
- Remove the `<script src="/vendor/phoenix/...">` and `<script src="/vendor/live_view/...">` tags from `lib/bds/desktop/layouts.ex`. Keep only `/assets/app.css` and `/assets/app.js`.
|
||||||
|
|
||||||
|
## JS Pipeline
|
||||||
|
|
||||||
|
The JS pipeline mirrors the CSS pipeline.
|
||||||
|
|
||||||
|
### Entrypoint
|
||||||
|
|
||||||
|
`assets/js/app.js` is the single esbuild entrypoint. It:
|
||||||
|
|
||||||
|
- imports `phoenix`, `phoenix_html`, and `phoenix_live_view` (resolved from `deps/` via esbuild `nodePaths`)
|
||||||
|
- constructs the `LiveSocket` with the desktop CSRF token
|
||||||
|
- registers all hooks currently defined inline in `priv/ui/live.js`
|
||||||
|
- wires the native menu / titlebar / shortcut bridges
|
||||||
|
|
||||||
|
### Module layout
|
||||||
|
|
||||||
|
Split the current `priv/ui/live.js` (1.4k lines) into focused modules under `assets/js/`:
|
||||||
|
|
||||||
|
- `assets/js/app.js` - entrypoint, LiveSocket, hook registration
|
||||||
|
- `assets/js/hooks/` - one file per hook
|
||||||
|
- `assets/js/bridges/` - native menu, titlebar, shortcut bridges
|
||||||
|
- `assets/js/monaco/` - thin host glue only; do NOT bundle Monaco itself
|
||||||
|
|
||||||
|
### Monaco carve-out
|
||||||
|
|
||||||
|
Monaco is loaded as prebuilt assets from `/monaco/...` and is not part of the esbuild graph. The host glue in `assets/js/monaco/` only configures the loader URL and posts messages; it does not `import` Monaco modules.
|
||||||
|
|
||||||
|
### Vendor stripping
|
||||||
|
|
||||||
|
After the switch:
|
||||||
|
|
||||||
|
- `priv/ui/app.css` is deleted (its content has been redistributed under `assets/css/`).
|
||||||
|
- `priv/ui/live.js` is deleted.
|
||||||
|
- `priv/ui/monaco/` stays (or moves to `priv/static/monaco/`).
|
||||||
|
- The `phoenix` and `phoenix_live_view` dep static drops are no longer served.
|
||||||
|
|
||||||
|
## Iconography
|
||||||
|
|
||||||
|
The app keeps its existing **inline SVG** icon set. Do NOT adopt the Phoenix 1.8 Heroicons-via-Tailwind-plugin pattern.
|
||||||
|
|
||||||
|
Reasons:
|
||||||
|
|
||||||
|
- The target visual look is defined by the current bDS UI; its icons are part of that look.
|
||||||
|
- Inline SVG keeps icons under direct control for sizing, stroke, and currentColor styling via Tailwind utility classes.
|
||||||
|
- Avoiding the Heroicons git dep keeps the build Node-free and dependency-light.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- Existing SVG icons stay where they are (HEEx components / inline `<svg>` markup).
|
||||||
|
- Icon size and color are controlled by Tailwind utilities on the wrapping element (`size-4`, `text-[var(--color-icon)]`, etc.) using `fill="currentColor"` or `stroke="currentColor"`.
|
||||||
|
- No `:heroicons` mix dep, no Tailwind icon plugin, no `assets/vendor/heroicons.js`.
|
||||||
|
- If a new icon is needed, add the SVG inline in the appropriate component module.
|
||||||
|
|
||||||
|
## HEEx Styling Strategy
|
||||||
|
|
||||||
|
### Prefer Utility Classes In Templates
|
||||||
|
|
||||||
|
Use utility classes in HEEx for:
|
||||||
|
|
||||||
|
- shell flex layout
|
||||||
|
- editor content spacing
|
||||||
|
- section stacks
|
||||||
|
- truncation and overflow
|
||||||
|
- standard button alignment
|
||||||
|
- grid column changes at breakpoints
|
||||||
|
- selected and hovered states that are directly tied to assign state
|
||||||
|
|
||||||
|
### Keep Dynamic Class Lists Explicit
|
||||||
|
|
||||||
|
LiveView templates should build classes with arrays where state is already in assigns.
|
||||||
|
|
||||||
|
Example pattern:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
class={[
|
||||||
|
"flex items-center gap-2 px-3 py-2 text-sm",
|
||||||
|
selected? && "bg-[var(--color-selected-bg)] text-white",
|
||||||
|
disabled? && "opacity-50 pointer-events-none"
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Do Not Hide Structure Behind Giant Custom Class Trees
|
||||||
|
|
||||||
|
Avoid re-creating the current `app.css` by keeping every nested selector and merely moving it into `assets/css`.
|
||||||
|
|
||||||
|
If a selector exists only because the old CSS owned all layout, move that responsibility into HEEx.
|
||||||
|
|
||||||
|
## Tailwind Source Conventions
|
||||||
|
|
||||||
|
### app.css
|
||||||
|
|
||||||
|
`assets/css/app.css` should use Tailwind v4 import syntax.
|
||||||
|
|
||||||
|
`source(none)` disables Tailwind's automatic content detection so only directories listed via `@source` are scanned. Every directory that ships HEEx or class strings MUST be listed. Audit `lib/` for additional renderers (preview, generation, MCP surfaces) and add `@source` lines for any that emit class names consumed at build time.
|
||||||
|
|
||||||
|
Runtime-driven values (e.g. resizable panel widths that change via CSS variable assignment from JS) MUST stay as plain CSS custom properties under `:root` or a scoped selector. `@theme` values are baked at build time and are not appropriate for values mutated at runtime. Use `@theme` for stable design tokens (colors, font sizes, spacing scale extensions) that should produce utility classes; use `:root { --foo: … }` for everything that the app writes to at runtime.
|
||||||
|
|
||||||
|
Suggested structure:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import "tailwindcss" source(none);
|
||||||
|
|
||||||
|
@source "../css";
|
||||||
|
@source "../js";
|
||||||
|
@source "../../lib/bds/desktop";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
/* app tokens */
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
/* html, body, root shell defaults */
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "./shell.css";
|
||||||
|
@import "./sidebar.css";
|
||||||
|
@import "./tabs.css";
|
||||||
|
@import "./editor.css";
|
||||||
|
@import "./forms.css";
|
||||||
|
@import "./panel.css";
|
||||||
|
@import "./assistant.css";
|
||||||
|
@import "./overlays.css";
|
||||||
|
@import "./menu_editor.css";
|
||||||
|
@import "./media_editor.css";
|
||||||
|
@import "./utilities.css";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component CSS Rules
|
||||||
|
|
||||||
|
When writing component CSS in imported files:
|
||||||
|
|
||||||
|
- use `@layer components` for shared semantic classes
|
||||||
|
- use `@layer utilities` only for narrowly reusable custom utilities
|
||||||
|
- keep selectors shallow
|
||||||
|
- avoid giant descendant chains unless required by generated HTML structure or overlay mechanics
|
||||||
|
- prefer `@apply` sparingly and only for stable component classes, not as a substitute for writing HEEx utilities
|
||||||
|
|
||||||
|
## Current UI Structure Reference
|
||||||
|
|
||||||
|
The implementation agent must use the current monolith as a source map, not as the final architecture.
|
||||||
|
|
||||||
|
The current stylesheet is `priv/ui/app.css` and is approximately 8.5k lines.
|
||||||
|
|
||||||
|
Use the following region map when carrying styling over.
|
||||||
|
|
||||||
|
### Current app.css region map
|
||||||
|
|
||||||
|
- Lines `1-140`: root variables, base element reset, buttons, top-level shell defaults
|
||||||
|
- Lines `141-415`: app shell and window titlebar
|
||||||
|
- `.app`, `.app-main`, `.app-content`
|
||||||
|
- `.window-titlebar*`
|
||||||
|
- Lines `416-623`: activity bar and sidebar shells
|
||||||
|
- `.activity-bar*`
|
||||||
|
- `.sidebar-shell*`, `.assistant-sidebar-shell*`
|
||||||
|
- `.sidebar*`, `.assistant-sidebar*`
|
||||||
|
- `.resizable-panel-divider`
|
||||||
|
- Lines `624-812`: workbench tab bar
|
||||||
|
- `.tab-bar*`, `.tab*`
|
||||||
|
- Lines `813-1100`: shared editor shell and editor tabs
|
||||||
|
- `.editor-shell`, `.editor-frame`, `.editor-main`, `.editor-meta`
|
||||||
|
- `.editor-toolbar*`
|
||||||
|
- `.post-editor .editor-header`, `.editor-tabs`, `.editor-tab*`
|
||||||
|
- Lines `1100-1700`: post editor forms and metadata/media panel
|
||||||
|
- `.post-editor .editor-content`
|
||||||
|
- `.post-editor .editor-field*`
|
||||||
|
- `.post-editor .post-editor-input`, `.post-editor-textarea`
|
||||||
|
- `.tag-input*`, `.tag-chip*`
|
||||||
|
- media insertion and post media list
|
||||||
|
- Line `1691` onward: first responsive collapse for editor/media layout
|
||||||
|
- Lines `1736-1833`: early shell/gallery overlay and insert-media grid rules
|
||||||
|
- Lines `1833+`: more mobile/narrow viewport adjustments
|
||||||
|
- Lines `1950-2263`: status bar and shell footer controls
|
||||||
|
- `.status-bar*`
|
||||||
|
- `.project-selector*`
|
||||||
|
- Lines `2264-2599`: overlay root and modal system
|
||||||
|
- `.overlay-root`
|
||||||
|
- `.ai-suggestions-modal*`
|
||||||
|
- `.insert-modal*`
|
||||||
|
- `.language-picker-modal*`
|
||||||
|
- `.confirm-delete-modal*`
|
||||||
|
- `.gallery-overlay*`
|
||||||
|
- Lines `2600-2876`: menu editor
|
||||||
|
- `.menu-editor-*`
|
||||||
|
- Lines `2889+`: media editor
|
||||||
|
- `[data-testid="media-editor"] *`
|
||||||
|
- Lines `3458+`: style/theme picker surface
|
||||||
|
- `.style-theme-*`
|
||||||
|
- Lines `4958`, `6722`, `8268`, `8514`: later breakpoint-specific adjustments for desktop shell and advanced editors
|
||||||
|
|
||||||
|
Treat those ranges as source material to be redistributed into the new Tailwind source layout.
|
||||||
|
|
||||||
|
## Current HEEx And Component Surface Reference
|
||||||
|
|
||||||
|
The styling rewrite needs to move with the component structure, not only with CSS selectors.
|
||||||
|
|
||||||
|
Important current rendering surfaces:
|
||||||
|
|
||||||
|
- `lib/bds/desktop/shell_live.ex`
|
||||||
|
- top-level workbench render entry
|
||||||
|
- `lib/bds/desktop/shell_live/sidebar_components.ex`
|
||||||
|
- sidebar search, archive tree, tag/category chips, nav/settings lists, load more
|
||||||
|
- `lib/bds/desktop/shell_live/panel_renderer.ex`
|
||||||
|
- tasks, output, git log, panel toolbars
|
||||||
|
- `lib/bds/desktop/shell_live/post_editor.ex`
|
||||||
|
- post editor render surface
|
||||||
|
- `lib/bds/desktop/shell_live/media_editor.ex`
|
||||||
|
- media editor render surface
|
||||||
|
- `lib/bds/desktop/shell_live/script_editor.ex`
|
||||||
|
- script editor render surface
|
||||||
|
- `lib/bds/desktop/shell_live/template_editor.ex`
|
||||||
|
- template editor render surface
|
||||||
|
- `lib/bds/desktop/shell_live/chat_editor.ex`
|
||||||
|
- assistant/chat surface
|
||||||
|
- `lib/bds/desktop/shell_live/menu_editor.ex`
|
||||||
|
- menu editor tree surface
|
||||||
|
|
||||||
|
Implementation rule:
|
||||||
|
|
||||||
|
- move simple layout and state styling into these HEEx/component surfaces
|
||||||
|
- keep authored CSS for shared primitives and complex desktop behavior
|
||||||
|
|
||||||
|
## Desktop-Specific Styling Rules
|
||||||
|
|
||||||
|
The app is a desktop shell, not a normal browser page.
|
||||||
|
|
||||||
|
The implementation must preserve:
|
||||||
|
|
||||||
|
- draggable titlebar regions
|
||||||
|
- non-draggable controls inside the titlebar
|
||||||
|
- local, app-like split-pane behavior
|
||||||
|
- fixed-height titlebar, tabs, and status bar rails
|
||||||
|
- overlay stacking over the shell
|
||||||
|
- editor and sidebar widths controlled by CSS variables where appropriate
|
||||||
|
- visual parity for assistant/sidebar/panel open and closed states
|
||||||
|
|
||||||
|
Do not optimize for tiny public web payloads at the expense of shell clarity.
|
||||||
|
Do optimize for maintainability, explicit component ownership, and predictable desktop behavior.
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 0: Tests And Validation Baseline
|
||||||
|
|
||||||
|
- Add a smoke test that requests `/assets/app.css` and `/assets/app.js` and asserts a 200 with non-empty body served from `priv/static/assets`.
|
||||||
|
- Add a render snapshot test for the top-level shell HEEx so class-string regressions during the rewrite are caught.
|
||||||
|
- Establish that every phase ends with clean `mix compile --warnings-as-errors`, `mix test`, and `mix dialyzer` runs (per repo AGENTS.md).
|
||||||
|
|
||||||
|
### Phase 1: Install Phoenix Asset Tooling
|
||||||
|
|
||||||
|
- add Tailwind and esbuild dependencies
|
||||||
|
- create `assets/css/app.css` and `assets/js/app.js`
|
||||||
|
- configure `config/config.exs`, `config/dev.exs`, and `mix.exs`
|
||||||
|
- switch endpoint/layouts to generated assets
|
||||||
|
- keep current visuals as close as possible
|
||||||
|
|
||||||
|
### Phase 2: Split The Monolith Into Source Modules
|
||||||
|
|
||||||
|
- move the current `priv/ui/app.css` into the proposed `assets/css/*.css` modules
|
||||||
|
- keep selectors mostly intact at first
|
||||||
|
- copy raw selectors only; do NOT rewrite to `@apply` in this phase
|
||||||
|
- defer all `@apply` and utility-extraction decisions to Phase 3
|
||||||
|
- verify the desktop shell still renders correctly
|
||||||
|
|
||||||
|
### Phase 3: Move Common Layout Into HEEx
|
||||||
|
|
||||||
|
- rewrite top-level shell markup, tabs, headers, and common forms to use Tailwind classes directly
|
||||||
|
- reduce selector nesting
|
||||||
|
- keep only the thin semantic CSS layer
|
||||||
|
|
||||||
|
### Phase 4: Normalize Shared Primitives
|
||||||
|
|
||||||
|
- standardize buttons
|
||||||
|
- standardize inputs and textareas
|
||||||
|
- standardize tabs and badges
|
||||||
|
- standardize panel entries and empty states
|
||||||
|
|
||||||
|
### Phase 5: Finish Desktop-Specific Surfaces
|
||||||
|
|
||||||
|
- overlays
|
||||||
|
- menu editor drag/drop states
|
||||||
|
- media preview/detail layouts
|
||||||
|
- assistant/chat surfaces
|
||||||
|
- narrow viewport behavior
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
The rewrite is successful when:
|
||||||
|
|
||||||
|
- assets are built through Phoenix default tooling
|
||||||
|
- the desktop endpoint serves generated assets from `priv/static`
|
||||||
|
- the visual shell matches the existing app closely enough for feature work to continue
|
||||||
|
- the current `priv/ui/app.css` is no longer the source of truth
|
||||||
|
- the new styling is split into source modules with clear ownership
|
||||||
|
- HEEx markup owns common layout and state classes
|
||||||
|
- authored CSS is limited to tokens, desktop primitives, and complex selectors
|
||||||
|
- Tailwind v4 and esbuild versions are explicitly pinned in `config/config.exs`
|
||||||
|
- no Node.js / `package.json` is introduced under `assets/`
|
||||||
|
- no `mix phx.digest` step exists; generated asset filenames remain stable
|
||||||
|
- inline SVG iconography from the existing UI is preserved (no Heroicons dep)
|
||||||
|
- `mix compile --warnings-as-errors`, `mix test`, and `mix dialyzer` are clean after every phase
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
These are not goals of the rewrite:
|
||||||
|
|
||||||
|
- blindly replacing every old class with utilities in one pass
|
||||||
|
- preserving the exact old selector tree
|
||||||
|
- introducing CSS modules or a React-style styling system
|
||||||
|
- optimizing for generic web landing-page concerns
|
||||||
|
- changing product behavior unrelated to styling and asset delivery
|
||||||
|
|
||||||
|
## Guidance For The Coding Agent
|
||||||
|
|
||||||
|
When implementing this plan:
|
||||||
|
|
||||||
|
- start by wiring Phoenix asset tooling, not by rewriting 8k lines of CSS in place
|
||||||
|
- preserve runtime behavior first, then simplify
|
||||||
|
- move layout decisions to HEEx only when they become clearer there
|
||||||
|
- keep desktop-specific mechanics in CSS
|
||||||
|
- use the current `priv/ui/app.css` region map as a source index, not as a blueprint for the final architecture
|
||||||
19
assets/css/app.css
Normal file
19
assets/css/app.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
@import "tailwindcss" source(none);
|
||||||
|
|
||||||
|
@source "../css";
|
||||||
|
@source "../js";
|
||||||
|
@source "../../lib/bds/desktop";
|
||||||
|
|
||||||
|
@import "./tokens.css";
|
||||||
|
@import "./shell.css";
|
||||||
|
@import "./sidebar.css";
|
||||||
|
@import "./tabs.css";
|
||||||
|
@import "./editor.css";
|
||||||
|
@import "./forms.css";
|
||||||
|
@import "./panel.css";
|
||||||
|
@import "./assistant.css";
|
||||||
|
@import "./overlays.css";
|
||||||
|
@import "./menu_editor.css";
|
||||||
|
@import "./media_editor.css";
|
||||||
|
@import "./import_editor.css";
|
||||||
|
@import "./utilities.css";
|
||||||
206
assets/css/assistant.css
Normal file
206
assets/css/assistant.css
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
.settings-view-shell,
|
||||||
|
.style-view,
|
||||||
|
.tags-view-shell,
|
||||||
|
.scripts-view-shell,
|
||||||
|
.templates-view-shell,
|
||||||
|
.chat-panel {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel {
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
background: var(--vscode-sideBar-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel-title {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
overflow: visible;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel-title-main {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel-header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-model-selector-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-model-selector-button,
|
||||||
|
.chat-model-selector-option {
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-model-selector-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
left: 0;
|
||||||
|
right: auto;
|
||||||
|
border: 1px solid var(--vscode-dropdown-border, var(--vscode-panel-border));
|
||||||
|
background: var(--vscode-dropdown-background, var(--vscode-sideBar-background));
|
||||||
|
color: var(--vscode-dropdown-foreground, var(--vscode-foreground));
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-model-selector-button.chat-model-selector-inline {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-model-selector-caret {
|
||||||
|
position: static;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages,
|
||||||
|
.chat-surface-scroll {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message {
|
||||||
|
display: flex;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.user {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message-content {
|
||||||
|
max-width: min(760px, 100%);
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
background: var(--vscode-sideBar-background);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-message.user .chat-message-content {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-list-activeSelectionForeground);
|
||||||
|
border: 0;
|
||||||
|
padding: 6px 12px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-tool-surface-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-tool-surface-table th,
|
||||||
|
.chat-tool-surface-table td {
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
padding: 6px 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-tool-surface-json {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--vscode-textCodeBlock-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-input-container {
|
||||||
|
--chat-input-line-height: 20px;
|
||||||
|
--chat-input-min-height: 20px;
|
||||||
|
border-top: 1px solid var(--vscode-panel-border);
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: var(--vscode-sideBar-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 30px;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-input-wrapper:focus-within {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-input {
|
||||||
|
flex: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: var(--chat-input-min-height);
|
||||||
|
min-height: var(--chat-input-min-height);
|
||||||
|
margin: 0;
|
||||||
|
padding: 6px 8px;
|
||||||
|
line-height: var(--chat-input-line-height);
|
||||||
|
max-height: 160px;
|
||||||
|
resize: vertical;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-input::placeholder {
|
||||||
|
color: var(--vscode-input-placeholderForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-send-button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
max-width: 22px;
|
||||||
|
max-height: 22px;
|
||||||
|
padding: 0;
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-send-button:hover:not(:disabled) {
|
||||||
|
background: var(--vscode-button-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-send-button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
1131
assets/css/editor.css
Normal file
1131
assets/css/editor.css
Normal file
File diff suppressed because it is too large
Load Diff
141
assets/css/forms.css
Normal file
141
assets/css/forms.css
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
.settings-view,
|
||||||
|
.style-view {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header,
|
||||||
|
.style-view-header {
|
||||||
|
padding: 18px 20px;
|
||||||
|
border-bottom: 1px solid var(--line, #3c3c3c);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-search input {
|
||||||
|
width: min(320px, 40vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-content {
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-section {
|
||||||
|
border: 1px solid var(--line, #3c3c3c);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--panel-2, #252526);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-section-header {
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-bottom: 1px solid var(--line, #3c3c3c);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-section-content {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(180px, 240px) minmax(0, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-control,
|
||||||
|
.setting-input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-actions {
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-theme-picker {
|
||||||
|
padding: 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-theme-option {
|
||||||
|
border: 1px solid var(--line, #3c3c3c);
|
||||||
|
background: var(--panel-2, #252526);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-theme-option.selected {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
box-shadow: 0 0 0 1px var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-theme-swatch {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-theme-tones {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-theme-tone {
|
||||||
|
height: 42px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-apply-row {
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-preview-container {
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-preview-frame {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 420px;
|
||||||
|
border: 1px solid var(--line, #3c3c3c);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.setting-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
689
assets/css/import_editor.css
Normal file
689
assets/css/import_editor.css
Normal file
@@ -0,0 +1,689 @@
|
|||||||
|
.import-analysis {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 18px 20px 26px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis-header p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-definition-name {
|
||||||
|
width: min(480px, 100%);
|
||||||
|
border: 1px solid var(--vscode-input-border, transparent);
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground, var(--vscode-foreground));
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-selectors {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px minmax(0, 1fr) auto;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-editor-background) 76%, var(--vscode-input-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-row label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-path {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-family: var(--vscode-editor-font-family, ui-monospace, monospace);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-path.placeholder {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis button,
|
||||||
|
.import-analysis select {
|
||||||
|
border: 1px solid var(--vscode-button-border, transparent);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis button {
|
||||||
|
background: var(--vscode-button-secondaryBackground, var(--vscode-button-background));
|
||||||
|
color: var(--vscode-button-secondaryForeground, var(--vscode-button-foreground));
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis button:hover:not(:disabled) {
|
||||||
|
background: var(--vscode-button-secondaryHoverBackground, var(--vscode-button-hoverBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analyze-btn,
|
||||||
|
.import-execute-btn {
|
||||||
|
background: var(--vscode-button-background) !important;
|
||||||
|
color: var(--vscode-button-foreground) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis button:disabled {
|
||||||
|
opacity: 0.65;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-editor-background) 84%, var(--vscode-input-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-spinner {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border: 2px solid var(--vscode-descriptionForeground);
|
||||||
|
border-top-color: var(--vscode-button-background);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: import-spinner-rotate 0.8s linear infinite;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-progress {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-progress-step {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-progress-detail {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes import-spinner-rotate {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-site-info {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-site-info-item,
|
||||||
|
.import-stat-card,
|
||||||
|
.import-date-distribution,
|
||||||
|
.import-detail-section,
|
||||||
|
.import-execute-section {
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-editor-background) 84%, var(--vscode-input-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-site-info-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-card {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-card h3,
|
||||||
|
.import-date-distribution h3,
|
||||||
|
.import-detail-section h3,
|
||||||
|
.taxonomy-group h4 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-number {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-breakdown,
|
||||||
|
.import-execute-summary,
|
||||||
|
.import-taxonomy-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-breakdown {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-stat-tag,
|
||||||
|
.import-count-tag,
|
||||||
|
.import-taxonomy-pill,
|
||||||
|
.macro-status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px 9px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-new,
|
||||||
|
.import-taxonomy-pill.new-tax {
|
||||||
|
background: rgba(117, 190, 255, 0.16);
|
||||||
|
color: #75beff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-update,
|
||||||
|
.stat-mapped,
|
||||||
|
.import-taxonomy-pill.exists,
|
||||||
|
.import-taxonomy-pill.mapped,
|
||||||
|
.macro-status-badge.mapped,
|
||||||
|
.import-execution-complete {
|
||||||
|
background: rgba(115, 201, 145, 0.16);
|
||||||
|
color: #73c991;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-conflict {
|
||||||
|
background: rgba(255, 166, 87, 0.16);
|
||||||
|
color: #ffb169;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-duplicate,
|
||||||
|
.stat-missing,
|
||||||
|
.macro-status-badge.unmapped,
|
||||||
|
.import-execution-error {
|
||||||
|
background: rgba(204, 167, 0, 0.16);
|
||||||
|
color: #cca700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-date-distribution,
|
||||||
|
.import-detail-section,
|
||||||
|
.import-execute-section {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-section-toggle {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 0;
|
||||||
|
border: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
color: inherit !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-section-toggle:hover {
|
||||||
|
background: transparent !important;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribution-bars {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribution-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 56px minmax(0, 1fr) 72px;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribution-year,
|
||||||
|
.distribution-count,
|
||||||
|
.slug-cell {
|
||||||
|
font-family: var(--vscode-editor-font-family, ui-monospace, monospace);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribution-bar-container {
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribution-bar {
|
||||||
|
height: 100%;
|
||||||
|
min-width: 8px;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribution-bar-posts {
|
||||||
|
background: linear-gradient(90deg, rgba(117, 190, 255, 0.8), rgba(117, 190, 255, 0.35));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execute-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execute-summary {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execution-complete,
|
||||||
|
.import-execution-error {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execution-progress {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-editor-background) 84%, var(--vscode-input-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execution-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execution-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-progress-bar {
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, rgba(117, 190, 255, 0.85), rgba(117, 190, 255, 0.45));
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-progress-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-phase {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail,
|
||||||
|
.import-counter {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table th,
|
||||||
|
.import-detail-table td {
|
||||||
|
padding: 10px 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table th {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table .status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 4px 9px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table .status-badge.new {
|
||||||
|
background: rgba(117, 190, 255, 0.16);
|
||||||
|
color: #75beff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table .status-badge.update {
|
||||||
|
background: rgba(115, 201, 145, 0.16);
|
||||||
|
color: #73c991;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table .status-badge.conflict {
|
||||||
|
background: rgba(255, 166, 87, 0.16);
|
||||||
|
color: #ffb169;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-detail-table .status-badge.duplicate,
|
||||||
|
.import-detail-table .status-badge.missing {
|
||||||
|
background: rgba(204, 167, 0, 0.16);
|
||||||
|
color: #cca700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categories-cell,
|
||||||
|
.existing-match,
|
||||||
|
.mime-type-cell,
|
||||||
|
.post-type-cell {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mime-type-cell,
|
||||||
|
.post-type-cell,
|
||||||
|
.existing-match,
|
||||||
|
.slug-cell {
|
||||||
|
font-family: var(--vscode-editor-font-family, ui-monospace, monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resolution-select,
|
||||||
|
.taxonomy-mapping-input {
|
||||||
|
min-width: 150px;
|
||||||
|
background: var(--vscode-dropdown-background, var(--vscode-input-background));
|
||||||
|
color: var(--vscode-dropdown-foreground, var(--vscode-foreground));
|
||||||
|
border: 1px solid var(--vscode-dropdown-border, var(--vscode-panel-border));
|
||||||
|
padding: 6px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-analyze-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 0 12px;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-analyze-dropdown {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-analyze-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-model-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 6px);
|
||||||
|
left: 0;
|
||||||
|
min-width: 220px;
|
||||||
|
max-height: 280px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: var(--vscode-dropdown-background, var(--vscode-sideBar-background));
|
||||||
|
border: 1px solid var(--vscode-dropdown-border, var(--vscode-panel-border));
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-model-option {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
color: var(--vscode-foreground) !important;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-model-option:hover {
|
||||||
|
background: var(--vscode-list-hoverBackground) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-analyze-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-taxonomy-groups {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-taxonomy-entry,
|
||||||
|
.import-taxonomy-edit-form {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-taxonomy-entry,
|
||||||
|
.import-taxonomy-edit-form {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-taxonomy-pill {
|
||||||
|
border: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.import-taxonomy-pill {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapped-target {
|
||||||
|
background: rgba(115, 201, 145, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-mapping-arrow {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-mapping-input {
|
||||||
|
min-width: 170px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-edit-btn,
|
||||||
|
.taxonomy-clear-btn {
|
||||||
|
min-width: 28px;
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 0 8px !important;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-edit-btn.ghost,
|
||||||
|
.taxonomy-clear-btn {
|
||||||
|
background: transparent !important;
|
||||||
|
border: 1px solid var(--vscode-panel-border) !important;
|
||||||
|
color: var(--vscode-descriptionForeground) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.macros-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.macro-item {
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.macro-item.unmapped {
|
||||||
|
border-left: 3px solid #cca700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.macro-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.macro-name,
|
||||||
|
.import-taxonomy-pill {
|
||||||
|
font-family: var(--vscode-editor-font-family, ui-monospace, monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.macro-count {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 56px 20px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
border: 1px dashed var(--vscode-panel-border);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-empty-state p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.import-site-info,
|
||||||
|
.import-stat-cards,
|
||||||
|
.import-taxonomy-groups {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 780px) {
|
||||||
|
.import-analysis {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-row,
|
||||||
|
.distribution-row,
|
||||||
|
.import-execute-section,
|
||||||
|
.import-site-info,
|
||||||
|
.import-stat-cards,
|
||||||
|
.import-taxonomy-groups {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-execute-section {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-file-row {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-analysis button,
|
||||||
|
.resolution-select,
|
||||||
|
.taxonomy-mapping-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxonomy-analyze-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-taxonomy-entry,
|
||||||
|
.import-taxonomy-edit-form {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
474
assets/css/media_editor.css
Normal file
474
assets/css/media_editor.css
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
[data-testid="media-editor"] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 12px;
|
||||||
|
min-height: 35px;
|
||||||
|
background-color: var(--vscode-tab-activeBackground);
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-tab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: var(--vscode-tab-inactiveBackground);
|
||||||
|
color: var(--vscode-tab-inactiveForeground);
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-tab.active {
|
||||||
|
background-color: var(--vscode-tab-activeBackground);
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-tab-title {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-tab-dirty {
|
||||||
|
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-actions button {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-actions button.danger:hover {
|
||||||
|
background-color: var(--vscode-notificationsErrorIcon-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .auto-save-indicator {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-actions-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-actions-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-actions-btn-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-actions-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
right: 0;
|
||||||
|
width: 280px;
|
||||||
|
background: var(--vscode-dropdown-background, #252526);
|
||||||
|
border: 1px solid var(--vscode-dropdown-border, #454545);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-actions-divider {
|
||||||
|
height: 1px;
|
||||||
|
background: var(--vscode-dropdown-border, #454545);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-dropdown-foreground, #ccc);
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-item:hover:not(:disabled) {
|
||||||
|
background: var(--vscode-list-hoverBackground, #2a2d2e);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-item:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-text {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-text strong {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .quick-action-text small {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
|
overflow-y: auto;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] > .editor-content.media-editor {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-field label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .editor-field-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-editor-input,
|
||||||
|
[data-testid="media-editor"] .post-editor-textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid var(--vscode-input-border, var(--vscode-panel-border));
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--vscode-input-background, rgba(255, 255, 255, 0.06));
|
||||||
|
color: var(--vscode-input-foreground, var(--vscode-foreground));
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-editor-input.disabled,
|
||||||
|
[data-testid="media-editor"] .post-editor-input:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-editor-textarea {
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-preview {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-preview-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-preview-image {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-self: stretch;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-preview-image img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-details {
|
||||||
|
width: 320px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-editor-details-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .media-details textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-posts-section label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .add-link-btn {
|
||||||
|
background: var(--vscode-button-secondaryBackground);
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .add-link-btn:hover {
|
||||||
|
background: var(--vscode-button-secondaryHoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker {
|
||||||
|
background: var(--vscode-dropdown-background);
|
||||||
|
border: 1px solid var(--vscode-dropdown-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 8px;
|
||||||
|
max-height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-search {
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom: 1px solid var(--vscode-dropdown-border);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: var(--vscode-dropdown-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-search input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-search input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-list {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-item {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-item:hover {
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .post-picker-more {
|
||||||
|
padding: 6px 8px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .no-posts,
|
||||||
|
[data-testid="media-editor"] .no-linked-posts {
|
||||||
|
padding: 12px 8px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-posts-list {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-post-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 8px;
|
||||||
|
background: var(--vscode-sideBar-background);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-post-title,
|
||||||
|
[data-testid="media-editor"] .linked-post-link {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
color: inherit;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-post-title:hover,
|
||||||
|
[data-testid="media-editor"] .linked-post-link:hover {
|
||||||
|
color: var(--vscode-textLink-foreground);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-post-item .unlink-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-post-item:hover .unlink-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-testid="media-editor"] .linked-post-item .unlink-btn:hover {
|
||||||
|
color: var(--vscode-errorForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.68);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal {
|
||||||
|
width: min(640px, calc(100vw - 32px));
|
||||||
|
background: #1e1e1e;
|
||||||
|
border: 1px solid #3c3c3c;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal-header,
|
||||||
|
.translation-modal-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal-header {
|
||||||
|
border-bottom: 1px solid #3c3c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal-footer {
|
||||||
|
border-top: 1px solid #3c3c3c;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal-body {
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-modal-close {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #c5c5c5;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
289
assets/css/menu_editor.css
Normal file
289
assets/css/menu_editor.css
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
.menu-editor-view {
|
||||||
|
padding: 1rem;
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-header p {
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tree-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
padding: 0.5rem;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.2rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
padding-bottom: 0.4rem;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tool {
|
||||||
|
width: 1.8rem;
|
||||||
|
height: 1.8rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tool:hover:not(:disabled) {
|
||||||
|
background: var(--vscode-toolbar-hoverBackground);
|
||||||
|
border-color: var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tool:disabled {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tree-shell {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tree-level {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-tree-item {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row {
|
||||||
|
--menu-editor-indent: calc(var(--menu-editor-depth) * 1rem);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.3rem 0.45rem 0.3rem calc(0.4rem + var(--menu-editor-indent));
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row.is-selected {
|
||||||
|
background: var(--vscode-list-activeSelectionBackground);
|
||||||
|
color: var(--vscode-list-activeSelectionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row.is-dragging {
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row.is-drop-before::before,
|
||||||
|
.menu-editor-row.is-drop-after::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: calc(0.4rem + var(--menu-editor-indent));
|
||||||
|
right: 0.45rem;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row.is-drop-before::before {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row.is-drop-after::after {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row.is-drop-inside {
|
||||||
|
box-shadow: inset 0 0 0 1px var(--vscode-focusBorder);
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row-handle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1rem;
|
||||||
|
min-width: 1rem;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row-handle:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row-kind {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1rem;
|
||||||
|
min-width: 1rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row-title {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-row-title.is-editing {
|
||||||
|
white-space: normal;
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-entry-form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-input {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--vscode-focusBorder);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
padding: 0.25rem 0.45rem;
|
||||||
|
min-height: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-search {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--vscode-panel-border);
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
max-height: 18rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-search-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-search-head strong {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-search-head span {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-action {
|
||||||
|
border: 1px solid var(--vscode-button-border, transparent);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-action:hover {
|
||||||
|
background: var(--vscode-button-secondaryHoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-picker-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.35rem;
|
||||||
|
max-height: 16rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-picker-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
padding: 0.45rem 0.55rem;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-picker-item:hover {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-picker-item small,
|
||||||
|
.menu-editor-picker-state {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-empty {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
padding: 0.5rem 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.menu-editor-inline-search-head {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-editor-inline-actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
336
assets/css/overlays.css
Normal file
336
assets/css/overlays.css
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
.overlay-root {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-root:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-shared-actions {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal-backdrop,
|
||||||
|
.insert-modal-backdrop,
|
||||||
|
.language-picker-modal-backdrop,
|
||||||
|
.confirm-delete-modal-backdrop,
|
||||||
|
.confirm-dialog-overlay,
|
||||||
|
.gallery-overlay,
|
||||||
|
.lightbox-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.68);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal,
|
||||||
|
.insert-modal,
|
||||||
|
.language-picker-modal,
|
||||||
|
.confirm-delete-modal,
|
||||||
|
.confirm-dialog,
|
||||||
|
.gallery-overlay-content {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border: 1px solid #3c3c3c;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal,
|
||||||
|
.language-picker-modal,
|
||||||
|
.confirm-delete-modal,
|
||||||
|
.confirm-dialog {
|
||||||
|
width: min(680px, calc(100vw - 32px));
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal {
|
||||||
|
width: min(680px, calc(100vw - 32px));
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-overlay-content {
|
||||||
|
width: min(980px, calc(100vw - 48px));
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal-header,
|
||||||
|
.language-picker-modal-header,
|
||||||
|
.confirm-delete-modal-header,
|
||||||
|
.insert-modal-header,
|
||||||
|
.gallery-overlay-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #3c3c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-header.media-header-only {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal-header h2,
|
||||||
|
.language-picker-modal-header h2,
|
||||||
|
.confirm-delete-modal-header h2,
|
||||||
|
.gallery-overlay-header h2,
|
||||||
|
.insert-modal-title,
|
||||||
|
.confirm-dialog h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal-close,
|
||||||
|
.confirm-delete-modal-close,
|
||||||
|
.gallery-overlay-close,
|
||||||
|
.shared-popover-close,
|
||||||
|
.lightbox-close {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #c5c5c5;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal-body,
|
||||||
|
.language-picker-modal-body,
|
||||||
|
.confirm-delete-modal-body {
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid #3c3c3c;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #252526;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-checkbox {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-checkbox input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 2px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-checkbox input:checked + .checkmark,
|
||||||
|
.ai-suggestion-checkbox input:checked ~ .checkmark {
|
||||||
|
background: #0078d4;
|
||||||
|
border-color: #0078d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-checkbox input:checked + .checkmark::after,
|
||||||
|
.ai-suggestion-checkbox input:checked ~ .checkmark::after {
|
||||||
|
content: "✓";
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-has-value,
|
||||||
|
.language-picker-badge,
|
||||||
|
.insert-modal-similarity-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
color: #c5c5c5;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-comparison {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-column.muted {
|
||||||
|
color: #9d9d9d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-column.highlighted {
|
||||||
|
border: 1px solid rgba(0, 122, 204, 0.4);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-column-label {
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-arrow {
|
||||||
|
color: #9d9d9d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-value {
|
||||||
|
min-height: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestion-value.loading {
|
||||||
|
color: var(--accent-color);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-error {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(220, 50, 50, 0.12);
|
||||||
|
border: 1px solid rgba(220, 50, 50, 0.35);
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-suggestions-modal-footer,
|
||||||
|
.confirm-delete-modal-footer,
|
||||||
|
.confirm-dialog-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid #3c3c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-cancel,
|
||||||
|
.button-delete,
|
||||||
|
.button-apply,
|
||||||
|
.confirm-dialog-actions button,
|
||||||
|
.insert-modal-submit,
|
||||||
|
.language-picker-row,
|
||||||
|
.shared-popover-entry,
|
||||||
|
.colour-swatch {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-cancel,
|
||||||
|
.confirm-dialog-actions button,
|
||||||
|
.insert-modal-submit {
|
||||||
|
border: 1px solid #4c4c4c;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: transparent;
|
||||||
|
color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-apply,
|
||||||
|
.confirm-dialog-actions .primary,
|
||||||
|
.insert-modal-submit {
|
||||||
|
background: #0e639c;
|
||||||
|
border-color: #0e639c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-delete {
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: #c73c3c;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-tabs {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-tab {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
color: #9d9d9d;
|
||||||
|
padding: 10px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-tab.active {
|
||||||
|
color: #ffffff;
|
||||||
|
border-bottom-color: #0e639c;
|
||||||
|
background: #252526;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-search {
|
||||||
|
border-bottom: 1px solid #3c3c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-input,
|
||||||
|
.shared-popover-input {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #f0f0f0;
|
||||||
|
padding: 14px 20px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
541
assets/css/panel.css
Normal file
541
assets/css/panel.css
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
.panel-shell {
|
||||||
|
min-height: 160px;
|
||||||
|
max-height: 160px;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-shell.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tab {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-tab-inactiveForeground);
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tab:hover {
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tab.active {
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
border-bottom-color: var(--vscode-focusBorder);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-heading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-description,
|
||||||
|
.assistant-sidebar-context-text,
|
||||||
|
.assistant-sidebar-message-content {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-status {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-status.is-offline {
|
||||||
|
background: rgba(255, 196, 0, 0.18);
|
||||||
|
border-color: rgba(255, 196, 0, 0.35);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--vscode-editorWidget-background, rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-label,
|
||||||
|
.assistant-sidebar-message-role {
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-value {
|
||||||
|
text-align: right;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-text,
|
||||||
|
.assistant-sidebar-message-content {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-prompt-form,
|
||||||
|
.assistant-sidebar-welcome,
|
||||||
|
.assistant-sidebar-transcript {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-prompt {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120px;
|
||||||
|
resize: vertical;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
padding: 10px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-prompt:focus {
|
||||||
|
outline: 1px solid var(--vscode-focusBorder);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-start-button {
|
||||||
|
align-self: flex-start;
|
||||||
|
border: 1px solid var(--vscode-button-border, transparent);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
padding: 7px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-start-button:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-card,
|
||||||
|
.assistant-sidebar-message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
background: var(--vscode-editorWidget-background, rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-message.user {
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-message.assistant {
|
||||||
|
background: var(--vscode-editorWidget-background, rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar {
|
||||||
|
height: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--vscode-statusBar-background);
|
||||||
|
color: var(--vscode-statusBar-foreground);
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0 8px;
|
||||||
|
user-select: none;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 0;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-left,
|
||||||
|
.status-bar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 100%;
|
||||||
|
max-width: none;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item .task-message-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-spinner {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-top-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-list {
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-list,
|
||||||
|
.git-log-list {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-entry {
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--vscode-sideBar-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-entry {
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--vscode-sideBar-background);
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.editor-frame {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-shell {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-empty {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content {
|
||||||
|
max-width: 720px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0 0 4px;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content > .text-muted {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: var(--vscode-sideBar-background);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-breakdown {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-tag {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-published {
|
||||||
|
color: var(--vscode-testing-iconPassed);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-draft {
|
||||||
|
color: var(--vscode-editorWarning-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-archived {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-section {
|
||||||
|
background-color: var(--vscode-sideBar-background);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-section h4 {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-chart {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 40px;
|
||||||
|
background-color: var(--vscode-activityBarBadge-background);
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
margin-top: auto;
|
||||||
|
min-height: 4px;
|
||||||
|
position: relative;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar-count {
|
||||||
|
position: absolute;
|
||||||
|
top: -16px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar-label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 9px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
margin-top: 4px;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar-label-month {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-bar-label-year {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-cloud {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px 10px;
|
||||||
|
align-items: baseline;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-tag {
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
cursor: default;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-tag:hover {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-tag.has-color {
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-tag.has-color:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-cloud-more {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-count {
|
||||||
|
font-size: 10px;
|
||||||
|
opacity: 0.5;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-category {
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-posts-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
text-align: left;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-item:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-title {
|
||||||
|
flex: 1;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-status {
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-status.status-published {
|
||||||
|
color: var(--vscode-testing-iconPassed);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-status.status-draft {
|
||||||
|
color: var(--vscode-editorWarning-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-status.status-archived {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-date {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
807
assets/css/shell.css
Normal file
807
assets/css/shell.css
Normal file
@@ -0,0 +1,807 @@
|
|||||||
|
.app {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar {
|
||||||
|
position: relative;
|
||||||
|
height: 34px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--vscode-editorGroupHeader-tabsBackground);
|
||||||
|
border-bottom: 1px solid var(--vscode-editorGroupHeader-tabsBorder);
|
||||||
|
flex-shrink: 0;
|
||||||
|
app-region: drag;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
padding-right: calc(10px + var(--bds-titlebar-overlay-right, 0px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 6px;
|
||||||
|
gap: 2px;
|
||||||
|
app-region: no-drag;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-group {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-bar.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar.is-mac .window-titlebar-menu-bar {
|
||||||
|
margin-left: max(var(--bds-titlebar-macos-left-inset, 78px), calc(6px + var(--bds-titlebar-overlay-left, 0px)));
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-button {
|
||||||
|
height: 24px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-titleBar-activeForeground);
|
||||||
|
padding: 0 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-button:hover,
|
||||||
|
.window-titlebar-action-button:hover {
|
||||||
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-button.is-active {
|
||||||
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-button:focus,
|
||||||
|
.window-titlebar-menu-button:focus-visible,
|
||||||
|
.window-titlebar-action-button:focus,
|
||||||
|
.window-titlebar-action-button:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
left: 0;
|
||||||
|
min-width: 210px;
|
||||||
|
padding: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
background-color: var(--vscode-menu-background, var(--vscode-editorWidget-background));
|
||||||
|
border: 1px solid var(--vscode-menu-border, var(--vscode-panel-border));
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: var(--vscode-widget-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
|
||||||
|
app-region: no-drag;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-menu-foreground, var(--vscode-foreground));
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item:focus,
|
||||||
|
.window-titlebar-menu-item:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item:hover,
|
||||||
|
.window-titlebar-menu-item.is-keyboard-active {
|
||||||
|
background-color: var(--vscode-menu-selectionBackground, var(--vscode-toolbar-hoverBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-item-accelerator {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-menu-separator {
|
||||||
|
height: 1px;
|
||||||
|
margin: 4px 2px;
|
||||||
|
background-color: var(--vscode-menu-separatorBackground, rgba(255, 255, 255, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-drag-region {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-title {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
max-width: 45%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--vscode-titleBar-activeForeground);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-actions {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 6px;
|
||||||
|
app-region: no-drag;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-action-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-sidebar-icon,
|
||||||
|
.window-titlebar-panel-icon,
|
||||||
|
.window-titlebar-assistant-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: 1.5px solid currentColor;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-sidebar-icon::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 33.3333%;
|
||||||
|
width: 1.5px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-panel-icon::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 66.6667%;
|
||||||
|
height: 1.5px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-assistant-icon::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 66.6667%;
|
||||||
|
width: 1.5px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-sidebar-pane,
|
||||||
|
.window-titlebar-panel-pane,
|
||||||
|
.window-titlebar-assistant-pane {
|
||||||
|
position: absolute;
|
||||||
|
background-color: currentColor;
|
||||||
|
transition: opacity 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-sidebar-pane {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 33.3333%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-panel-pane {
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 33.3333%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-assistant-pane {
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 33.3333%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-sidebar-icon.is-inactive .window-titlebar-sidebar-pane,
|
||||||
|
.window-titlebar-panel-icon.is-inactive .window-titlebar-panel-pane,
|
||||||
|
.window-titlebar-assistant-icon.is-inactive .window-titlebar-assistant-pane {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-shell {
|
||||||
|
height: 200px;
|
||||||
|
border-top: 1px solid var(--vscode-panel-border);
|
||||||
|
background: var(--vscode-panel-background);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-shell.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar-button.is-destructive {
|
||||||
|
color: #f48771;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-overlay-backdrop,
|
||||||
|
.gallery-overlay-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.68);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-overlay-dismiss {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-overlay {
|
||||||
|
position: relative;
|
||||||
|
width: min(980px, calc(100vw - 48px));
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #1e1e1e;
|
||||||
|
border: 1px solid #3c3c3c;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-media-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-media-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
border: 1px solid #3c3c3c;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #252526;
|
||||||
|
color: inherit;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-media-thumb {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 112px;
|
||||||
|
border-radius: 6px;
|
||||||
|
object-fit: cover;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-modal-media-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-picker-options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-picker-option {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 28px 1fr auto;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-picker-label,
|
||||||
|
.language-picker-status,
|
||||||
|
.lightbox-counter {
|
||||||
|
color: #9d9d9d;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-counter {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.insert-modal-media-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
height: 35px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 8px;
|
||||||
|
background-color: var(--vscode-sideBar-background);
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tab {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
padding: 0 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-tab.active {
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-close {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 18px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-close:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 12px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-entry,
|
||||||
|
.assistant-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-list,
|
||||||
|
.git-log-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-entry-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-status {
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-status-running {
|
||||||
|
color: var(--vscode-terminal-ansiGreen, var(--vscode-statusBar-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-status-pending {
|
||||||
|
color: var(--vscode-terminal-ansiYellow, var(--vscode-statusBar-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-empty-state {
|
||||||
|
min-height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar {
|
||||||
|
height: 22px;
|
||||||
|
background: var(--vscode-statusBar-background);
|
||||||
|
color: var(--vscode-statusBar-foreground);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-left,
|
||||||
|
.status-bar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-left {
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-shell-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-shell-toggle-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 22px;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-shell-toggle-button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-shell-toggle-button:focus,
|
||||||
|
.status-shell-toggle-button:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-shell-toggle-button .window-titlebar-sidebar-icon,
|
||||||
|
.status-shell-toggle-button .window-titlebar-panel-icon,
|
||||||
|
.status-shell-toggle-button .window-titlebar-assistant-icon {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-task-button {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item.theme-badge {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item.language-badge {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
border-radius: 3px;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item.offline-badge {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.4;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item.offline-badge.active {
|
||||||
|
background-color: rgba(255, 196, 0, 0.28);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-selector {
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-selector-trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 22px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-statusBar-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-selector-trigger:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-selector-trigger:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-icon,
|
||||||
|
.dropdown-arrow,
|
||||||
|
.project-check-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name,
|
||||||
|
.project-item-name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name {
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 100%;
|
||||||
|
min-width: 220px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
background-color: #252526;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-dropdown-header {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-list {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-item:hover,
|
||||||
|
.project-item.active {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-item.active .project-check-icon {
|
||||||
|
color: #89d185;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-dropdown-footer {
|
||||||
|
padding: 8px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-project-btn,
|
||||||
|
.existing-project-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.12);
|
||||||
|
color: inherit;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-project-btn:hover,
|
||||||
|
.existing-project-btn:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-language-select {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-language-select:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-count {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar-item.brand {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.editor-frame {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-meta {
|
||||||
|
border-left: none;
|
||||||
|
border-top: 1px solid var(--vscode-panel-border);
|
||||||
|
padding-left: 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-section ul {
|
||||||
|
margin: 12px 0 0;
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
padding: 9px 14px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--panel-3);
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-meta-card,
|
||||||
|
.assistant-card,
|
||||||
|
.panel-entry {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header,
|
||||||
|
.assistant-header,
|
||||||
|
.panel-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
1049
assets/css/sidebar.css
Normal file
1049
assets/css/sidebar.css
Normal file
File diff suppressed because it is too large
Load Diff
189
assets/css/tabs.css
Normal file
189
assets/css/tabs.css
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
.tab-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--vscode-editorGroupHeader-tabsBackground);
|
||||||
|
border-bottom: 1px solid var(--vscode-editorGroupHeader-tabsBorder);
|
||||||
|
height: 35px;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-tabs::-webkit-scrollbar {
|
||||||
|
height: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar-empty {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 0 6px 0 10px;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 100px;
|
||||||
|
max-width: 180px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--vscode-tab-inactiveBackground);
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid var(--vscode-tab-border);
|
||||||
|
color: var(--vscode-tab-inactiveForeground);
|
||||||
|
font-size: 13px;
|
||||||
|
user-select: none;
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-select {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
cursor: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
background-color: var(--vscode-tab-activeBackground);
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.transient .tab-title {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-dirty-indicator {
|
||||||
|
color: var(--vscode-editorWarning-foreground, #e2c08d);
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-title,
|
||||||
|
.status-bar-item {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-close {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--vscode-icon-foreground, #c5c5c5);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:hover .tab-close {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active .tab-close {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-close:hover {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: var(--vscode-toolbar-hoverBackground);
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-close:active {
|
||||||
|
background-color: var(--vscode-toolbar-activeBackground, rgba(99, 102, 103, 0.31));
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.dirty .tab-dirty-indicator {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.dirty .tab-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.dirty:hover .tab-close {
|
||||||
|
display: flex;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.dirty:hover .tab-dirty-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:focus-visible {
|
||||||
|
outline: 1px solid var(--vscode-focusBorder, #007fd4);
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-item-details {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
color: inherit;
|
||||||
|
font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
148
assets/css/tokens.css
Normal file
148
assets/css/tokens.css
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
:root {
|
||||||
|
--accent-color: #007acc;
|
||||||
|
--accent-color-transparent: rgba(0, 122, 204, 0.25);
|
||||||
|
--vscode-editor-background: #1e1e1e;
|
||||||
|
--vscode-editor-foreground: #cccccc;
|
||||||
|
--vscode-sideBar-background: #252526;
|
||||||
|
--vscode-activityBar-background: #333333;
|
||||||
|
--vscode-activityBar-foreground: #ffffff;
|
||||||
|
--vscode-panel-background: #1e1e1e;
|
||||||
|
--vscode-titleBar-activeBackground: #252526;
|
||||||
|
--vscode-titleBar-activeForeground: #cccccc;
|
||||||
|
--vscode-statusBar-background: #007acc;
|
||||||
|
--vscode-statusBar-foreground: #ffffff;
|
||||||
|
--vscode-tab-activeBackground: #1e1e1e;
|
||||||
|
--vscode-tab-inactiveBackground: #2d2d2d;
|
||||||
|
--vscode-tab-activeForeground: #ffffff;
|
||||||
|
--vscode-tab-inactiveForeground: #969696;
|
||||||
|
--vscode-editorGroupHeader-tabsBackground: #252526;
|
||||||
|
--vscode-editorGroupHeader-tabsBorder: #1e1e1e;
|
||||||
|
--vscode-toolbar-hoverBackground: rgba(90, 93, 94, 0.31);
|
||||||
|
--vscode-toolbar-activeBackground: rgba(99, 102, 103, 0.31);
|
||||||
|
--vscode-foreground: #cccccc;
|
||||||
|
--vscode-descriptionForeground: #858585;
|
||||||
|
--vscode-panel-border: #80808059;
|
||||||
|
--vscode-sideBar-border: #80808059;
|
||||||
|
--vscode-tab-border: #252526;
|
||||||
|
--vscode-focusBorder: #007fd4;
|
||||||
|
--vscode-input-background: rgba(255, 255, 255, 0.06);
|
||||||
|
--vscode-input-border: rgba(255, 255, 255, 0.12);
|
||||||
|
--vscode-list-hoverBackground: #2a2d2e;
|
||||||
|
--vscode-list-activeSelectionBackground: #094771;
|
||||||
|
--vscode-list-activeSelectionForeground: #ffffff;
|
||||||
|
--vscode-activityBarBadge-background: #007acc;
|
||||||
|
--vscode-activityBarBadge-foreground: #ffffff;
|
||||||
|
--vscode-testing-iconPassed: #73c991;
|
||||||
|
--vscode-editorWarning-foreground: #cca700;
|
||||||
|
--vscode-input-foreground: #cccccc;
|
||||||
|
--vscode-input-placeholderForeground: #a6a6a6;
|
||||||
|
--vscode-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
--vscode-font-size: 13px;
|
||||||
|
--panel-1: var(--vscode-editor-background);
|
||||||
|
--panel-2: var(--vscode-sideBar-background);
|
||||||
|
--panel-3: var(--vscode-input-background);
|
||||||
|
--ink: var(--vscode-foreground);
|
||||||
|
--line: var(--vscode-panel-border);
|
||||||
|
--accent: var(--vscode-focusBorder);
|
||||||
|
--accent-soft: var(--vscode-list-hoverBackground);
|
||||||
|
--success: var(--vscode-testing-iconPassed);
|
||||||
|
--sidebar-width: 280px;
|
||||||
|
--assistant-width: 360px;
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
font-family: var(--vscode-font-family);
|
||||||
|
font-size: var(--vscode-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
body > [data-phx-session],
|
||||||
|
body > [data-phx-main] {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: var(--vscode-font-family);
|
||||||
|
font-size: var(--vscode-font-size);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
background-color: var(--vscode-button-background);
|
||||||
|
border: none;
|
||||||
|
padding: 6px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: var(--vscode-button-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
outline: 1px solid var(--vscode-focusBorder);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.secondary {
|
||||||
|
background-color: var(--vscode-button-secondaryBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.secondary:hover {
|
||||||
|
background-color: #4a4d51;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.compact {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.primary {
|
||||||
|
background-color: var(--vscode-button-background);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.primary:hover {
|
||||||
|
background-color: var(--vscode-button-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.success {
|
||||||
|
background-color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.success:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.danger {
|
||||||
|
background-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.danger:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
button svg,
|
||||||
|
button svg * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
0
assets/css/utilities.css
Normal file
0
assets/css/utilities.css
Normal file
@@ -1,3 +1,7 @@
|
|||||||
|
import { Socket } from "phoenix";
|
||||||
|
import { LiveSocket } from "phoenix_live_view";
|
||||||
|
import "phoenix_html";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const csrfToken = document
|
const csrfToken = document
|
||||||
.querySelector("meta[name='csrf-token']")
|
.querySelector("meta[name='csrf-token']")
|
||||||
@@ -298,15 +302,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const colorIsDark = (value) => {
|
const normalizeMonacoColor = (value, fallback) => {
|
||||||
const rgb = parseRgbColor(value);
|
const rgb = parseRgbColor(value);
|
||||||
|
|
||||||
if (!rgb) {
|
if (!rgb) {
|
||||||
return true;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
return `#${[rgb.r, rgb.g, rgb.b]
|
||||||
return luminance < 0.5;
|
.map((channel) => clamp(channel, 0, 255).toString(16).padStart(2, "0"))
|
||||||
|
.join("")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadScript = (src) =>
|
const loadScript = (src) =>
|
||||||
@@ -492,14 +497,27 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ensureMonacoTheme = (monaco) => {
|
const ensureMonacoTheme = (monaco) => {
|
||||||
const background = cssVar("--vscode-editor-background", cssVar("--vscode-input-background", "#1e1e1e"));
|
const background = normalizeMonacoColor(
|
||||||
const foreground = cssVar("--vscode-editor-foreground", "#d4d4d4");
|
cssVar("--vscode-editor-background", cssVar("--vscode-input-background", "#1e1e1e")),
|
||||||
const lineNumber = cssVar("--vscode-editorLineNumber-foreground", "#858585");
|
"#1e1e1e"
|
||||||
const activeLineNumber = cssVar("--vscode-editorLineNumber-activeForeground", foreground);
|
);
|
||||||
const selection = cssVar("--vscode-editor-selectionBackground", "#264f78");
|
const foreground = normalizeMonacoColor(cssVar("--vscode-editor-foreground", "#d4d4d4"), "#d4d4d4");
|
||||||
const inactiveSelection = cssVar("--vscode-editor-inactiveSelectionBackground", "#3a3d41");
|
const lineNumber = normalizeMonacoColor(cssVar("--vscode-editorLineNumber-foreground", "#858585"), "#858585");
|
||||||
const cursor = cssVar("--vscode-editorCursor-foreground", foreground);
|
const activeLineNumber = normalizeMonacoColor(
|
||||||
const border = cssVar("--vscode-panel-border", "#3c3c3c");
|
cssVar("--vscode-editorLineNumber-activeForeground", foreground),
|
||||||
|
foreground
|
||||||
|
);
|
||||||
|
const selection = normalizeMonacoColor(cssVar("--vscode-editor-selectionBackground", "#264f78"), "#264f78");
|
||||||
|
const inactiveSelection = normalizeMonacoColor(
|
||||||
|
cssVar("--vscode-editor-inactiveSelectionBackground", "#3a3d41"),
|
||||||
|
"#3a3d41"
|
||||||
|
);
|
||||||
|
const cursor = normalizeMonacoColor(cssVar("--vscode-editorCursor-foreground", foreground), foreground);
|
||||||
|
const border = normalizeMonacoColor(cssVar("--vscode-panel-border", "#3c3c3c"), "#3c3c3c");
|
||||||
|
const lineHighlight = normalizeMonacoColor(
|
||||||
|
cssVar("--vscode-editor-lineHighlightBackground", background),
|
||||||
|
background
|
||||||
|
);
|
||||||
const signature = [background, foreground, lineNumber, activeLineNumber, selection, inactiveSelection, cursor, border].join("|");
|
const signature = [background, foreground, lineNumber, activeLineNumber, selection, inactiveSelection, cursor, border].join("|");
|
||||||
|
|
||||||
if (signature === monacoThemeSignature) {
|
if (signature === monacoThemeSignature) {
|
||||||
@@ -508,7 +526,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
monaco.editor.defineTheme("bds-theme", {
|
monaco.editor.defineTheme("bds-theme", {
|
||||||
base: colorIsDark(background) ? "vs-dark" : "vs",
|
base: "vs-dark",
|
||||||
inherit: true,
|
inherit: true,
|
||||||
rules: [
|
rules: [
|
||||||
{ token: "keyword.macro", foreground: "C586C0", fontStyle: "bold" },
|
{ token: "keyword.macro", foreground: "C586C0", fontStyle: "bold" },
|
||||||
@@ -518,7 +536,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
colors: {
|
colors: {
|
||||||
"editor.background": background,
|
"editor.background": background,
|
||||||
"editor.foreground": foreground,
|
"editor.foreground": foreground,
|
||||||
"editor.lineHighlightBackground": cssVar("--vscode-editor-lineHighlightBackground", background),
|
"editor.lineHighlightBackground": lineHighlight,
|
||||||
"editorCursor.foreground": cursor,
|
"editorCursor.foreground": cursor,
|
||||||
"editor.selectionBackground": selection,
|
"editor.selectionBackground": selection,
|
||||||
"editor.inactiveSelectionBackground": inactiveSelection,
|
"editor.inactiveSelectionBackground": inactiveSelection,
|
||||||
@@ -549,11 +567,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return monacoLoaderPromise;
|
return monacoLoaderPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
monacoLoaderPromise = loadScript("/assets/monaco/vs/loader.js")
|
monacoLoaderPromise = loadScript("/monaco/vs/loader.js")
|
||||||
.then(
|
.then(
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
window.require.config({ paths: { vs: "/assets/monaco/vs" } });
|
window.require.config({ paths: { vs: "/monaco/vs" } });
|
||||||
window.require(["vs/editor/editor.main"], () => {
|
window.require(["vs/editor/editor.main"], () => {
|
||||||
ensureMonacoTheme(window.monaco);
|
ensureMonacoTheme(window.monaco);
|
||||||
registerLiquidLanguage(window.monaco);
|
registerLiquidLanguage(window.monaco);
|
||||||
@@ -1146,6 +1164,84 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
this.isApplyingRemoteUpdate = false;
|
this.isApplyingRemoteUpdate = false;
|
||||||
this.lastKnownValue = this.textarea?.value || "";
|
this.lastKnownValue = this.textarea?.value || "";
|
||||||
|
|
||||||
|
this.syncEditorFromTextarea = () => {
|
||||||
|
this.textarea = document.getElementById(this.el.dataset.monacoInputId) || this.el.querySelector("textarea");
|
||||||
|
|
||||||
|
if (!this.textarea || !this.editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = this.textarea.value || "";
|
||||||
|
|
||||||
|
if (this.editor.getValue() !== value) {
|
||||||
|
this.isApplyingRemoteUpdate = true;
|
||||||
|
this.editor.setValue(value);
|
||||||
|
this.isApplyingRemoteUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastKnownValue = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.layoutEditorSoon = () => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (!this.editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor.layout();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.waitForMonacoVisibleSize = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
let settled = false;
|
||||||
|
let attempts = 0;
|
||||||
|
|
||||||
|
const hasVisibleSize = () => {
|
||||||
|
const rect = this.host?.getBoundingClientRect();
|
||||||
|
return Boolean(rect && rect.width > 0 && rect.height > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const finish = () => {
|
||||||
|
if (settled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settled = true;
|
||||||
|
this.visibleSizeObserver?.disconnect();
|
||||||
|
this.visibleSizeObserver = null;
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const check = () => {
|
||||||
|
if (hasVisibleSize() || attempts >= 20) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts += 1;
|
||||||
|
window.requestAnimationFrame(check);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasVisibleSize()) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.ResizeObserver && this.host) {
|
||||||
|
this.visibleSizeObserver = new ResizeObserver(() => {
|
||||||
|
if (hasVisibleSize()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.visibleSizeObserver.observe(this.host);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.requestAnimationFrame(check);
|
||||||
|
});
|
||||||
|
|
||||||
this.queueSync = () => {
|
this.queueSync = () => {
|
||||||
if (!this.textarea || !this.editor) {
|
if (!this.textarea || !this.editor) {
|
||||||
return;
|
return;
|
||||||
@@ -1200,11 +1296,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadMonaco()
|
loadMonaco()
|
||||||
.then((monaco) => {
|
.then(async (monaco) => {
|
||||||
if (!this.host || !this.textarea) {
|
if (!this.host || !this.textarea) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.waitForMonacoVisibleSize();
|
||||||
|
|
||||||
ensureMonacoTheme(monaco);
|
ensureMonacoTheme(monaco);
|
||||||
|
|
||||||
this.editor = monaco.editor.create(this.host, {
|
this.editor = monaco.editor.create(this.host, {
|
||||||
@@ -1231,6 +1329,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
monacoEditors.set(this.editorId || this.el.id, this.editor);
|
monacoEditors.set(this.editorId || this.el.id, this.editor);
|
||||||
|
monaco.editor.setTheme("bds-theme");
|
||||||
|
this.syncEditorFromTextarea();
|
||||||
|
this.layoutEditorSoon();
|
||||||
|
|
||||||
this.changeSubscription = this.editor.onDidChangeModelContent(() => {
|
this.changeSubscription = this.editor.onDidChangeModelContent(() => {
|
||||||
if (this.isApplyingRemoteUpdate) {
|
if (this.isApplyingRemoteUpdate) {
|
||||||
@@ -1270,17 +1371,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
this.editor.updateOptions({ wordWrap: this.wordWrap });
|
this.editor.updateOptions({ wordWrap: this.wordWrap });
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.editor.getValue() !== this.textarea.value && this.lastKnownValue !== this.textarea.value) {
|
this.syncEditorFromTextarea();
|
||||||
this.isApplyingRemoteUpdate = true;
|
this.layoutEditorSoon();
|
||||||
this.editor.setValue(this.textarea.value);
|
|
||||||
this.isApplyingRemoteUpdate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastKnownValue = this.textarea.value;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyed() {
|
destroyed() {
|
||||||
window.clearTimeout(this.syncTimer);
|
window.clearTimeout(this.syncTimer);
|
||||||
|
this.visibleSizeObserver?.disconnect();
|
||||||
this.changeSubscription?.dispose();
|
this.changeSubscription?.dispose();
|
||||||
monacoEditors.delete(this.editorId || this.el.id);
|
monacoEditors.delete(this.editorId || this.el.id);
|
||||||
this.editor?.dispose();
|
this.editor?.dispose();
|
||||||
@@ -1416,7 +1513,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const liveSocket = new LiveView.LiveSocket("/live", Phoenix.Socket, {
|
const liveSocket = new LiveSocket("/live", Socket, {
|
||||||
params: { _csrf_token: csrfToken },
|
params: { _csrf_token: csrfToken },
|
||||||
hooks: Hooks,
|
hooks: Hooks,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -27,6 +27,31 @@ config :bds, BDS.Desktop.Endpoint,
|
|||||||
pubsub_server: BDS.PubSub,
|
pubsub_server: BDS.PubSub,
|
||||||
live_view: [signing_salt: "desktop-live-view"]
|
live_view: [signing_salt: "desktop-live-view"]
|
||||||
|
|
||||||
|
config :tailwind,
|
||||||
|
version: "4.1.14",
|
||||||
|
default: [
|
||||||
|
cd: Path.expand("..", __DIR__),
|
||||||
|
args: ~w(
|
||||||
|
--input=assets/css/app.css
|
||||||
|
--output=priv/static/assets/app.css
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
config :esbuild,
|
||||||
|
version: "0.25.4",
|
||||||
|
default: [
|
||||||
|
cd: Path.expand("../assets", __DIR__),
|
||||||
|
args: ~w(
|
||||||
|
js/app.js
|
||||||
|
--bundle
|
||||||
|
--target=es2022
|
||||||
|
--outdir=../priv/static/assets
|
||||||
|
--external:/fonts/*
|
||||||
|
--external:/images/*
|
||||||
|
),
|
||||||
|
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
|
||||||
|
]
|
||||||
|
|
||||||
config :bds, :scripting,
|
config :bds, :scripting,
|
||||||
runtime: BDS.Scripting.Lua,
|
runtime: BDS.Scripting.Lua,
|
||||||
timeout: 300_000,
|
timeout: 300_000,
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :bds, BDS.Repo, pool_size: 5
|
config :bds, BDS.Repo, pool_size: 5
|
||||||
|
|
||||||
|
config :bds, BDS.Desktop.Endpoint,
|
||||||
|
watchers: [
|
||||||
|
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]},
|
||||||
|
esbuild: {Esbuild, :install_and_run, [:default, ~w(--watch)]}
|
||||||
|
]
|
||||||
|
|||||||
@@ -16,20 +16,14 @@ defmodule BDS.Desktop.Endpoint do
|
|||||||
|
|
||||||
plug(Plug.Static,
|
plug(Plug.Static,
|
||||||
at: "/assets",
|
at: "/assets",
|
||||||
from: {:bds, "priv/ui"},
|
from: {:bds, "priv/static/assets"},
|
||||||
only: ["app.css", "live.js", "monaco"]
|
only: ["app.css", "app.js"]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Plug.Static,
|
plug(Plug.Static,
|
||||||
at: "/vendor/phoenix",
|
at: "/monaco",
|
||||||
from: {:phoenix, "priv/static"},
|
from: {:bds, "priv/ui/monaco"},
|
||||||
only: ["phoenix.min.js"]
|
only: ["vs"]
|
||||||
)
|
|
||||||
|
|
||||||
plug(Plug.Static,
|
|
||||||
at: "/vendor/live_view",
|
|
||||||
from: {:phoenix_live_view, "priv/static"},
|
|
||||||
only: ["phoenix_live_view.min.js"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(BDS.Desktop.Router)
|
plug(BDS.Desktop.Router)
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ defmodule BDS.Desktop.Layouts do
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<%= @inner_content %>
|
<%= @inner_content %>
|
||||||
<script defer phx-track-static src="/vendor/phoenix/phoenix.min.js"></script>
|
<script defer phx-track-static src="/assets/app.js"></script>
|
||||||
<script defer phx-track-static src="/vendor/live_view/phoenix_live_view.min.js"></script>
|
|
||||||
<script defer phx-track-static src="/assets/live.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1251,7 +1251,9 @@ defmodule BDS.Scripting.ApiDocs do
|
|||||||
"",
|
"",
|
||||||
"**Module APIs**",
|
"**Module APIs**",
|
||||||
"",
|
"",
|
||||||
Enum.map(methods, fn method -> "- [#{method.module}.#{method.name}](##{method.module}#{method.name})" end),
|
Enum.map(methods, fn method ->
|
||||||
|
"- [#{method.module}.#{method.name}](##{method.module}#{method.name})"
|
||||||
|
end),
|
||||||
"",
|
"",
|
||||||
Enum.map(methods, &render_method/1),
|
Enum.map(methods, &render_method/1),
|
||||||
"[↑ Back to Table of contents](#table-of-contents)",
|
"[↑ Back to Table of contents](#table-of-contents)",
|
||||||
@@ -1342,23 +1344,56 @@ defmodule BDS.Scripting.ApiDocs do
|
|||||||
|
|
||||||
defp example_argument_value(name, "string") do
|
defp example_argument_value(name, "string") do
|
||||||
case name do
|
case name do
|
||||||
"id" -> "\"id-1\""
|
"id" ->
|
||||||
suffix when suffix in ["post_id", "media_id", "project_id", "tag_id", "target_tag_id"] -> "\"id-1\""
|
"\"id-1\""
|
||||||
"source_tag_ids" -> "{\"id-1\", \"id-2\"}"
|
|
||||||
"language" -> "\"en\""
|
suffix when suffix in ["post_id", "media_id", "project_id", "tag_id", "target_tag_id"] ->
|
||||||
"status" -> "\"draft\""
|
"\"id-1\""
|
||||||
"kind" -> "\"post\""
|
|
||||||
"slug" -> "\"example-slug\""
|
"source_tag_ids" ->
|
||||||
"title" -> "\"Example Title\""
|
"{\"id-1\", \"id-2\"}"
|
||||||
"name" -> "\"Example Name\""
|
|
||||||
"query" -> "\"example query\""
|
"language" ->
|
||||||
"content" -> "\"Example content\""
|
"\"en\""
|
||||||
"message" -> "\"Update content\""
|
|
||||||
"folder_path" -> "\"/Users/me/Sites/example\""
|
"status" ->
|
||||||
"source_path" -> "\"/Users/me/Pictures/example.jpg\""
|
"\"draft\""
|
||||||
"item_path" -> "\"/Users/me/Sites/example/output/index.html\""
|
|
||||||
"action" -> "\"save\""
|
"kind" ->
|
||||||
_ -> "\"value\""
|
"\"post\""
|
||||||
|
|
||||||
|
"slug" ->
|
||||||
|
"\"example-slug\""
|
||||||
|
|
||||||
|
"title" ->
|
||||||
|
"\"Example Title\""
|
||||||
|
|
||||||
|
"name" ->
|
||||||
|
"\"Example Name\""
|
||||||
|
|
||||||
|
"query" ->
|
||||||
|
"\"example query\""
|
||||||
|
|
||||||
|
"content" ->
|
||||||
|
"\"Example content\""
|
||||||
|
|
||||||
|
"message" ->
|
||||||
|
"\"Update content\""
|
||||||
|
|
||||||
|
"folder_path" ->
|
||||||
|
"\"/Users/me/Sites/example\""
|
||||||
|
|
||||||
|
"source_path" ->
|
||||||
|
"\"/Users/me/Pictures/example.jpg\""
|
||||||
|
|
||||||
|
"item_path" ->
|
||||||
|
"\"/Users/me/Sites/example/output/index.html\""
|
||||||
|
|
||||||
|
"action" ->
|
||||||
|
"\"save\""
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
"\"value\""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1397,10 +1432,17 @@ defmodule BDS.Scripting.ApiDocs do
|
|||||||
|
|
||||||
defp example_response_value(returns) do
|
defp example_response_value(returns) do
|
||||||
cond do
|
cond do
|
||||||
returns == "nil" -> nil
|
returns == "nil" ->
|
||||||
nullable_return?(returns) -> {:nullable, example_response_value(non_nil_return(returns))}
|
nil
|
||||||
String.ends_with?(returns, "[]") -> [example_value_for_type(String.trim_trailing(returns, "[]"))]
|
|
||||||
true -> example_value_for_type(returns)
|
nullable_return?(returns) ->
|
||||||
|
{:nullable, example_response_value(non_nil_return(returns))}
|
||||||
|
|
||||||
|
String.ends_with?(returns, "[]") ->
|
||||||
|
[example_value_for_type(String.trim_trailing(returns, "[]"))]
|
||||||
|
|
||||||
|
true ->
|
||||||
|
example_value_for_type(returns)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1420,8 +1462,11 @@ defmodule BDS.Scripting.ApiDocs do
|
|||||||
|
|
||||||
defp example_value_for_type(type) do
|
defp example_value_for_type(type) do
|
||||||
case Enum.find(@data_structures, &(&1.name == type)) do
|
case Enum.find(@data_structures, &(&1.name == type)) do
|
||||||
nil -> [{"key", "value"}]
|
nil ->
|
||||||
structure -> Enum.map(structure.fields, fn field -> {field.name, example_field_value(field.type)} end)
|
[{"key", "value"}]
|
||||||
|
|
||||||
|
structure ->
|
||||||
|
Enum.map(structure.fields, fn field -> {field.name, example_field_value(field.type)} end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1442,7 +1487,10 @@ defmodule BDS.Scripting.ApiDocs do
|
|||||||
defp render_lua_value(false, _indent), do: "false"
|
defp render_lua_value(false, _indent), do: "false"
|
||||||
defp render_lua_value(nil, _indent), do: "nil"
|
defp render_lua_value(nil, _indent), do: "nil"
|
||||||
defp render_lua_value(value, _indent) when is_integer(value), do: Integer.to_string(value)
|
defp render_lua_value(value, _indent) when is_integer(value), do: Integer.to_string(value)
|
||||||
defp render_lua_value(value, _indent) when is_float(value), do: :erlang.float_to_binary(value, [:compact])
|
|
||||||
|
defp render_lua_value(value, _indent) when is_float(value),
|
||||||
|
do: :erlang.float_to_binary(value, [:compact])
|
||||||
|
|
||||||
defp render_lua_value(value, _indent) when is_binary(value), do: inspect(value)
|
defp render_lua_value(value, _indent) when is_binary(value), do: inspect(value)
|
||||||
|
|
||||||
defp render_lua_value([], _indent), do: "{}"
|
defp render_lua_value([], _indent), do: "{}"
|
||||||
|
|||||||
5
mix.exs
5
mix.exs
@@ -36,6 +36,8 @@ defmodule BDS.MixProject do
|
|||||||
{:image, "~> 0.65"},
|
{:image, "~> 0.65"},
|
||||||
{:stemex, "~> 0.2.1"},
|
{:stemex, "~> 0.2.1"},
|
||||||
{:gettext, "~> 0.24"},
|
{:gettext, "~> 0.24"},
|
||||||
|
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev},
|
||||||
|
{:esbuild, "~> 0.10", runtime: Mix.env() == :dev},
|
||||||
{:lazy_html, ">= 0.1.0", only: :test},
|
{:lazy_html, ">= 0.1.0", only: :test},
|
||||||
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
|
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
|
||||||
]
|
]
|
||||||
@@ -46,6 +48,9 @@ defmodule BDS.MixProject do
|
|||||||
setup: ["deps.get", "ecto.setup"],
|
setup: ["deps.get", "ecto.setup"],
|
||||||
"ecto.setup": ["ecto.create", "ecto.migrate"],
|
"ecto.setup": ["ecto.create", "ecto.migrate"],
|
||||||
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
||||||
|
"assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
|
||||||
|
"assets.build": ["tailwind default", "esbuild default"],
|
||||||
|
"assets.deploy": ["tailwind default --minify", "esbuild default --minify"],
|
||||||
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
|
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
|
||||||
validate: ["test", "dialyzer"]
|
validate: ["test", "dialyzer"]
|
||||||
]
|
]
|
||||||
|
|||||||
2
mix.lock
2
mix.lock
@@ -15,6 +15,7 @@
|
|||||||
"ecto_sqlite3": {:hex, :ecto_sqlite3, "0.22.0", "edab2d0f701b7dd05dcf7e2d97769c106aff62b5cfddc000d1dd6f46b9cbd8c3", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "5af9e031bffcc5da0b7bca90c271a7b1e7c04a93fecf7f6cd35bc1b1921a64bd"},
|
"ecto_sqlite3": {:hex, :ecto_sqlite3, "0.22.0", "edab2d0f701b7dd05dcf7e2d97769c106aff62b5cfddc000d1dd6f46b9cbd8c3", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "5af9e031bffcc5da0b7bca90c271a7b1e7c04a93fecf7f6cd35bc1b1921a64bd"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
|
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
|
||||||
"erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"},
|
"erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"},
|
||||||
|
"esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"},
|
||||||
"ex_dbus": {:hex, :ex_dbus, "0.1.4", "053df83d45b27ba0b9b6ef55a47253922069a3ace12a2a7dd30d3aff58301e17", [:mix], [{:dbus, "~> 0.8.0", [hex: :dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "d8baeaf465eab57b70a47b70e29fdfef6eb09ba110fc37176eebe6ac7874d6d5"},
|
"ex_dbus": {:hex, :ex_dbus, "0.1.4", "053df83d45b27ba0b9b6ef55a47253922069a3ace12a2a7dd30d3aff58301e17", [:mix], [{:dbus, "~> 0.8.0", [hex: :dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "d8baeaf465eab57b70a47b70e29fdfef6eb09ba110fc37176eebe6ac7874d6d5"},
|
||||||
"ex_sni": {:hex, :ex_sni, "0.2.9", "81f9421035dd3edb6d69f1a4dd5f53c7071b41628130d32ba5ab7bb4bfdc2da0", [:mix], [{:debouncer, "~> 0.1", [hex: :debouncer, repo: "hexpm", optional: false]}, {:ex_dbus, "~> 0.1", [hex: :ex_dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "921d67d913765ed20ea8354fd1798dabc957bf66990a6842d6aaa7cd5ee5bc06"},
|
"ex_sni": {:hex, :ex_sni, "0.2.9", "81f9421035dd3edb6d69f1a4dd5f53c7071b41628130d32ba5ab7bb4bfdc2da0", [:mix], [{:debouncer, "~> 0.1", [hex: :debouncer, repo: "hexpm", optional: false]}, {:ex_dbus, "~> 0.1", [hex: :ex_dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "921d67d913765ed20ea8354fd1798dabc957bf66990a6842d6aaa7cd5ee5bc06"},
|
||||||
"ex_stemmers": {:hex, :ex_stemmers, "0.1.0", "63a84ae3a6f0c28a1d75768411f0ae15cfe8462fb70589b60977aa1b04c9372d", [:mix], [{:rustler, "~> 0.32.1", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "498826e2188e502f41d1a15f3d90e7738f0d94747e197367f03a2a44c09167c0"},
|
"ex_stemmers": {:hex, :ex_stemmers, "0.1.0", "63a84ae3a6f0c28a1d75768411f0ae15cfe8462fb70589b60977aa1b04c9372d", [:mix], [{:rustler, "~> 0.32.1", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "498826e2188e502f41d1a15f3d90e7738f0d94747e197367f03a2a44c09167c0"},
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
"saxy": {:hex, :saxy, "1.4.0", "c7203ad20001f72eaaad07d08f82be063fa94a40924e6bb39d93d55f979abcba", [:mix], [], "hexpm", "3fe790354d3f2234ad0b5be2d99822a23fa2d4e8ccd6657c672901dac172e9a9"},
|
"saxy": {:hex, :saxy, "1.4.0", "c7203ad20001f72eaaad07d08f82be063fa94a40924e6bb39d93d55f979abcba", [:mix], [], "hexpm", "3fe790354d3f2234ad0b5be2d99822a23fa2d4e8ccd6657c672901dac172e9a9"},
|
||||||
"stemex": {:hex, :stemex, "0.2.1", "47017c6b10cdd6926a0d523ccf1f801c5f3faf5a0a9c862f49304e07f9b5584f", [:mix], [], "hexpm", "dbfc76d27adfa31d831d183979c595942884e6530a4496714aa5b70d0964c2e4"},
|
"stemex": {:hex, :stemex, "0.2.1", "47017c6b10cdd6926a0d523ccf1f801c5f3faf5a0a9c862f49304e07f9b5584f", [:mix], [], "hexpm", "dbfc76d27adfa31d831d183979c595942884e6530a4496714aa5b70d0964c2e4"},
|
||||||
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
|
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
|
||||||
|
"tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"},
|
||||||
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
|
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
|
||||||
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
||||||
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
||||||
|
|||||||
2
priv/static/assets/app.css
Normal file
2
priv/static/assets/app.css
Normal file
File diff suppressed because one or more lines are too long
26
priv/static/assets/app.js
Normal file
26
priv/static/assets/app.js
Normal file
File diff suppressed because one or more lines are too long
8527
priv/ui/app.css
8527
priv/ui/app.css
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,27 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
import Phoenix.LiveViewTest
|
import Phoenix.LiveViewTest
|
||||||
|
|
||||||
@shell_live_source_root Path.expand("../../../lib/bds/desktop/shell_live", __DIR__)
|
@shell_live_source_root Path.expand("../../../lib/bds/desktop/shell_live", __DIR__)
|
||||||
|
@css_source_files [
|
||||||
|
"tokens.css",
|
||||||
|
"shell.css",
|
||||||
|
"sidebar.css",
|
||||||
|
"tabs.css",
|
||||||
|
"editor.css",
|
||||||
|
"forms.css",
|
||||||
|
"panel.css",
|
||||||
|
"assistant.css",
|
||||||
|
"overlays.css",
|
||||||
|
"menu_editor.css",
|
||||||
|
"media_editor.css",
|
||||||
|
"import_editor.css",
|
||||||
|
"utilities.css"
|
||||||
|
]
|
||||||
|
|
||||||
|
defp desktop_css_source do
|
||||||
|
@css_source_files
|
||||||
|
|> Enum.map(&File.read!(Path.expand("../../../assets/css/#{&1}", __DIR__)))
|
||||||
|
|> Enum.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
test "shell live modules use contexts instead of direct Repo.get calls" do
|
test "shell live modules use contexts instead of direct Repo.get calls" do
|
||||||
source_files =
|
source_files =
|
||||||
@@ -3264,7 +3285,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~ ~s(class="chat-model-selector-button chat-model-selector-inline")
|
assert html =~ ~s(class="chat-model-selector-button chat-model-selector-inline")
|
||||||
refute html =~ ~s(class="chat-panel-header-actions")
|
refute html =~ ~s(class="chat-panel-header-actions")
|
||||||
|
|
||||||
css = File.read!(Path.expand("../../../priv/ui/app.css", __DIR__))
|
css = desktop_css_source()
|
||||||
assert css =~ ".chat-model-selector-wrap"
|
assert css =~ ".chat-model-selector-wrap"
|
||||||
assert css =~ "left: 0;"
|
assert css =~ "left: 0;"
|
||||||
assert css =~ "right: auto;"
|
assert css =~ "right: auto;"
|
||||||
@@ -3313,7 +3334,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert selector_html =~ ~s(data-testid="chat-model-selector-option")
|
assert selector_html =~ ~s(data-testid="chat-model-selector-option")
|
||||||
assert selector_html =~ "llama-current"
|
assert selector_html =~ "llama-current"
|
||||||
|
|
||||||
css = File.read!(Path.expand("../../../priv/ui/app.css", __DIR__))
|
css = desktop_css_source()
|
||||||
assert css =~ ".chat-panel-title {"
|
assert css =~ ".chat-panel-title {"
|
||||||
assert css =~ "overflow: visible;"
|
assert css =~ "overflow: visible;"
|
||||||
|
|
||||||
@@ -3628,7 +3649,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "chat editor hook reopens server-expanded A2UI surfaces after patches" do
|
test "chat editor hook reopens server-expanded A2UI surfaces after patches" do
|
||||||
live_js = File.read!(Path.expand("../../../priv/ui/live.js", __DIR__))
|
live_js = File.read!(Path.expand("../../../assets/js/app.js", __DIR__))
|
||||||
|
|
||||||
chat_editor =
|
chat_editor =
|
||||||
File.read!(Path.expand("../../../lib/bds/desktop/shell_live/chat_editor.ex", __DIR__))
|
File.read!(Path.expand("../../../lib/bds/desktop/shell_live/chat_editor.ex", __DIR__))
|
||||||
@@ -3738,7 +3759,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~
|
assert html =~
|
||||||
~s(<div class="chat-message-text chat-user-message-text" data-testid="chat-user-message-text">wie viele Posts sind im Blog?</div>)
|
~s(<div class="chat-message-text chat-user-message-text" data-testid="chat-user-message-text">wie viele Posts sind im Blog?</div>)
|
||||||
|
|
||||||
css = File.read!(Path.expand("../../../priv/ui/app.css", __DIR__))
|
css = desktop_css_source()
|
||||||
assert css =~ ".chat-panel .chat-message.user .chat-message-content"
|
assert css =~ ".chat-panel .chat-message.user .chat-message-content"
|
||||||
assert css =~ "background: transparent;"
|
assert css =~ "background: transparent;"
|
||||||
assert css =~ "border: 0;"
|
assert css =~ "border: 0;"
|
||||||
@@ -3762,7 +3783,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~ ~s(rows="1")
|
assert html =~ ~s(rows="1")
|
||||||
assert html =~ ~s(class="chat-input chat-surface-input")
|
assert html =~ ~s(class="chat-input chat-surface-input")
|
||||||
|
|
||||||
css = File.read!(Path.expand("../../../priv/ui/app.css", __DIR__))
|
css = desktop_css_source()
|
||||||
assert css =~ "--chat-input-line-height: 20px;"
|
assert css =~ "--chat-input-line-height: 20px;"
|
||||||
assert css =~ "--chat-input-min-height: 20px;"
|
assert css =~ "--chat-input-min-height: 20px;"
|
||||||
assert css =~ ".chat-panel .chat-input-container"
|
assert css =~ ".chat-panel .chat-input-container"
|
||||||
@@ -3784,7 +3805,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert css =~ "max-height: 22px;"
|
assert css =~ "max-height: 22px;"
|
||||||
assert css =~ "padding: 0;"
|
assert css =~ "padding: 0;"
|
||||||
|
|
||||||
live_js = File.read!(Path.expand("../../../priv/ui/live.js", __DIR__))
|
live_js = File.read!(Path.expand("../../../assets/js/app.js", __DIR__))
|
||||||
|
|
||||||
assert live_js =~
|
assert live_js =~
|
||||||
"minHeight = parseFloat(styles.getPropertyValue(\"--chat-input-min-height\"))"
|
"minHeight = parseFloat(styles.getPropertyValue(\"--chat-input-min-height\"))"
|
||||||
|
|||||||
@@ -171,7 +171,9 @@ defmodule BDS.DesktopTest do
|
|||||||
|
|
||||||
test "desktop external links point at the bDS2 GitHub project and issue tracker" do
|
test "desktop external links point at the bDS2 GitHub project and issue tracker" do
|
||||||
assert BDS.Desktop.ExternalLinks.github_url() == "https://github.com/rfc1437/bDS2"
|
assert BDS.Desktop.ExternalLinks.github_url() == "https://github.com/rfc1437/bDS2"
|
||||||
assert BDS.Desktop.ExternalLinks.github_issues_url() == "https://github.com/rfc1437/bDS2/issues"
|
|
||||||
|
assert BDS.Desktop.ExternalLinks.github_issues_url() ==
|
||||||
|
"https://github.com/rfc1437/bDS2/issues"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "icon menu quit requests app-owned shutdown" do
|
test "icon menu quit requests app-owned shutdown" do
|
||||||
@@ -254,7 +256,7 @@ defmodule BDS.DesktopTest do
|
|||||||
assert_receive :window_quit_requested
|
assert_receive :window_quit_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
test "desktop root html is a LiveView shell and references only the live bootstrap assets" do
|
test "desktop root html is a LiveView shell and references the generated asset entrypoints" do
|
||||||
conn = conn(:get, "/?k=#{Desktop.Auth.login_key()}")
|
conn = conn(:get, "/?k=#{Desktop.Auth.login_key()}")
|
||||||
conn = BDS.Desktop.Endpoint.call(conn, BDS.Desktop.Endpoint.init([]))
|
conn = BDS.Desktop.Endpoint.call(conn, BDS.Desktop.Endpoint.init([]))
|
||||||
|
|
||||||
@@ -270,9 +272,24 @@ defmodule BDS.DesktopTest do
|
|||||||
assert conn.resp_body =~ ~s(class="sidebar")
|
assert conn.resp_body =~ ~s(class="sidebar")
|
||||||
assert conn.resp_body =~ ~s(class="status-bar")
|
assert conn.resp_body =~ ~s(class="status-bar")
|
||||||
assert conn.resp_body =~ ~s(data-phx-main)
|
assert conn.resp_body =~ ~s(data-phx-main)
|
||||||
assert conn.resp_body =~ ~s(src="/assets/live.js")
|
|
||||||
assert conn.resp_body =~ ~s(href="/assets/app.css")
|
assert conn.resp_body =~ ~s(href="/assets/app.css")
|
||||||
refute conn.resp_body =~ ~s(src="/assets/app.js")
|
assert conn.resp_body =~ ~s(src="/assets/app.js")
|
||||||
|
refute conn.resp_body =~ ~s(src="/assets/live.js")
|
||||||
|
refute conn.resp_body =~ ~s(src="/vendor/phoenix/phoenix.min.js")
|
||||||
|
refute conn.resp_body =~ ~s(src="/vendor/live_view/phoenix_live_view.min.js")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "desktop endpoint serves generated Phoenix-style CSS and JS assets" do
|
||||||
|
css_conn = conn(:get, "/assets/app.css?k=#{Desktop.Auth.login_key()}")
|
||||||
|
css_conn = BDS.Desktop.Endpoint.call(css_conn, BDS.Desktop.Endpoint.init([]))
|
||||||
|
|
||||||
|
js_conn = conn(:get, "/assets/app.js?k=#{Desktop.Auth.login_key()}")
|
||||||
|
js_conn = BDS.Desktop.Endpoint.call(js_conn, BDS.Desktop.Endpoint.init([]))
|
||||||
|
|
||||||
|
assert css_conn.status == 200
|
||||||
|
assert byte_size(css_conn.resp_body) > 0
|
||||||
|
assert js_conn.status == 200
|
||||||
|
assert byte_size(js_conn.resp_body) > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "desktop endpoint serves the live shell without extra router-side secret injection" do
|
test "desktop endpoint serves the live shell without extra router-side secret injection" do
|
||||||
|
|||||||
@@ -6,6 +6,28 @@ defmodule BDS.UI.ShellTest do
|
|||||||
alias BDS.UI.Session
|
alias BDS.UI.Session
|
||||||
alias BDS.UI.Workbench
|
alias BDS.UI.Workbench
|
||||||
|
|
||||||
|
@css_sources [
|
||||||
|
"tokens.css",
|
||||||
|
"shell.css",
|
||||||
|
"sidebar.css",
|
||||||
|
"tabs.css",
|
||||||
|
"editor.css",
|
||||||
|
"forms.css",
|
||||||
|
"panel.css",
|
||||||
|
"assistant.css",
|
||||||
|
"overlays.css",
|
||||||
|
"menu_editor.css",
|
||||||
|
"media_editor.css",
|
||||||
|
"import_editor.css",
|
||||||
|
"utilities.css"
|
||||||
|
]
|
||||||
|
|
||||||
|
defp css_source do
|
||||||
|
@css_sources
|
||||||
|
|> Enum.map(&File.read!("/Users/gb/Projects/bDS2/assets/css/#{&1}"))
|
||||||
|
|> Enum.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
test "registry exposes the shared sidebar and editor contracts for the base shell" do
|
test "registry exposes the shared sidebar and editor contracts for the base shell" do
|
||||||
sidebar_views = Registry.sidebar_views()
|
sidebar_views = Registry.sidebar_views()
|
||||||
editor_routes = Registry.editor_routes()
|
editor_routes = Registry.editor_routes()
|
||||||
@@ -101,25 +123,100 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell keeps the compact frame metrics and live bootstrap assets" do
|
test "desktop shell keeps the compact frame metrics and live bootstrap assets" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
live_js = File.read!("/Users/gb/Projects/bDS2/priv/ui/live.js")
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
assert File.exists?("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
assert File.exists?("/Users/gb/Projects/bDS2/assets/css/shell.css")
|
||||||
assert File.exists?("/Users/gb/Projects/bDS2/priv/ui/live.js")
|
assert File.exists?("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
assert css =~ ".window-titlebar"
|
assert css =~ ".window-titlebar"
|
||||||
assert css =~ "height: 34px"
|
assert css =~ "height: 34px"
|
||||||
assert css =~ "width: 48px"
|
assert css =~ "width: 48px"
|
||||||
assert css =~ "height: 35px"
|
assert css =~ "height: 35px"
|
||||||
assert css =~ "height: 22px"
|
assert css =~ "height: 22px"
|
||||||
assert live_js =~ "LiveView.LiveSocket"
|
assert live_js =~ "new LiveSocket"
|
||||||
assert live_js =~ "Phoenix.Socket"
|
assert live_js =~ "Socket"
|
||||||
assert template =~ "data-project-id={@projects.active_project_id || \"\"}"
|
assert template =~ "data-project-id={@projects.active_project_id || \"\"}"
|
||||||
assert template =~ "data-workbench-session={encoded_workbench_session(@workbench)}"
|
assert template =~ "data-workbench-session={encoded_workbench_session(@workbench)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "desktop shell css keeps editor and help docs on the VS Code dark surface" do
|
||||||
|
css = css_source()
|
||||||
|
|
||||||
|
assert css =~ ".post-editor .post-editor-markdown-surface"
|
||||||
|
assert css =~ ".scripts-monaco.monaco-editor-shell"
|
||||||
|
assert css =~ ".templates-monaco.monaco-editor-shell"
|
||||||
|
assert css =~ ".help-doc-markdown"
|
||||||
|
assert css =~ "background: var(--vscode-editor-background);"
|
||||||
|
assert css =~ "color: var(--vscode-editor-foreground);"
|
||||||
|
refute Regex.match?(~r/\.sidebar-item\s*\{[^}]*background:\s*var\(--panel-2\)/s, css)
|
||||||
|
refute Regex.match?(~r/\.sidebar-item\s*\{[^}]*color:\s*var\(--ink\)/s, css)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "desktop help documentation keeps the old markdown viewer styling contract" do
|
||||||
|
css = css_source()
|
||||||
|
|
||||||
|
assert css =~ ".help-doc-view"
|
||||||
|
assert css =~ ".help-doc-view .misc-editor-content"
|
||||||
|
assert css =~ ".documentation-article"
|
||||||
|
assert css =~ ".documentation-content.markdown-body h1"
|
||||||
|
assert css =~ ".documentation-content.markdown-body table"
|
||||||
|
assert css =~ ".documentation-content.markdown-body ul"
|
||||||
|
assert css =~ "background: var(--doc-surface);"
|
||||||
|
assert css =~ "box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "desktop settings editor keeps the old preferences styling contract" do
|
||||||
|
css = css_source()
|
||||||
|
|
||||||
|
assert css =~ ".settings-view"
|
||||||
|
assert css =~ ".settings-header"
|
||||||
|
assert css =~ ".settings-content"
|
||||||
|
assert css =~ ".setting-section"
|
||||||
|
assert css =~ ".setting-section-header"
|
||||||
|
assert css =~ ".setting-section-content"
|
||||||
|
assert css =~ ".setting-row"
|
||||||
|
assert css =~ ".setting-control"
|
||||||
|
assert css =~ "grid-template-columns: minmax(180px, 240px) minmax(0, 1fr);"
|
||||||
|
assert css =~ "background: var(--panel-2, #252526);"
|
||||||
|
assert css =~ "border: 1px solid var(--line, #3c3c3c);"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "monaco editor styling forces the internal editor surface to the dark theme" do
|
||||||
|
css = css_source()
|
||||||
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
|
|
||||||
|
assert css =~ ".monaco-editor .margin"
|
||||||
|
assert css =~ ".monaco-editor-background"
|
||||||
|
assert css =~ "background-color: var(--vscode-editor-background) !important;"
|
||||||
|
assert css =~ ".monaco-editor .view-line"
|
||||||
|
assert live_js =~ "base: \"vs-dark\""
|
||||||
|
assert live_js =~ "monaco.editor.setTheme(\"bds-theme\");"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "monaco editor hook forces first visible layout and textarea content sync" do
|
||||||
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
|
|
||||||
|
assert live_js =~ "this.syncEditorFromTextarea"
|
||||||
|
assert live_js =~ "this.layoutEditorSoon"
|
||||||
|
assert live_js =~ "this.waitForMonacoVisibleSize"
|
||||||
|
assert live_js =~ "ResizeObserver"
|
||||||
|
assert live_js =~ "requestAnimationFrame"
|
||||||
|
assert live_js =~ "this.editor.layout()"
|
||||||
|
assert live_js =~ "this.syncEditorFromTextarea()"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "monaco theme uses normalized app colors before defining the dark theme" do
|
||||||
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
|
|
||||||
|
assert live_js =~ "normalizeMonacoColor"
|
||||||
|
assert live_js =~ "base: \"vs-dark\""
|
||||||
|
assert live_js =~ "\"editor.background\": background"
|
||||||
|
assert live_js =~ "monaco.editor.defineTheme(\"bds-theme\""
|
||||||
|
end
|
||||||
|
|
||||||
test "desktop shell assets persist workbench layout per project" do
|
test "desktop shell assets persist workbench layout per project" do
|
||||||
live_js = File.read!("/Users/gb/Projects/bDS2/priv/ui/live.js")
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
live_ex = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live.ex")
|
live_ex = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live.ex")
|
||||||
|
|
||||||
session_util_ex =
|
session_util_ex =
|
||||||
@@ -134,8 +231,8 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell assets reveal loaded media sidebar thumbnails" do
|
test "desktop shell assets reveal loaded media sidebar thumbnails" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
live_js = File.read!("/Users/gb/Projects/bDS2/priv/ui/live.js")
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
|
|
||||||
assert css =~ ".media-thumbnail.is-loaded .media-thumbnail-image"
|
assert css =~ ".media-thumbnail.is-loaded .media-thumbnail-image"
|
||||||
assert css =~ ".media-thumbnail.is-loaded .media-thumbnail-fallback"
|
assert css =~ ".media-thumbnail.is-loaded .media-thumbnail-fallback"
|
||||||
@@ -145,7 +242,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell css keeps the status bar and hidden menu alignment rules" do
|
test "desktop shell css keeps the status bar and hidden menu alignment rules" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
assert css =~ ".window-titlebar-menu-bar.is-hidden"
|
assert css =~ ".window-titlebar-menu-bar.is-hidden"
|
||||||
assert css =~ "--vscode-statusBar-background: #007acc"
|
assert css =~ "--vscode-statusBar-background: #007acc"
|
||||||
@@ -162,8 +259,8 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell assets keep old activity, tab, focus, and titlebar overlay parity rules" do
|
test "desktop shell assets keep old activity, tab, focus, and titlebar overlay parity rules" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
live_js = File.read!("/Users/gb/Projects/bDS2/priv/ui/live.js")
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
assert css =~ "color: var(--vscode-activityBar-foreground)"
|
assert css =~ "color: var(--vscode-activityBar-foreground)"
|
||||||
@@ -194,14 +291,14 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell keeps sidebar delete buttons visible in the default state" do
|
test "desktop shell keeps sidebar delete buttons visible in the default state" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
assert Regex.match?(~r/\.sidebar-delete-button\s*\{[^}]*opacity:\s*1;/s, css)
|
assert Regex.match?(~r/\.sidebar-delete-button\s*\{[^}]*opacity:\s*1;/s, css)
|
||||||
refute Regex.match?(~r/\.sidebar-delete-button\s*\{[^}]*opacity:\s*0;/s, css)
|
refute Regex.match?(~r/\.sidebar-delete-button\s*\{[^}]*opacity:\s*0;/s, css)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell css keeps the old activity bar active marker contrast" do
|
test "desktop shell css keeps the old activity bar active marker contrast" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
assert css =~ "--vscode-activityBar-foreground: #ffffff"
|
assert css =~ "--vscode-activityBar-foreground: #ffffff"
|
||||||
assert css =~ ".activity-bar-item:hover {"
|
assert css =~ ".activity-bar-item:hover {"
|
||||||
@@ -211,8 +308,8 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell assets keep legacy titlebar menu keyboard and anchoring behavior" do
|
test "desktop shell assets keep legacy titlebar menu keyboard and anchoring behavior" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
live_js = File.read!("/Users/gb/Projects/bDS2/priv/ui/live.js")
|
live_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
live_ex = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live.ex")
|
live_ex = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live.ex")
|
||||||
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
@@ -232,7 +329,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell css keeps the old media editor layout contract" do
|
test "desktop shell css keeps the old media editor layout contract" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
template =
|
template =
|
||||||
File.read!(
|
File.read!(
|
||||||
@@ -288,7 +385,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell css keeps old panel and output density" do
|
test "desktop shell css keeps old panel and output density" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
assert css =~ ".panel-content {"
|
assert css =~ ".panel-content {"
|
||||||
assert css =~ "padding: 8px;"
|
assert css =~ "padding: 8px;"
|
||||||
@@ -302,7 +399,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell css keeps legacy sidebar header and post list layout" do
|
test "desktop shell css keeps legacy sidebar header and post list layout" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
assert css =~ ".sidebar-section {"
|
assert css =~ ".sidebar-section {"
|
||||||
assert css =~ "margin-bottom: 4px;"
|
assert css =~ "margin-bottom: 4px;"
|
||||||
@@ -317,7 +414,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell assets keep the assistant sidebar chat surface contract" do
|
test "desktop shell assets keep the assistant sidebar chat surface contract" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
assert css =~ ".assistant-sidebar-context"
|
assert css =~ ".assistant-sidebar-context"
|
||||||
@@ -332,7 +429,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell assets expose the shared overlay render contract" do
|
test "desktop shell assets expose the shared overlay render contract" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
live_ex = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live.ex")
|
live_ex = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live.ex")
|
||||||
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
@@ -439,7 +536,7 @@ defmodule BDS.UI.ShellTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "desktop shell css keeps the old assistant sidebar panel styling" do
|
test "desktop shell css keeps the old assistant sidebar panel styling" do
|
||||||
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
css = css_source()
|
||||||
|
|
||||||
assert css =~ ".assistant-content {"
|
assert css =~ ".assistant-content {"
|
||||||
assert css =~ "padding: 12px;"
|
assert css =~ "padding: 12px;"
|
||||||
|
|||||||
Reference in New Issue
Block a user