feat: rework of the full CSS machine to tailwind and modular CSS
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
1 parent
6b6c985187
commit
b17e9cc3f8
29 files changed
+7030
-8623
No files matched your search
+577
@@ -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
|
||||||
@@ -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";
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
Whitespace-only changes.
@@ -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: "{}"
|
||||||
|
|||||||
@@ -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"]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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"},
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/*! tailwindcss v4.1.14 | MIT License | https://tailwindcss.com */
|
||||||
|
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.block{display:block}.hidden{display:none}.inline{display:inline}.table{display:table}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.resize{resize:both}.rounded{border-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.invert{--tw-invert:invert(100Line truncated
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
(()=>{var $e=t=>typeof t=="function"?t:function(){return t},ki=typeof self<"u"?self:null,Pe=typeof window<"u"?window:null,re=ki||Pe||globalThis,Ai="2.0.0",oe={connecting:0,open:1,closing:2,closed:3},Ci=1e4,Ti=1e3,Q={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},de={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},pt={longpoll:"longpoll",websocket:"websocket"},_i={complete:4},mt="base64url.bearer.phx.",Ye=class{constructor(t,e,i,s){this.channel=t,this.event=e,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=s,this.timeoutTimer=null,this.recHooks=[],this.sent=!1}resend(t){this.timeout=t,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()}))}receive(t,e){return this.hasReceived(t)&&e(this.receivedResp.response),this.recHooks.push({status:t,callback:e}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}matchReceive({status:t,response:e,_ref:i}){this.recHooks.filter(s=>s.status===t).forEach(s=>s.callback(e))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,t=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=t,this.matchReceive(t)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(t){return this.receivedResp&&this.receivedResp.status===t}trigger(t,e){this.channel.trigger(this.refEvent,{status:t,response:e})}},Ht=class{constructor(t,e){this.callback=t,this.timerCalc=e,this.timer=null,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}},Pi=class{constructor(t,e,i){this.state=Q.closed,this.topic=t,this.params=$e(e||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new Ye(this,de.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new Ht(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=Q.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(s=>s.send()),this.pushBuffer=[]}),this.joinPush.receive("error",()=>{this.state=Q.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic} ${this.joinRef()}`),this.state=Q.closed,this.socket.remove(this)}),this.onError(s=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,s),this.isJoining()&&this.joinPush.reset(),this.state=Q.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic} (${this.joinRef()})`,this.joinPush.timeout),new Ye(this,de.leave,$e({}),this.timeout).send(),this.state=Q.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(de.reply,(s,n)=>{this.trigger(this.replyEventName(n),s)})}join(t=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=t,this.joinedOnce=!0,this.rejoin(),this.joinPush}onClose(t){this.on(de.close,t)}onError(t){return this.on(de.error,e=>t(e))}on(t,e){let i=this.bindingRef++;return this.bindings.push({event:t,ref:i,callback:e}),i}off(t,e){this.bindings=this.bindings.filter(i=>!(i.event===t&&(typeof e>"u"||e===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(t,e,i=this.timeout){if(e=e||{},!this.joinedOnce)throw new Error(`tried to push '${t}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let s=new Ye(this,t,function(){return e},i);return this.canPush()?s.send():(s.startTimeout(),this.pushBuffer.push(s)),s}leave(t=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=Q.leaving;let e=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(de.close,"leave")},i=new Ye(this,de.leave,$e({}),t);return i.receive("ok",()=>e()).receive("timeout",()=>e()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(t,e,i){return e}isMember(t,e,i,s){return this.topic!==t?!1:sLine truncated
|
||||||
|
`),()=>this.onerror("timeout"),e=>{this.awaitingBatchAck=!1,!e||e.status!==200?(this.onerror(e&&e.status),this.closeAndRetry(1011,"internal server error",!1)):this.batchBuffer.length>0&&(this.batchSend(this.batchBuffer),this.batchBuffer=[])})}close(t,e,i){for(let n of this.reqs)n.abort();this.readyState=oe.closed;let s=Object.assign({code:1e3,reason:void 0,wasClean:!0},{code:t,reason:e,wasClean:i});this.batchBuffer=[],clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,typeof CloseEvent<"u"?this.onclose(new CloseEvent("close",s)):this.onclose(s)}ajax(t,e,i,s,n){let r,o=()=>{this.reqs.delete(r),s()};r=Qe.request(t,this.endpointURL(),e,i,this.timeout,o,a=>{this.reqs.delete(r),this.isActive()&&n(a)}),this.reqs.add(r)}};var Ze={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(t,e){if(t.payload.constructor===ArrayBuffer)return e(this.binaryEncode(t));{let i=[t.join_ref,t.ref,t.topic,t.event,t.payload];return e(JSON.stringify(i))}},decode(t,e){if(t.constructor===ArrayBuffer)return e(this.binaryDecode(t));{let[i,s,n,r,o]=JSON.parse(t);return e({join_ref:i,ref:s,topic:n,event:r,payload:o})}},binaryEncode(t){let{join_ref:e,ref:i,event:s,topic:n,payload:r}=t,o=this.META_LENGTH+e.length+i.length+n.length+s.length,a=new ArrayBuffer(this.HEADER_LENGTH+o),h=new DataView(a),c=0;h.setUint8(c++,this.KINDS.push),h.setUint8(c++,e.length),h.setUint8(c++,i.length),h.setUint8(c++,n.length),h.setUint8(c++,s.length),Array.from(e,g=>h.setUint8(c++,g.charCodeAt(0))),Array.from(i,g=>h.setUint8(c++,g.charCodeAt(0))),Array.from(n,g=>h.setUint8(c++,g.charCodeAt(0))),Array.from(s,g=>h.setUint8(c++,g.charCodeAt(0)));var f=new Uint8Array(a.byteLength+r.byteLength);return f.set(new Uint8Array(a),0),f.set(new Uint8Array(r),a.byteLength),f.buffer},binaryDecode(t){let e=new DataView(t),i=e.getUint8(0),s=new TextDecoder;switch(i){case this.KINDS.push:return this.decodePush(t,e,s);case this.KINDS.reply:return this.decodeReply(t,e,s);case this.KINDS.broadcast:return this.decodeBroadcast(t,e,s)}},decodePush(t,e,i){let s=e.getUint8(1),n=e.getUint8(2),r=e.getUint8(3),o=this.HEADER_LENGTH+this.META_LENGTH-1,a=i.decode(t.slice(o,o+s));o=o+s;let h=i.decode(t.slice(o,o+n));o=o+n;let c=i.decode(t.slice(o,o+r));o=o+r;let f=t.slice(o,t.byteLength);return{join_ref:a,ref:null,topic:h,event:c,payload:f}},decodeReply(t,e,i){let s=e.getUint8(1),n=e.getUint8(2),r=e.getUint8(3),o=e.getUint8(4),a=this.HEADER_LENGTH+this.META_LENGTH,h=i.decode(t.slice(a,a+s));a=a+s;let c=i.decode(t.slice(a,a+n));a=a+n;let f=i.decode(t.slice(a,a+r));a=a+r;let g=i.decode(t.slice(a,a+o));a=a+o;let b=t.slice(a,t.byteLength),w={status:g,response:b};return{join_ref:h,ref:c,topic:f,event:de.reply,payload:w}},decodeBroadcast(t,e,i){let s=e.getUint8(1),n=e.getUint8(2),r=this.HEADER_LENGTH+2,o=i.decode(t.slice(r,r+s));r=r+s;let a=i.decode(t.slice(r,r+n));r=r+n;let h=t.slice(r,t.byteLength);return{join_ref:null,ref:null,topic:o,event:a,payload:h}}},Nt=class{constructor(t,e={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.timeout=e.timeout||Ci,this.transport=e.transport||re.WebSocket||_e,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=e.longPollFallbackMs,this.fallbackTimer=null,this.sessionStore=e.sessionStorage||re&&re.sessionStorage,this.establishedConnections=0,this.defaultEncoder=Ze.encode.bind(Ze),this.defaultDecoder=Ze.decode.bind(Ze),this.closeWasClean=!0,this.disconnecting=!1,this.binaryType=e.binaryType||"arraybuffer",this.connectClock=1,this.pageHidden=!1,this.transport!==_e?(this.encode=e.encode||this.defaultEncoder,this.decode=e.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let i=null;Pe&&Pe.addEventListener&&(Pe.addEventListener("pagehide",s=>{this.conn&&(this.disconnect(),i=this.connectClock)}),Pe.addEventListener("pageshow",s=>{i===this.connectClock&&(i=null,this.connect())}),Pe.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.pageHidden=!0:(this.pageHidden=!1,!this.isConnected()&&!this.closeWasClean&&this.teardown(()=>this.connect()))})),this.heartbeatIntervalMs=e.heartbeatIntervalMs||3e4,this.rejoinAfterMs=s=>e.rejoinAfterMs?e.rejoinAfterMs(s):[1e3,2e3,5e3][s-1]||1e4,this.reconnectAfterMs=s=>e.reconnectAfterMs?e.reconnectAfterMs(s):[10,50,100,150,200,250,500,1e3,2e3][s-1]||5e3,this.logger=e.logger||null,!this.logger&&e.debug&&(this.logger=(s,n,r)=>{console.log(`${s}: ${n}`,r)}),this.longpollerTimeout=e.longpollerTimeout||2e4,this.params=$e(e.params||{}),this.endPoint=`${t}/${pt.websocket}`,this.vsn=e.vsn||Ai,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new Ht(()=>{if(this.pageHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardown(()=>this.connect())},this.reconnectAfterMs),this.authToken=e.authToken}getLongPollTransport(){return _e}replaceTransport(t){this.con
Line truncated
|
||||||
|
hook attached to non-connected DOM element
|
||||||
|
ensure you are calling createHook within your connectedCallback. ${t.outerHTML}
|
||||||
|
`),this.putPrivate(t,"custom-el-hook",e)},getCustomElHook(t){return this.private(t,"custom-el-hook")},isUsedInput(t){return t.nodeType===Node.ELEMENT_NODE&&(this.private(t,dt)||this.private(t,Xe))},resetForm(t){Array.from(t.elements).forEach(e=>{this.deletePrivate(e,dt),this.deletePrivate(e,Xe)})},isPhxChild(t){return t.getAttribute&&t.getAttribute(ke)},isPhxSticky(t){return t.getAttribute&&t.getAttribute(Tt)!==null},isChildOfAny(t,e){return!!e.find(i=>i.contains(t))},firstPhxChild(t){return this.isPhxChild(t)?t:this.all(t,`[${ke}]`)[0]},isPortalTemplate(t){return t.tagName==="TEMPLATE"&&t.hasAttribute(Dt)},closestViewEl(t){let e=t.closest(`[${Ae}],${ze}`);return e?e.hasAttribute(Ae)?this.byId(e.getAttribute(Ae)):e.hasAttribute(ce)?e:null:null},dispatchEvent(t,e,i={}){let s=!0;t.nodeName==="INPUT"&&t.type==="file"&&e==="click"&&(s=!1);let o={bubbles:i.bubbles===void 0?s:!!i.bubbles,cancelable:!0,detail:i.detail||{}},a=e==="click"?new MouseEvent("click",o):new CustomEvent(e,o);t.dispatchEvent(a)},cloneNode(t,e){if(typeof e>"u")return t.cloneNode(!0);{let i=t.cloneNode(!1);return i.innerHTML=e,i}},mergeAttrs(t,e,i={}){let s=new Set(i.exclude||[]),n=i.isIgnored,r=e.attributes;for(let a=r.length-1;a>=0;a--){let h=r[a].name;if(s.has(h)){if(h==="value"){let c=e.value??e.getAttribute(h);t.value===c&&t.setAttribute("value",e.getAttribute(h))}}else{let c=e.getAttribute(h);t.getAttribute(h)!==c&&(!n||n&&h.startsWith("data-"))&&t.setAttribute(h,c)}}let o=t.attributes;for(let a=o.length-1;a>=0;a--){let h=o[a].name;n?h.startsWith("data-")&&!e.hasAttribute(h)&&!zt.includes(h)&&t.removeAttribute(h):e.hasAttribute(h)||t.removeAttribute(h)}},mergeFocusedInput(t,e){t instanceof HTMLSelectElement||ye.mergeAttrs(t,e,{exclude:["value"]}),e.readOnly?t.setAttribute("readonly",!0):t.removeAttribute("readonly")},hasSelectionRange(t){return t.setSelectionRange&&(t.type==="text"||t.type==="textarea")},restoreFocus(t,e,i){if(t instanceof HTMLSelectElement&&t.focus(),!ye.isTextualInput(t))return;t.matches(":focus")||t.focus(),this.hasSelectionRange(t)&&t.setSelectionRange(e,i)},isFormInput(t){return t.localName&&customElements.get(t.localName)?customElements.get(t.localName).formAssociated:/^(?:input|select|textarea)$/i.test(t.tagName)&&t.type!=="button"},syncAttrsToProps(t){t instanceof HTMLInputElement&&gi.indexOf(t.type.toLocaleLowerCase())>=0&&(t.checked=t.getAttribute("checked")!==null)},isTextualInput(t){return Fi.indexOf(t.type)>=0},isNowTriggerFormExternal(t,e){return t.getAttribute&&t.getAttribute(e)!==null&&document.body.contains(t)},cleanChildNodes(t,e){if(ye.isPhxUpdate(t,e,["append","prepend",ht])){let i=[];t.childNodes.forEach(s=>{s.id||(!(s.nodeType===Node.TEXT_NODE&&s.nodeValue.trim()==="")&&s.nodeType!==Node.COMMENT_NODE&&O(`only HTML element tags with an id are allowed inside containers with phx-update.
|
||||||
|
|
||||||
|
removing illegal node: "${(s.outerHTML||s.nodeValue).trim()}"
|
||||||
|
|
||||||
|
`),i.push(s))}),i.forEach(s=>s.remove())}},replaceRootContainer(t,e,i){let s=new Set(["id",ce,Ee,Lt,fe]);if(t.tagName.toLowerCase()===e.toLowerCase())return Array.from(t.attributes).filter(n=>!s.has(n.name.toLowerCase())).forEach(n=>t.removeAttribute(n.name)),Object.keys(i).filter(n=>!s.has(n.toLowerCase())).forEach(n=>t.setAttribute(n,i[n])),t;{let n=document.createElement(e);return Object.keys(i).forEach(r=>n.setAttribute(r,i[r])),s.forEach(r=>n.setAttribute(r,t.getAttribute(r))),n.innerHTML=t.innerHTML,t.replaceWith(n),n}},getSticky(t,e,i){let s=(ye.private(t,"sticky")||[]).find(([n])=>e===n);if(s){let[n,r,o]=s;return o}else return typeof i=="function"?i():i},deleteSticky(t,e){this.updatePrivate(t,"sticky",[],i=>i.filter(([s,n])=>s!==e))},putSticky(t,e,i){let s=i(t);this.updatePrivate(t,"sticky",[],n=>{let r=n.findIndex(([o])=>e===o);return r>=0?n[r]=[e,i,s]:n.push([e,i,s]),n})},applyStickyOperations(t){let e=ye.private(t,"sticky");e&&e.forEach(([i,s,n])=>this.putSticky(t,i,s))},isLocked(t){return t.hasAttribute&&t.hasAttribute(B)},attributeIgnored(t,e){return e.some(i=>t.name==i||i==="*"||i.includes("*")&&t.name.match(i)!=null)}},u=ye,We=class{static isActive(t,e){let i=e._phxRef===void 0,n=t.getAttribute(kt).split(",").indexOf(W.genFileRef(e))>=0;return e.size>0&&(i||n)}static isPreflighted(t,e){return t.getAttribute(It).split(",").indexOf(W.genFileRef(e))>=0&&this.isActive(t,e)}static isPreflightInProgress(t){return t._preflightInProgress===!0}static markPreflightInProgress(t){t._preflightInProgress=!0}constructor(t,e,i,s){this.ref=W.genFileRef(e),this.fileEl=t,this.file=e,this.view=i,this.meta=null,this._isCancelled=!1,this._isDone=!1,this._progress=0,this._lastProgressSent=-1,this._onDone=function(){},this._onElUpdated=this.onElUpdated.bind(this),this.fileEl.addEventListener(at,this._onElUpdated),this.autoUpload=s}metadata(){return this.meta}progress(t){this._progress=Math.floor(t),this._progress>this._lastProgressSent&&(this._progress>=100?(this._progress=100,this._lastProgressSent=100,this._isDone=!0,this.view.pushFileProgress(this.fileEl,this.ref,100,()=>{W.untrackFile(this.fileEl,this.file),this._onDone()})):(this._lastProgressSent=this._progress,this.view.pushFileProgress(this.fileEl,this.ref,this._progress)))}isCancelled(){return this._isCancelled}cancel(){this.file._preflightInProgress=!1,this._isCancelled=!0,this._isDone=!0,this._onDone()}isDone(){return this._isDone}error(t="failed"){this.fileEl.removeEventListener(at,this._onElUpdated),this.view.pushFileProgress(this.fileEl,this.ref,{error:t}),this.isAutoUpload()||W.clearFiles(this.fileEl)}isAutoUpload(){return this.autoUpload}onDone(t){this._onDone=()=>{this.fileEl.removeEventListener(at,this._onElUpdated),t()}}onElUpdated(){this.fileEl.getAttribute(kt).split(",").indexOf(this.ref)===-1&&(W.untrackFile(this.fileEl,this.file),this.cancel())}toPreflightPayload(){return{last_modified:this.file.lastModified,name:this.file.name,relative_path:this.file.webkitRelativePath,size:this.file.size,type:this.file.type,ref:this.ref,meta:typeof this.file.meta=="function"?this.file.meta():void 0}}uploader(t){if(this.meta.uploader){let e=t[this.meta.uploader]||O(`no uploader configured for ${this.meta.uploader}`);return{name:this.meta.uploader,callback:e}}else return{name:"channel",callback:is}}zipPostFlight(t){this.meta=t.entries[this.ref],this.meta||O(`no preflight upload response returned with ref ${this.ref}`,{input:this.fileEl,response:t})}},rs=0,W=class Pt{static genFileRef(e){let i=e._phxRef;return i!==void 0?i:(e._phxRef=(rs++).toString(),e._phxRef)}static getEntryDataURL(e,i,s){let n=this.activeFiles(e).find(r=>this.genFileRef(r)===i);s(URL.createObjectURL(n))}static hasUploadsInProgress(e){let i=0;return u.findUploadInputs(e).forEach(s=>{s.getAttribute(It)!==s.getAttribute(Hi)&&i++}),i>0}static serializeUploads(e){let i=this.activeFiles(e),s={};return i.forEach(n=>{let r={path:e.name},o=e.getAttribute(pe);s[o]=s[o]||[],r.ref=this.genFileRef(n),r.last_modified=n.lastModified,r.name=n.name||r.ref,r.relative_path=n.webkitRelativePath,r.type=n.type,r.size=n.size,typeof n.meta=="function"&&(r.meta=n.meta()),s[o].push(r)}),s}static clearFiles(e){e.value=null,e.removeAttribute(pe),u.putPrivate(e,"files",[])}static untrackFile(e,i){u.putPrivate(e,"files",u.private(e,"files").filter(s=>!Object.is(s,i)))}static trackFiles(e,i,s){if(e.getAttribute("multiple")!==null){let n=i.filter(r=>!this.activeFiles(e).find(o=>Object.is(o,r)));u.updatePrivate(e,"files",[],r=>r.concat(n)),e.value=null}else s&&s.files.length>0&&(e.files=s.files),u.putPrivate(e,"files",i)}static activeFileInputs(e){let i=u.findUploadInputs(e);return Array.from(i).filter(s=>s.files&&this.activeFiles(s).length>0)}static activeFiles(e){return(u.private(e,"files")||[]).filter(i=>We.isActive(e,i))}static inputsAwaitingPreflight(e){let i=u.findUploadInputs(e);return Array.from(i).filter(s=>this.filesAwaitingPreflight(s).length>0)}static filesAwaitingPreflight(e){return this.activeFiles(e).filter
Line truncated
|
||||||
|
`,T)})),S.length>0&&i.time("post-morph append/prepend restoration",()=>{S.forEach(T=>T.perform())}),i.silenceEvents(()=>u.restoreFocus(o,a,h)),u.dispatchEvent(document,"phx:update"),w.forEach(T=>this.trackAfter("added",T)),p.forEach(T=>this.trackAfter("updated",T)),this.transitionPendingRemoves(),$){i.unload();let T=u.private($,"submitter");if(T&&T.name&&r.contains(T)){let R=document.createElement("input");R.type="hidden";let L=T.getAttribute("form");L&&R.setAttribute("form",L),R.name=T.name,R.value=T.value,T.parentElement.insertBefore(R,T)}Object.getPrototypeOf($).submit.call($)}return!0}onNodeDiscarded(t){(u.isPhxChild(t)||u.isPhxSticky(t))&&this.liveSocket.destroyViewByEl(t),this.trackAfter("discarded",t)}maybePendingRemove(t){return t.getAttribute&&t.getAttribute(this.phxRemove)!==null?(this.pendingRemoves.push(t),!0):!1}removeStreamChildElement(t,e=!1){!e&&!this.view.ownsElement(t)||(this.streamInserts[t.id]?(this.streamComponentRestore[t.id]=t,t.remove()):this.maybePendingRemove(t)||(t.remove(),this.onNodeDiscarded(t)))}getStreamInsert(t){return(t.id?this.streamInserts[t.id]:{})||{}}setStreamRef(t,e){u.putSticky(t,je,i=>i.setAttribute(je,e))}maybeReOrderStream(t,e){let{ref:i,streamAt:s,reset:n}=this.getStreamInsert(t);if(s!==void 0&&(this.setStreamRef(t,i),!(!n&&!e)&&t.parentElement)){if(s===0)t.parentElement.insertBefore(t,t.parentElement.firstElementChild);else if(s>0){let r=Array.from(t.parentElement.children),o=r.indexOf(t);if(s>=r.length-1)t.parentElement.appendChild(t);else{let a=r[s];o>s?t.parentElement.insertBefore(t,a):t.parentElement.insertBefore(t,a.nextElementSibling)}}this.maybeLimitStream(t)}}maybeLimitStream(t){let{limit:e}=this.getStreamInsert(t),i=e!==null&&Array.from(t.parentElement.children);e&&e<0&&i.length>e*-1?i.slice(0,i.length+e).forEach(s=>this.removeStreamChildElement(s)):e&&e>=0&&i.length>e&&i.slice(e).forEach(s=>this.removeStreamChildElement(s))}transitionPendingRemoves(){let{pendingRemoves:t,liveSocket:e}=this;t.length>0&&e.transitionRemoves(t,()=>{t.forEach(i=>{let s=u.firstPhxChild(i);s&&e.destroyViewByEl(s),i.remove()}),this.trackAfter("transitionsDiscarded",t)})}isChangedSelect(t,e){return!(t instanceof HTMLSelectElement)||t.multiple?!1:t.options.length!==e.options.length?!0:(e.value=t.value,!t.isEqualNode(e))}isCIDPatch(){return this.cidPatch}skipCIDSibling(t){return t.nodeType===Node.ELEMENT_NODE&&t.hasAttribute(pi)}targetCIDContainer(t){if(!this.isCIDPatch())return;let[e,...i]=u.findComponentNodeList(this.view.id,this.targetCID);return i.length===0&&u.childNodeLength(t)===1?e:e&&e.parentNode}indexOf(t,e){return Array.from(t.children).indexOf(e)}teleport(t,e){let i=t.getAttribute(Dt),s=document.querySelector(i);if(!s)throw new Error("portal target with selector "+i+" not found");let n=t.content.firstElementChild;if(this.skipCIDSibling(n))return;if(!n?.id)throw new Error("phx-portal template must have a single root element with ID!");let r=document.getElementById(n.id),o;r?(s.contains(r)||s.appendChild(r),o=r):(o=document.createElement(n.tagName),s.appendChild(o)),n.setAttribute(Ae,this.view.id),n.setAttribute(ve,t.id),e(o,n,!0),n.removeAttribute(Ae),n.removeAttribute(ve),this.view.pushPortalElementId(n.id)}handleRuntimeHook(t,e){let i=t.getAttribute(lt),s=t.hasAttribute("nonce")?t.getAttribute("nonce"):null;if(t.hasAttribute("nonce")){let r=document.createElement("template");r.innerHTML=e,s=r.content.querySelector(`script[${lt}="${CSS.escape(i)}"]`).getAttribute("nonce")}let n=document.createElement("script");n.textContent=t.textContent,u.mergeAttrs(n,t,{isIgnored:!1}),s&&(n.nonce=s),t.replaceWith(n),t=n}},As=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),Cs=new Set(["'",'"']),oi=(t,e,i)=>{let s=0,n=!1,r,o,a,h,c,f,g=t.match(/^(\s*(?:<!--.*?-->\s*)*)<([^\s\/>]+)/);if(g===null)throw new Error(`malformed html ${t}`);for(s=g[0].length,r=g[1],a=g[2],h=s,s;s<t.length&&t.charAt(s)!==">";s++)if(t.charAt(s)==="="){let p=t.slice(s-3,s)===" id";s++;let S=t.charAt(s);if(Cs.has(S)){let A=s;for(s++,s;s<t.length&&t.charAt(s)!==S;s++);if(p){c=t.slice(A+1,s);break}}}let b=t.length-1;for(n=!1;b>=r.length+a.length;){let p=t.charAt(b);if(n)p==="-"&&t.slice(b-3,b)==="<!-"?(n=!1,b-=4):b-=1;else if(p===">"&&t.slice(b-2,b)==="--")n=!0,b-=3;else{if(p===">")break;b-=1}}o=t.slice(b+1,t.length);let w=Object.keys(e).map(p=>e[p]===!0?p:`${p}="${e[p]}"`).join(" ");if(i){let p=c?` id="${c}"`:"";As.has(a)?f=`<${a}${p}${w===""?"":" "}${w}/>`:f=`<${a}${p}${w===""?"":" "}${w}></${a}>`}else{let p=t.slice(h,b+1);f=`<${a}${w===""?"":" "}${w}${p}`}return[f,r,o]},ai=class{static extract(t){let{[Gt]:e,[Kt]:i,[Yt]:s}=t;return delete t[Gt],delete t[Kt],delete t[Yt],{diff:t,title:s,reply:e||null,events:i||[]}}constructor(t,e){this.viewId=t,this.rendered={},this.magicId=0,this.mergeDiff(e)}parentViewId(){return this.viewId}toString(t){let{buffer:e,streams:i}=this.recursiveToString(this.rendered,this.rendered[K],t,!0,{});return{buffeLine truncated
|
||||||
|
|
||||||
|
An element can only ever be associated with a single view!
|
||||||
|
Please ensure that you are not trying to initialize multiple LiveSockets on the same page.
|
||||||
|
This could happen if you're accidentally trying to render your root layout more than once.
|
||||||
|
Ensure that the template set on the LiveView is different than the root layout.
|
||||||
|
`,{view:o}),new Error("Cannot bind multiple views to the same DOM element.");u.putPrivate(this.el,"view",this),this.id=this.el.id,this.el.setAttribute(fe,this.root.id),this.ref=0,this.lastAckRef=null,this.childJoins=0,this.loaderTimer=null,this.disconnectedTimer=null,this.pendingDiffs=[],this.pendingForms=new Set,this.redirect=!1,this.href=null,this.joinCount=this.parent?this.parent.joinCount-1:0,this.joinAttempts=0,this.joinPending=!0,this.destroyed=!1,this.joinCallback=function(a){a&&a()},this.stopCallback=function(){},this.pendingJoinOps=[],this.viewHooks={},this.formSubmits=[],this.children=this.parent?null:{},this.root.children[this.id]={},this.formsForRecovery={},this.channel=this.liveSocket.channel(`lv:${this.id}`,()=>{let a=this.href&&this.expandURL(this.href);return{redirect:this.redirect?a:void 0,url:this.redirect?void 0:a||void 0,params:this.connectParams(r),session:this.getSession(),static:this.getStatic(),flash:this.flash,sticky:this.el.hasAttribute(Tt)}}),this.portalElementIds=new Set}setHref(e){this.href=e}setRedirect(e){this.redirect=!0,this.href=e}isMain(){return this.el.hasAttribute(Lt)}connectParams(e){let i=this.liveSocket.params(this.el),s=u.all(document,`[${this.binding(Oi)}]`).map(n=>n.src||n.href).filter(n=>typeof n=="string");return s.length>0&&(i._track_static=s),i._mounts=this.joinCount,i._mount_attempts=this.joinAttempts,i._live_referer=e,this.joinAttempts++,i}isConnected(){return this.channel.canPush()}getSession(){return this.el.getAttribute(ce)}getStatic(){let e=this.el.getAttribute(Ee);return e===""?null:e}destroy(e=function(){}){this.destroyAllChildren(),this.destroyPortalElements(),this.destroyed=!0,u.deletePrivate(this.el,"view"),delete this.root.children[this.id],this.parent&&delete this.root.children[this.parent.id][this.id],clearTimeout(this.loaderTimer);let i=()=>{e();for(let s in this.viewHooks)this.destroyHook(this.viewHooks[s])};u.markPhxChildDestroyed(this.el),this.log("destroyed",()=>["the child has been removed from the parent"]),this.channel.leave().receive("ok",i).receive("error",i).receive("timeout",i)}setContainerClasses(...e){this.el.classList.remove(Ut,be,Re,jt,Fe),this.el.classList.add(...e)}showLoader(e){if(clearTimeout(this.loaderTimer),e)this.loaderTimer=setTimeout(()=>this.showLoader(),e);else{for(let i in this.viewHooks)this.viewHooks[i].__disconnected();this.setContainerClasses(be)}}execAll(e){u.all(this.el,`[${e}]`,i=>this.liveSocket.execJS(i,i.getAttribute(e)))}hideLoader(){clearTimeout(this.loaderTimer),clearTimeout(this.disconnectedTimer),this.setContainerClasses(Ut),this.execAll(this.binding("connected"))}triggerReconnected(){for(let e in this.viewHooks)this.viewHooks[e].__reconnected()}log(e,i){this.liveSocket.log(this,e,i)}transition(e,i,s=function(){}){this.liveSocket.transition(e,i,s)}withinTargets(e,i,s=document){if(e instanceof HTMLElement||e instanceof SVGElement)return this.liveSocket.owner(e,n=>i(n,e));if(ue(e))u.findComponentNodeList(this.id,e,s).length===0?O(`no component found matching phx-target of ${e}`):i(this,parseInt(e));else{let n=Array.from(s.querySelectorAll(e));n.length===0&&O(`nothing found matching the phx-target selector "${e}"`),n.forEach(r=>this.liveSocket.owner(r,o=>i(o,r)))}}applyDiff(e,i,s){this.log(e,()=>["",ct(i)]);let{diff:n,reply:r,events:o,title:a}=ai.extract(i),h=o.reduce((f,g)=>(g.length===3&&g[2]==!0?f.pre.push(g.slice(0,-1)):f.post.push(g),f),{pre:[],post:[]});this.liveSocket.dispatchEvents(h.pre);let c=()=>{s({diff:n,reply:r,events:h.post}),(typeof a=="string"||e=="mount"&&this.isMain())&&window.requestAnimationFrame(()=>u.putTitle(a))};"onDocumentPatch"in this.liveSocket.domCallbacks?this.liveSocket.triggerDOM("onDocumentPatch",[c]):c()}onJoin(e){let{rendered:i,container:s,liveview_version:n,pid:r}=e;if(s){let[o,a]=s;this.el=u.replaceRootContainer(this.el,o,a)}this.childJoins=0,this.joinPending=!0,this.flash=null,this.root===this&&(this.formsForRecovery=this.getFormsForRecovery()),this.isMain()&&window.history.state===null&&Z.pushState("replace",{type:"patch",id:this.id,position:this.liveSocket.currentHistoryPosition}),n!==this.liveSocket.version()&&console.warn(`LiveView asset version mismatch. JavaScript version ${this.liveSocket.version()} vs. server ${n}. To avoid issues, please ensure that your assets use the same version as the server.`),r&&this.el.setAttribute(Bi,r),Z.dropLocal(this.liveSocket.localStorage,window.location.pathname,di),this.applyDiff("mount",i,({diff:o,events:a})=>{this.rendered=new ai(this.id,o);let[h,c]=this.renderContainer(null,"join");this.dropPendingRefs(),this.joinCount++,this.joinAttempts=0,this.maybeRecoverForms(h,()=>{this.onJoinComplete(e,h,c,a)})})}dropPendingRefs(){u.all(document,`[${ie}="${this.refSrc()}"]`,e=>{e.removeAttribute(Me),e.removeAttribute(ie),e.removeAttribute(B)})}onJoinComplete({live_patch:e},i,s,n){if(this.joinCount>1||this.parent&&!this.parent.isJoinPending())return this.applyJoinPatch(e,i,s,n);u.findPhxChildrenInFragment(i,this.id).filter(o=>{let
Line truncated
|
||||||
|
a phoenix Socket must be provided as the second argument to the LiveSocket constructor. For example:
|
||||||
|
|
||||||
|
import {Socket} from "phoenix"
|
||||||
|
import {LiveSocket} from "phoenix_live_view"
|
||||||
|
let liveSocket = new LiveSocket("/live", Socket, {...})
|
||||||
|
`);this.socket=new e(t,i),this.bindingPrefix=i.bindingPrefix||zi,this.opts=i,this.params=qe(i.params||{}),this.viewLogger=i.viewLogger,this.metadataCallbacks=i.metadata||{},this.defaults=Object.assign(ct(Gi),i.defaults||{}),this.prevActive=null,this.silenced=!1,this.main=null,this.outgoingMainEl=null,this.clickStartedAtTarget=null,this.linkRef=1,this.roots={},this.href=window.location.href,this.pendingLink=null,this.currentLocation=ct(window.location),this.hooks=i.hooks||{},this.uploaders=i.uploaders||{},this.loaderTimeout=i.loaderTimeout||Wi,this.disconnectedTimeout=i.disconnectedTimeout||Xi,this.reloadWithJitterTimer=null,this.maxReloads=i.maxReloads||xi,this.reloadJitterMin=i.reloadJitterMin||Ii,this.reloadJitterMax=i.reloadJitterMax||Li,this.failsafeJitter=i.failsafeJitter||Di,this.localStorage=i.localStorage||window.localStorage,this.sessionStorage=i.sessionStorage||window.sessionStorage,this.boundTopLevelEvents=!1,this.boundEventNames=new Set,this.blockPhxChangeWhileComposing=i.blockPhxChangeWhileComposing||!1,this.serverCloseRef=null,this.domCallbacks=Object.assign({jsQuerySelectorAll:null,onPatchStart:qe(),onPatchEnd:qe(),onNodeAdded:qe(),onBeforeElUpdated:qe()},i.dom||{}),this.transitions=new Is,this.currentHistoryPosition=parseInt(this.sessionStorage.getItem(it))||0,window.addEventListener("pagehide",s=>{this.unloaded=!0}),this.socket.onOpen(()=>{this.isUnloaded()&&window.location.reload()})}version(){return"1.1.28"}isProfileEnabled(){return this.sessionStorage.getItem(bt)==="true"}isDebugEnabled(){return this.sessionStorage.getItem(tt)==="true"}isDebugDisabled(){return this.sessionStorage.getItem(tt)==="false"}enableDebug(){this.sessionStorage.setItem(tt,"true")}enableProfiling(){this.sessionStorage.setItem(bt,"true")}disableDebug(){this.sessionStorage.setItem(tt,"false")}disableProfiling(){this.sessionStorage.removeItem(bt)}enableLatencySim(t){this.enableDebug(),console.log("latency simulator enabled for the duration of this browser session. Call disableLatencySim() to disable"),this.sessionStorage.setItem(wt,t)}disableLatencySim(){this.sessionStorage.removeItem(wt)}getLatencySim(){let t=this.sessionStorage.getItem(wt);return t?parseInt(t):null}getSocket(){return this.socket}connect(){window.location.hostname==="localhost"&&!this.isDebugDisabled()&&this.enableDebug();let t=()=>{this.resetReloadStatus(),this.joinRootViews()?(this.bindTopLevelEvents(),this.socket.connect()):this.main?this.socket.connect():this.bindTopLevelEvents({dead:!0}),this.joinDeadView()};["complete","loaded","interactive"].indexOf(document.readyState)>=0?t():document.addEventListener("DOMContentLoaded",()=>t())}disconnect(t){clearTimeout(this.reloadWithJitterTimer),this.serverCloseRef&&(this.socket.off(this.serverCloseRef),this.serverCloseRef=null),this.socket.disconnect(t)}replaceTransport(t){clearTimeout(this.reloadWithJitterTimer),this.socket.replaceTransport(t),this.connect()}execJS(t,e,i=null){let s=new CustomEvent("phx:exec",{detail:{sourceElement:t}});this.owner(t,n=>x.exec(s,i,e,n,t))}js(){return wi(this,"js")}unload(){this.unloaded||(this.main&&this.isConnected()&&this.log(this.main,"socket",()=>["disconnect for page nav"]),this.unloaded=!0,this.destroyAllViews(),this.disconnect())}triggerDOM(t,e){this.domCallbacks[t](...e)}time(t,e){if(!this.isProfileEnabled()||!console.time)return e();console.time(t);let i=e();return console.timeEnd(t),i}log(t,e,i){if(this.viewLogger){let[s,n]=i();this.viewLogger(t,e,s,n)}else if(this.isDebugEnabled()){let[s,n]=i();es(t,e,s,n)}}requestDOMUpdate(t){this.transitions.after(t)}asyncTransition(t){this.transitions.addAsyncTransition(t)}transition(t,e,i=function(){}){this.transitions.addTransition(t,e,i)}onChannel(t,e,i){t.on(e,s=>{let n=this.getLatencySim();n?setTimeout(()=>i(s),n):i(s)})}reloadWithJitter(t,e){clearTimeout(this.reloadWithJitterTimer),this.disconnect();let i=this.reloadJitterMin,s=this.reloadJitterMax,n=Math.floor(Math.random()*(s-i+1))+i,r=Z.updateLocal(this.localStorage,window.location.pathname,di,0,o=>o+1);r>=this.maxReloads&&(n=this.failsafeJitter),this.reloadWithJitterTimer=setTimeout(()=>{t.isDestroyed()||t.isConnected()||(t.destroy(),e?e():this.log(t,"join",()=>[`encountered ${r} consecutive reloads`]),r>=this.maxReloads&&this.log(t,"join",()=>[`exceeded ${this.maxReloads} consecutive reloads. Entering failsafe mode`]),this.hasPendingLink()?window.location=this.pendingLink:window.location.reload())},n)}getHookDefinition(t){if(t)return this.maybeInternalHook(t)||this.hooks[t]||this.maybeRuntimeHook(t)}maybeInternalHook(t){return t&&t.startsWith("Phoenix.")&&ls[t.split(".")[1]]}maybeRuntimeHook(t){let e=document.querySelector(`script[${lt}="${CSS.escape(t)}"]`);if(!e)return;let i=window[`phx_hook_${t}`];if(!i||typeof i!="function"){O("a runtime hook must be a function",e);return}let s=i();if(s&&(typeof s=="object"||typeof s=="function"))return s;O("runtime hook must return an object with hook callbacks or an instance of ViewHook",e)}isUnloaded(){return this.u
Line truncated
|
||||||
|
`)?`
|
||||||
|
`:"",ne=q!==""&&!d.endsWith(`
|
||||||
|
`)?`
|
||||||
|
`:"",He=`${se}${d}${ne}`;this.editor.executeEdits("bds-insert-content",[{range:k,text:He,forceMoveMarkers:!0}]),this.editor.focus()},C().then(async l=>{!this.host||!this.textarea||(await this.waitForMonacoVisibleSize(),E(l),this.editor=l.editor.create(this.host,{value:this.textarea.value||"",language:this.language,theme:"bds-theme",automaticLayout:!0,minimap:{enabled:!1},scrollBeyondLastLine:!1,wordWrap:this.wordWrap,lineNumbers:"on",lineNumbersMinChars:3,fontSize:14,fontFamily:"'Cascadia Code', 'Consolas', 'Courier New', monospace",padding:{top:12,bottom:12},roundedSelection:!1,renderLineHighlight:"line",formatOnPaste:!0,cursorStyle:"line",cursorBlinking:"smooth",quickSuggestions:this.language!=="markdown-with-macros",tabSize:2,insertSpaces:!0}),V.set(this.editorId||this.el.id,this.editor),l.editor.setTheme("bds-theme"),this.syncEditorFromTextarea(),this.layoutEditorSoon(),this.changeSubscription=this.editor.onDidChangeModelContent(()=>{this.isApplyingRemoteUpdate||this.queueSync()}),this.insertEvent&&this.handleEvent(this.insertEvent,this.handleInsert))}).catch(l=>{console.error("Failed to load Monaco editor",l)})},updated(){this.textarea=document.getElementById(this.el.dataset.monacoInputId)||this.el.querySelector("textarea"),this.host=this.el.querySelector(".monaco-editor-instance"),this.language=this.el.dataset.monacoLanguage||this.language||"plaintext",this.wordWrap=this.el.dataset.monacoWordWrap||this.wordWrap||"off",!(!this.editor||!this.textarea)&&(C().then(l=>{E(l),l.editor.setTheme("bds-theme"),this.editor.getModel()?.getLanguageId()!==this.language&&l.editor.setModelLanguage(this.editor.getModel(),this.language),this.editor.updateOptions({wordWrap:this.wordWrap})}),this.syncEditorFromTextarea(),this.layoutEditorSoon())},destroyed(){window.clearTimeout(this.syncTimer),this.visibleSizeObserver?.disconnect(),this.changeSubscription?.dispose(),V.delete(this.editorId||this.el.id),this.editor?.dispose()}},MonacoDiffEditor:{mounted(){this.host=this.el.querySelector(".monaco-diff-editor-instance"),this.originalInput=this.el.querySelector(".monaco-diff-original"),this.modifiedInput=this.el.querySelector(".monaco-diff-modified"),this.filePath=this.el.dataset.monacoDiffFilePath||"working-tree",this.language=this.el.dataset.monacoDiffLanguage||"plaintext",this.viewStyle=this.el.dataset.monacoDiffViewStyle||"inline",this.wordWrap=this.el.dataset.monacoDiffWordWrap||"off",this.hideUnchanged=this.el.dataset.monacoDiffHideUnchanged==="true",this.readValues=()=>({original:this.originalInput?.value||"",modified:this.modifiedInput?.value||""}),this.applyDataset=()=>{this.filePath=this.el.dataset.monacoDiffFilePath||"working-tree",this.language=this.el.dataset.monacoDiffLanguage||"plaintext",this.viewStyle=this.el.dataset.monacoDiffViewStyle||"inline",this.wordWrap=this.el.dataset.monacoDiffWordWrap||"off",this.hideUnchanged=this.el.dataset.monacoDiffHideUnchanged==="true"},this.setModels=l=>{let d=this.readValues();this.originalModel?.dispose(),this.modifiedModel?.dispose(),this.originalModel=l.editor.createModel(d.original,this.language,l.Uri.parse(Ce(this.filePath,"original"))),this.modifiedModel=l.editor.createModel(d.modified,this.language,l.Uri.parse(Ce(this.filePath,"modified"))),this.editor.setModel({original:this.originalModel,modified:this.modifiedModel}),this.lastFilePath=this.filePath},C().then(l=>{this.host&&(E(l),this.editor=l.editor.createDiffEditor(this.host,{theme:"bds-theme",automaticLayout:!0,readOnly:!0,renderSideBySide:this.viewStyle==="side-by-side",minimap:{enabled:!1},scrollBeyondLastLine:!1,lineNumbers:"on",diffCodeLens:!1,originalEditable:!1,wordWrap:this.wordWrap,hideUnchangedRegions:{enabled:this.hideUnchanged},ignoreTrimWhitespace:!1}),this.setModels(l))}).catch(l=>{console.error("Failed to load Monaco diff editor",l)})},updated(){this.host=this.el.querySelector(".monaco-diff-editor-instance"),this.originalInput=this.el.querySelector(".monaco-diff-original"),this.modifiedInput=this.el.querySelector(".monaco-diff-modified"),this.applyDataset(),this.editor&&C().then(l=>{if(E(l),l.editor.setTheme("bds-theme"),this.editor.updateOptions({renderSideBySide:this.viewStyle==="side-by-side",wordWrap:this.wordWrap,hideUnchangedRegions:{enabled:this.hideUnchanged}}),this.lastFilePath!==this.filePath){this.setModels(l);return}let d=this.readValues();this.originalModel&&this.originalModel.getLanguageId()!==this.language&&l.editor.setModelLanguage(this.originalModel,this.language),this.modifiedModel&&this.modifiedModel.getLanguageId()!==this.language&&l.editor.setModelLanguage(this.modifiedModel,this.language),this.originalModel&&this.originalModel.getValue()!==d.original&&this.originalModel.setValue(d.original),this.modifiedModel&&this.modifiedModel.getValue()!==d.modified&&this.modifiedModel.setValue(d.modified)})},destroyed(){this.originalModel?.dispose(),this.modifiedModel?.dispose(),this.editor?.dispose()}}},P=new Ei("/live",Nt,{params:{_csrf_token:t},hooks:H,metadata:{keydown:l=>({key:l.key,met
Line truncated
|
||||||
-8527
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
|
||||||
|
|||||||
+119
-22
@@ -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