commit f9a59e9a663c60857d122eb500914419e00144b3 Author: whitlocktech Date: Fri Jun 5 20:53:53 2026 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7003292 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# ── Rust / Cargo ────────────────────────────────────────────────────────────── +target/ +Cargo.lock + +# ── Node / Vite ─────────────────────────────────────────────────────────────── +node_modules/ +dist/ +.vite/ + +# ── C# sidecar ──────────────────────────────────────────────────────────────── +sidecar/bin/ +sidecar/obj/ +*.user +*.suo + +# ── Tauri compiled binaries ─────────────────────────────────────────────────── +# The compiled sidecar exe is a build artifact — rebuild with dotnet publish +src-tauri/binaries/ + +# ── UO asset art (large binary files, user-supplied) ───────────────────────── +# BMPs are sourced from the user's UO client — not committed to the repo. +# The XML manifests (gumps.xml, script_gumps.xml) ARE committed. +UO Gumps/*.bmp +UO artwork/*.bmp +UO artwork/*.png +UO artwork/*.tga +UO artwork/*.dds + +# ── SQLite databases ────────────────────────────────────────────────────────── +*.db +*.db-shm +*.db-wal +*.sqlite + +# ── OS files ────────────────────────────────────────────────────────────────── +.DS_Store +Thumbs.db +desktop.ini + +# ── Editor / IDE ────────────────────────────────────────────────────────────── +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# ── Environment / secrets ───────────────────────────────────────────────────── +.env +.env.local +.env.*.local + +# ── Tauri dev artifacts ─────────────────────────────────────────────────────── +src-tauri/.cargo-lock diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..158cf89 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,674 @@ +# Artificer's Scrollwork — CLAUDE.md + +> Local UO development workbench for ServUO shard developers. +> Combines script analysis, asset reference, Gump rendering, and interactive flow tracing. +> Built with Tauri (Rust shell) + React frontend + C# sidecar. + +--- + +## Project Identity + +**Name:** Artificer's Scrollwork +**Short name / slug:** artificers-scrollwork +**Abbreviation:** ASW +**License:** Open source (MIT) +**Platform:** Windows desktop (Tauri) +**Target user:** ServUO shard developer working locally — no live shard connection required + +--- + +## Repository Structure + +``` +artificers-scrollwork/ +├── CLAUDE.md ← this file, always read first +├── src-tauri/ ← Rust/Tauri backend +│ ├── src/ +│ │ ├── main.rs +│ │ ├── config.rs ← path config, validation +│ │ ├── db.rs ← SQLite via rusqlite +│ │ ├── assets/ +│ │ │ ├── mod.rs +│ │ │ ├── mul_reader.rs ← .mul file parser +│ │ │ ├── uop_reader.rs ← .uop file parser +│ │ │ ├── art.rs ← art.mul / artLegacyMUL.uop +│ │ │ ├── gumpart.rs ← gumpart.mul / gumpartLegacyMUL.uop +│ │ │ ├── tiledata.rs ← tiledata.mul +│ │ │ ├── hues.rs ← hues.mul +│ │ │ ├── cliloc.rs ← cliloc.enu string table +│ │ │ ├── anim.rs ← animX.mul mobile sprites +│ │ │ └── multi.rs ← multi.mul structures +│ │ ├── ipc/ +│ │ │ ├── mod.rs +│ │ │ └── sidecar.rs ← C# sidecar process management +│ │ └── commands/ ← Tauri command handlers (IPC to frontend) +│ │ ├── config_commands.rs +│ │ ├── asset_commands.rs +│ │ ├── script_commands.rs +│ │ └── gump_commands.rs +│ └── Cargo.toml +├── sidecar/ ← C# Roslyn sidecar +│ ├── ArtificersScrollwork.Sidecar.csproj +│ ├── Program.cs ← stdin/stdout JSON IPC entry point +│ ├── Parsing/ +│ │ ├── ScriptIndexer.cs ← walks Scripts/ tree, indexes .cs files +│ │ ├── RoslynParser.cs ← Roslyn AST parsing +│ │ ├── ClassInfo.cs ← data model: class, base, props, methods +│ │ ├── MethodInfo.cs +│ │ └── CallChainTracer.cs ← traces method call chains across files +│ ├── Gumps/ +│ │ ├── GumpExtractor.cs ← extracts Add* calls from Gump constructors +│ │ └── GumpDrawList.cs ← serializable draw call list → JSON +│ └── Models/ +│ ├── ScriptIndex.cs +│ ├── DrawCall.cs +│ └── FlowNode.cs +├── src/ ← React frontend +│ ├── main.tsx +│ ├── App.tsx +│ ├── components/ +│ │ ├── layout/ +│ │ │ ├── AppShell.tsx ← three-pane shell +│ │ │ ├── LeftPanel.tsx ← browser tree +│ │ │ ├── CenterPanel.tsx ← flow / asset / gump view +│ │ │ └── RightPanel.tsx ← properties / fake data inputs +│ │ ├── asset/ +│ │ │ ├── StaticBrowser.tsx ← static tile browser +│ │ │ ├── MobileBrowser.tsx ← mobile browser +│ │ │ ├── ItemPreview.tsx ← single item art + metadata +│ │ │ └── MobilePreview.tsx ← single mobile sprite +│ │ ├── script/ +│ │ │ ├── ScriptTree.tsx ← class/method/hook tree +│ │ │ ├── ClassDetail.tsx ← class info panel +│ │ │ └── MethodDetail.tsx ← method info panel +│ │ ├── flow/ +│ │ │ ├── FlowViewer.tsx ← call chain flow diagram +│ │ │ ├── FlowNode.tsx ← individual node in flow +│ │ │ └── FakeDataInputs.tsx ← right panel fake data widgets +│ │ ├── gump/ +│ │ │ ├── GumpRenderer.tsx ← 800x600 canvas Gump renderer +│ │ │ └── GumpCanvas.tsx ← raw canvas draw call executor +│ │ └── config/ +│ │ └── ConfigScreen.tsx ← UO root + ServUO Scripts path setup +│ ├── hooks/ +│ │ ├── useAssets.ts +│ │ ├── useScripts.ts +│ │ └── useGump.ts +│ ├── store/ +│ │ └── appStore.ts ← Zustand global state +│ └── types/ +│ ├── assets.ts +│ ├── scripts.ts +│ ├── gump.ts +│ └── flow.ts +├── tauri.conf.json +├── package.json +└── README.md +``` + +--- + +## Architecture + +``` +┌─────────────────────────────────────────────┐ +│ React Frontend │ +│ LeftPanel | CenterPanel | RightPanel │ +│ Asset Browser | Flow Viewer | Gump Renderer │ +└────────────────────┬────────────────────────┘ + │ Tauri invoke() IPC +┌────────────────────▼────────────────────────┐ +│ Rust / Tauri Core │ +│ config.rs | db.rs | asset parsers │ +│ mul/uop readers | SQLite index │ +└────────────────────┬────────────────────────┘ + │ stdin/stdout JSON IPC +┌────────────────────▼────────────────────────┐ +│ C# Roslyn Sidecar │ +│ ScriptIndexer | RoslynParser │ +│ CallChainTracer | GumpExtractor │ +└─────────────────────────────────────────────┘ +``` + +### IPC Protocol — Rust ↔ C# Sidecar + +All messages are newline-delimited JSON over stdin/stdout. + +**Request format:** +```json +{ "id": "uuid", "command": "parse_class", "args": { "file": "path/to/Script.cs", "class": "PaperDollGump" } } +``` + +**Response format:** +```json +{ "id": "uuid", "ok": true, "data": { ... } } +{ "id": "uuid", "ok": false, "error": "message" } +``` + +**Commands:** +- `index_scripts` — walk Scripts/ tree, build full index +- `get_class` — return ClassInfo for a named class +- `trace_method` — return FlowNode tree for a method entry point +- `extract_gump` — return GumpDrawList for a Gump class constructor +- `search` — full-text search across indexed scripts + +--- + +## Configuration + +Stored in SQLite `config` table (key/value). + +| Key | Description | +|---|---| +| `uo_root` | Absolute path to UO client folder | +| `seruo_scripts` | Absolute path to ServUO Scripts/ folder | +| `index_version` | Hash/timestamp of last script index | +| `asset_format` | `mul` or `uop` (autodetected) | + +### Path Validation + +On config save, validate: + +**UO Root — required files:** +``` +art.mul OR artLegacyMUL.uop +gumpart.mul OR gumpartLegacyMUL.uop +tiledata.mul +hues.mul +cliloc.enu +unifont.mul +``` + +**UO Root — optional (warn if missing):** +``` +anim.mul / animX.mul → mobile animations +multi.mul → structures +map0.mul → world map +radarcol.mul → minimap colors +``` + +**ServUO Scripts — required:** +``` +/ must exist and contain at least one .cs file +``` + +Report validation results per-file with ✅ / ⚠️ / ❌ status. + +--- + +## Data Models + +### Asset Types + +```typescript +// types/assets.ts + +interface TileInfo { + id: number; // item ID / static ID + name: string; // from tiledata.mul + flags: number; // tiledata flags bitmask + weight: number; + quality: number; + height: number; + hue: number; + artData: ImageData; // decoded pixel data +} + +interface MobileInfo { + bodyId: number; + name: string; + flags: number; + frames: ImageData[]; // animation frames +} + +interface HueInfo { + id: number; + name: string; + colors: number[]; // 32-entry color table +} +``` + +### Script Types + +```typescript +// types/scripts.ts + +interface ClassInfo { + name: string; + namespace: string; + filePath: string; + baseClass: string | null; + interfaces: string[]; + properties: PropertyInfo[]; + methods: MethodInfo[]; + attributes: string[]; + isGump: boolean; + isMobile: boolean; + isItem: boolean; +} + +interface MethodInfo { + name: string; + returnType: string; + parameters: ParameterInfo[]; + isOverride: boolean; + isVirtual: boolean; + callsGump: boolean; // does this method send a Gump? + gumpClass: string | null; // which Gump class if so +} + +interface FlowNode { + id: string; + type: 'method_call' | 'condition' | 'gump_send' | 'return' | 'property_access'; + label: string; + children: FlowNode[]; + fakeInputKey?: string; // key for fake data injection if this node has an input + resolvedGump?: string; // Gump class name if type === 'gump_send' + assetRef?: number; // item/static ID if this node refs an asset +} +``` + +### Gump Types + +```typescript +// types/gump.ts + +type DrawCall = + | { type: 'background'; x: number; y: number; w: number; h: number; gumpId: number } + | { type: 'image'; x: number; y: number; gumpId: number; hue?: number } + | { type: 'label'; x: number; y: number; hue: number; text: string } + | { type: 'button'; x: number; y: number; normalId: number; pressedId: number; buttonId: number } + | { type: 'html'; x: number; y: number; w: number; h: number; text: string; hasBackground: boolean; hasScrollbar: boolean } + | { type: 'item'; x: number; y: number; itemId: number; hue?: number } + | { type: 'alpha_region'; x: number; y: number; w: number; h: number } + | { type: 'tiled_image'; x: number; y: number; w: number; h: number; gumpId: number } + | { type: 'checkbox'; x: number; y: number; inactiveId: number; activeId: number; checked: boolean; switchId: number } + | { type: 'radio'; x: number; y: number; inactiveId: number; activeId: number; checked: boolean; returnValue: number } + | { type: 'text_entry'; x: number; y: number; w: number; h: number; hue: number; entryId: number; initialText: string }; + +interface GumpDrawList { + className: string; + filePath: string; + isDynamic: boolean; // true if layout depends on runtime data + dynamicInputKeys: string[]; // fake data keys needed for dynamic Gumps + drawCalls: DrawCall[]; + width: number; // declared width if available + height: number; // declared height if available +} +``` + +--- + +## UI Layout + +### Three-Pane Shell + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ Artificer's Scrollwork [Config] [Index] │ +├─────────────────┬──────────────────────────┬─────────────────────┤ +│ LEFT PANEL │ CENTER PANEL │ RIGHT PANEL │ +│ ~280px │ flex-grow │ ~320px │ +│ │ │ │ +│ [Scripts] │ ┌────────────────────┐ │ Properties │ +│ [Statics] │ │ │ │ ───────────────── │ +│ [Mobiles] │ │ Flow / Asset / │ │ Fake Data Inputs │ +│ [Gumps] │ │ Gump View │ │ ───────────────── │ +│ │ │ │ │ Asset Metadata │ +│ Search box │ │ │ │ │ +│ │ │ │ │ │ +│ Tree │ └────────────────────┘ │ │ +│ │ │ │ +└─────────────────┴──────────────────────────┴─────────────────────┘ +``` + +### Center Panel Modes + +The center panel has distinct view modes, switched by what is selected in the left panel: + +| Mode | Triggered By | Shows | +|---|---|---| +| `asset_static` | Static selected | Tile art + metadata | +| `asset_mobile` | Mobile selected | Mobile sprite + stats | +| `script_class` | Class selected | Class detail, inheritance tree | +| `flow_method` | Method/hook selected | Call chain flow diagram | +| `gump_render` | Gump class or method sends Gump | 800x600 Gump canvas | + +### Gump Renderer + +- Fixed 800x600 HTML canvas +- Dark border/frame to indicate viewport bounds +- Renders draw calls top-to-bottom in order +- Dynamic Gumps show placeholder tiles with fake data input widgets in right panel +- "Refresh Gump" button re-renders with current fake data values + +--- + +## Asset Parsing — Rust Implementation Notes + +### .mul Format + +All .mul files share the same basic indexed format: +``` +idx.mul → index file: array of (offset: i32, length: i32, extra: i32) entries +.mul → data file: raw blocks at offsets given by index +``` + +Entry with `offset == -1` → entry does not exist (skip). + +### art.mul + +- Statics (items): ID offset = 0x4000 +- Each entry is a raw bitmap: `{ unknown: u16, width: u16, height: u16, lookup: [u16; height], data: [u16] }` +- Pixel format: 16-bit `0xARGB` (1-bit alpha, 5-bit RGB each) +- Transparent pixel = `0x0000` + +### gumpart.mul + +- Same index structure as art.mul +- Pixel format: same 16-bit `0xARGB` +- No offset for statics — IDs are direct + +### tiledata.mul + +- Land tiles: 428 blocks × 32 entries = 13,696 land entries +- Item tiles: blocks of 32 entries, each entry = `{ flags: u64, weight: u8, quality: u8, unknown: u16, unknown2: u8, quantity: u8, animId: u16, unknown3: u8, hue: u8, unknown4: u16, height: u8, name: [u8; 20] }` +- Block header = `u32` (skip it) + +### hues.mul + +- 375 blocks × 8 hues = 3,000 hues +- Each hue = `{ colors: [u16; 32], tableStart: u16, tableEnd: u16, name: [u8; 20] }` + +### .uop Format + +UOP is a container format used in newer clients: +``` +Header: { magic: u32=0x0050594D, version: u32, signature: u32, index_offset: u64, max_files: u32, tag: [u8; 36] } +Index block: { next_block: u64, file_count: u32, entries: [UOPEntry; file_count] } +UOPEntry: { data_offset: u64, header_length: u32, compressed_length: u32, decompressed_length: u32, hash: u64, crc: u32, compression: u16 } +``` + +Compression: `0` = none, `1` = zlib deflate. + +Hash function for UOP filenames (use to map asset IDs to UOP entries): +```rust +fn uop_hash(s: &str) -> u64 { + let s = s.to_lowercase(); + let (mut eax, mut ecx, mut edx, mut ebx, mut esi, mut edi) = + (0u32, 0u32, 0u32, s.len() as u32, 0u32, 0u32); + edx = 0; eax = edx; esi = eax; + ecx = 0x9E3779B9u32; + edi = ecx; esi = ecx; + for chunk in s.as_bytes().chunks(12) { + // standard UOP hash — implement from ClassicUO source + } + ((edi as u64) << 32) | (esi as u64) +} +``` + +Reference ClassicUO `UOFileUop.cs` for the full implementation. + +--- + +## C# Sidecar — Implementation Notes + +### Project Setup + +```xml + + + Exe + net8.0 + enable + enable + true + true + win-x64 + + + + + + +``` + +### Roslyn Parser Notes + +- Load all `.cs` files in Scripts/ into a `CSharpCompilation` for cross-file resolution +- Use `SemanticModel` for type resolution (base classes, method return types) +- `SyntaxTree` for structural traversal (Add* call extraction) +- Do NOT attempt to actually compile ServUO — use `WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))` with missing reference tolerance + +### GumpExtractor — Add* Call Mapping + +| C# Method | DrawCall type | +|---|---| +| `AddBackground(x, y, w, h, id)` | `background` | +| `AddImage(x, y, id)` | `image` | +| `AddImage(x, y, id, hue)` | `image` with hue | +| `AddLabel(x, y, hue, text)` | `label` | +| `AddHtml(x, y, w, h, text, bg, scroll)` | `html` | +| `AddHtmlLocalized(x, y, w, h, clilocId, ...)` | `html` (resolve via cliloc) | +| `AddButton(x, y, normalId, pressedId, btnId, ...)` | `button` | +| `AddItem(x, y, itemId)` | `item` | +| `AddItem(x, y, itemId, hue)` | `item` with hue | +| `AddAlphaRegion(x, y, w, h)` | `alpha_region` | +| `AddImageTiled(x, y, w, h, id)` | `tiled_image` | +| `AddCheck(x, y, inactId, actId, checked, switchId)` | `checkbox` | +| `AddRadio(x, y, inactId, actId, checked, returnVal)` | `radio` | +| `AddTextEntry(x, y, w, h, hue, entryId, text)` | `text_entry` | + +For dynamic Gumps (loop bodies, conditional Add* calls): +- Mark `isDynamic: true` +- Record the variable names that gate the dynamic calls as `dynamicInputKeys` +- Extract the static portions and emit placeholder draw calls for dynamic regions + +### CallChainTracer Notes + +- Entry point: a method name + class name +- Resolve method body via Roslyn +- Walk all `InvocationExpressionSyntax` nodes +- For each invocation: + - If `SendGump(new XxxGump(...))` → emit `gump_send` node, recurse into Gump constructor + - If calling another method in the same class → recurse + - If calling a method on a known ServUO base class → emit as leaf with label + - If an `if`/`else`/`switch` → emit `condition` node with branches + - Limit recursion depth to 8 to avoid infinite loops + +--- + +## Build & Development + +### Prerequisites + +- Rust stable (latest) +- Node.js 20+ +- .NET 8 SDK +- Tauri CLI: `cargo install tauri-cli` + +### Development Commands + +```bash +# Install JS deps +npm install + +# Build C# sidecar +cd sidecar && dotnet publish -c Release -r win-x64 --self-contained + +# Copy sidecar binary to Tauri resources +cp sidecar/bin/Release/net8.0/win-x64/publish/asw-sidecar.exe src-tauri/binaries/ + +# Run in dev mode +npm run tauri dev + +# Build release +npm run tauri build +``` + +### Tauri Sidecar Config + +In `tauri.conf.json`: +```json +{ + "tauri": { + "bundle": { + "externalBin": ["binaries/asw-sidecar"] + } + } +} +``` + +--- + +## Development Phases + +### Phase 1 — Foundation & Asset Reference +**Goal:** Working app that can browse UO assets + +- [ ] Tauri project scaffold with three-pane React shell +- [ ] Config screen — UO root + ServUO Scripts path, validation +- [ ] SQLite setup — config table, asset index tables +- [ ] Rust mul/uop parsers — art, gumpart, tiledata, hues, cliloc +- [ ] Asset indexer — walk tiledata, build SQLite records for all statics and mobiles +- [ ] Static browser — paginated grid with art preview and metadata +- [ ] Mobile browser — list with sprite preview +- [ ] Item/tile detail view — full metadata panel + +**Deliverable:** Browse every static and mobile in the UO client with art previews. + +--- + +### Phase 2 — Script Browser +**Goal:** Ingest and navigate ServUO scripts + +- [ ] C# sidecar project setup with Roslyn +- [ ] ScriptIndexer — walk Scripts/ tree, parse all .cs files, emit ClassInfo JSON +- [ ] Rust sidecar IPC layer — spawn sidecar, send/receive JSON commands +- [ ] SQLite script index — classes, methods, properties, file paths +- [ ] Re-index command with progress indicator +- [ ] Left panel script tree — namespaces → classes → methods +- [ ] Class detail view — inheritance chain, properties, methods list +- [ ] Method detail view — parameters, return type, attributes +- [ ] Search — cross-script full-text search + +**Deliverable:** Browse the entire ServUO Scripts/ tree with structured class and method views. + +--- + +### Phase 3 — Flow Tracer +**Goal:** Visualize call chains from any method entry point + +- [ ] CallChainTracer in C# sidecar +- [ ] FlowNode JSON serialization +- [ ] FlowViewer React component — tree/graph layout of call chain +- [ ] Condition nodes with true/false branches +- [ ] Asset reference inline — when flow references an Item ID, show thumbnail +- [ ] Hook index — surface well-known hooks (OnDoubleClick, OnSingleClick, OnDeath, etc.) as quick-launch entry points +- [ ] Fake data input system — right panel widgets for condition inputs +- [ ] Flow re-evaluation when fake data changes + +**Deliverable:** Click any method or hook, see the full call chain with fake data controls. + +--- + +### Phase 4 — Gump Renderer +**Goal:** Render any Gump class on an 800x600 canvas + +- [ ] GumpExtractor in C# sidecar +- [ ] GumpDrawList JSON serialization +- [ ] GumpCanvas React component — 800x600 HTML canvas +- [ ] Draw call executor — background, image, label, button, item, alpha, tiled, html, checkbox, radio, text entry +- [ ] gumpart.mul → ImageData conversion in Rust, served via Tauri command +- [ ] Hue application pipeline +- [ ] Static Gump rendering — fully static Add* calls +- [ ] Dynamic Gump rendering — fake data inputs drive dynamic draw call generation +- [ ] Gump link from flow tracer — gump_send nodes in flow open Gump renderer inline + +**Deliverable:** Any Gump class renders visually. Dynamic Gumps render with fake data. + +--- + +## Visual Design + +### Theme + +Dark theme. UO-adjacent aesthetic — aged parchment for text, deep slate/obsidian backgrounds, gold/amber accents. Feels like a scholar's workbench, not a generic dev tool. + +**Color palette (CSS variables):** +```css +--bg-base: #0e0d0b; +--bg-panel: #161410; +--bg-elevated: #1e1b16; +--bg-hover: #272318; +--border: #3a3020; +--border-accent: #6b5a2e; +--text-primary: #d4c49a; +--text-secondary: #8a7a5a; +--text-muted: #4a4030; +--accent-gold: #c8a84b; +--accent-gold-bright: #e8c86b; +--accent-red: #8b3a3a; +--accent-green: #3a6b3a; +--scrollbar-thumb: #3a3020; +``` + +**Typography:** +- UI labels: `'Cinzel'` (Google Fonts) — classical, serif, fits the UO aesthetic +- Code / script content: `'JetBrains Mono'` or `'Fira Code'` +- Body / metadata: `'EB Garamond'` + +### Gump Renderer Container + +The 800x600 canvas sits inside a styled container: +``` +┌─────────────────────────────────────┐ +│ PaperDollGump │ ← class name header +│ Scripts/Gumps/PaperDollGump.cs │ ← file path +├─────────────────────────────────────┤ +│ ┌───────────────────────────────┐ │ +│ │ │ │ +│ │ 800 × 600 canvas │ │ +│ │ │ │ +│ └───────────────────────────────┘ │ +│ [Refresh] [Dynamic inputs →] │ +└─────────────────────────────────────┘ +``` + +--- + +## Claude Code Session Discipline + +- **Always read this file first** before any code changes +- **One phase at a time** — do not begin Phase N+1 until Phase N deliverable is working +- **No placeholder implementations** — every function either works or is explicitly `todo!()` / `unimplemented!()` +- **Test asset parsing incrementally** — parse one file type, verify output in UI, then move to next +- **C# sidecar is a black box to Rust** — Rust only speaks JSON IPC, never references sidecar internals +- **SQLite is the source of truth** for indexed data — never re-parse files at runtime if SQLite has the data +- **Fake data state lives in React** — Zustand store, never in Rust or the sidecar +- **All Tauri commands return `Result`** — errors surface to the frontend as readable messages +- **Asset pixel data is never stored in SQLite** — always read from .mul/.uop at request time, cached in Rust memory for the session + +--- + +## Known Constraints & Decisions + +| Decision | Rationale | +|---|---| +| Gump canvas fixed at 800×600 | Matches UO's original coordinate system; no scaling math needed | +| C# sidecar is a separate process | Roslyn requires .NET; keeping it isolated prevents Rust/C# FFI complexity | +| No live shard connection | Tool is purely local dev; no TCP/UDP to a running ServUO instance | +| Windows only (initial) | Tauri supports cross-platform but ServUO/.NET/UO client are Windows-primary | +| ServUO scripts not compiled | Roslyn used in analysis mode only; no attempt to actually build/run scripts | +| Open source MIT | Consistent with all Whitlocktech projects | + +--- + +## Reference Resources + +- **ClassicUO** (open source UO client, C#) — reference for all .mul/.uop parsers: https://github.com/ClassicUO/ClassicUO +- **UOFiddler** (open source UO asset editor, C#) — reference for tiledata, art, gump rendering: https://github.com/polserver/UOFiddler +- **ServUO** — target script codebase: https://github.com/ServUO/ServUO +- **Roslyn docs** — https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/ +- **Tauri docs** — https://tauri.app/ +- **mul format reference** — http://docs.polserver.com/packets/index.php (UO file format documentation) diff --git a/CentrED_win_linux_server_client.zip b/CentrED_win_linux_server_client.zip new file mode 100644 index 0000000..f509a77 Binary files /dev/null and b/CentrED_win_linux_server_client.zip differ diff --git a/CentrED_win_linux_server_client/CentrED_linux_i386_gtk2_0-6-3.tbz b/CentrED_win_linux_server_client/CentrED_linux_i386_gtk2_0-6-3.tbz new file mode 100644 index 0000000..3206662 Binary files /dev/null and b/CentrED_win_linux_server_client/CentrED_linux_i386_gtk2_0-6-3.tbz differ diff --git a/CentrED_win_linux_server_client/CentrED_win32_0-6-3.exe b/CentrED_win_linux_server_client/CentrED_win32_0-6-3.exe new file mode 100644 index 0000000..f06b194 Binary files /dev/null and b/CentrED_win_linux_server_client/CentrED_win32_0-6-3.exe differ diff --git a/CentrED_win_linux_server_client/cedserver_linux_i386_0-6-3.tbz b/CentrED_win_linux_server_client/cedserver_linux_i386_0-6-3.tbz new file mode 100644 index 0000000..6fe8d43 Binary files /dev/null and b/CentrED_win_linux_server_client/cedserver_linux_i386_0-6-3.tbz differ diff --git a/CentrED_win_linux_server_client/cedserver_win32_0-6-3.zip b/CentrED_win_linux_server_client/cedserver_win32_0-6-3.zip new file mode 100644 index 0000000..976ae0f Binary files /dev/null and b/CentrED_win_linux_server_client/cedserver_win32_0-6-3.zip differ diff --git a/CentrED_win_linux_server_client/cedserver_win32_0-6-3/cedserver.exe b/CentrED_win_linux_server_client/cedserver_win32_0-6-3/cedserver.exe new file mode 100644 index 0000000..0bf5a3a Binary files /dev/null and b/CentrED_win_linux_server_client/cedserver_win32_0-6-3/cedserver.exe differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ff8b82 --- /dev/null +++ b/README.md @@ -0,0 +1,195 @@ +# Artificer's Scrollwork + +A local development workbench for ServUO shard developers. Browse UO client assets (statics, mobiles, gumps), navigate ServUO C# scripts with full class and method trees, trace method call chains as interactive flow diagrams, and render Gump classes visually — all without a live shard connection. + +Built with **Tauri** (Rust shell) + **React** frontend + **C# Roslyn sidecar**. + +--- + +## Requirements + +You need the following installed before you can build or run the app. + +### 1. Rust (stable) + +Download and run the installer from https://rustup.rs + +During installation, accept the default options. When it finishes, open a new terminal and verify: + +``` +rustc --version +cargo --version +``` + +### 2. Node.js 20 or later + +Download the LTS installer from https://nodejs.org + +After installing, verify: + +``` +node --version +npm --version +``` + +### 3. .NET 8 SDK + +Download from https://dotnet.microsoft.com/download/dotnet/8.0 + +Choose the **SDK** (not Runtime) for Windows x64. After installing, verify: + +``` +dotnet --version +``` + +Should print `8.x.x` or higher. + +### 4. Tauri CLI + +After Rust is installed, run: + +``` +cargo install tauri-cli +``` + +This takes a few minutes. Verify with: + +``` +cargo tauri --version +``` + +### 5. WebView2 Runtime + +Required by Tauri on Windows. Most Windows 10/11 machines already have it via Microsoft Edge. If the app fails to launch, download it from: + +https://developer.microsoft.com/microsoft-edge/webview2/ + +--- + +## Getting Started + +### Clone the repository + +``` +git clone https://gitea.whitlocktech.com/whitlocktech/Artificers-Scrollwork.git +cd Artificers-Scrollwork +``` + +### Install JavaScript dependencies + +``` +npm install +``` + +### Build the C# sidecar + +The sidecar handles Roslyn script parsing. It must be compiled before running the app. + +``` +cd sidecar +dotnet publish -c Release -r win-x64 --self-contained +cd .. +``` + +Then copy the compiled binary into the Tauri binaries folder: + +``` +copy sidecar\bin\Release\net8.0\win-x64\publish\asw-sidecar.exe src-tauri\binaries\asw-sidecar-x86_64-pc-windows-msvc.exe +``` + +--- + +## Running in Dev Mode + +Dev mode runs the app with hot reload. The Rust backend and React frontend reload automatically as you make changes. + +Make sure you have completed the sidecar build step above, then run: + +``` +npm run tauri dev +``` + +The app window will open. First launch may take a minute while Rust compiles the backend. + +On first run, use the Config screen (gear icon, top right) to set: +- **UO Root** — path to your Ultima Online client folder (must contain `art.mul` or `artLegacyMUL.uop`) +- **ServUO Scripts** — path to your ServUO `Scripts/` folder + +--- + +## Building the MSI Installer + +### Windows Defender note + +The Tauri bundler patches the compiled `.exe` immediately after Rust finishes building it. Windows Defender or the Program Compatibility Assistant can lock the file during this window and cause the build to fail with error 1224. + +**Before building**, add the target folder to Windows Defender exclusions: + +1. Open **Windows Security** → **Virus & threat protection** → **Manage settings** +2. Scroll to **Exclusions** → **Add or remove exclusions** +3. Add this folder: `\src-tauri\target` + +If the build still fails, run this in an **Administrator** PowerShell before building: + +```powershell +Stop-Service PcaSvc -Force +npm run tauri build +Start-Service PcaSvc +``` + +### Build steps + +Make sure the sidecar is compiled and copied (see above), then: + +``` +npm run tauri build +``` + +The finished MSI will be at: + +``` +src-tauri\target\release\bundle\msi\Artificer's Scrollwork_0.1.0_x64_en-US.msi +``` + +Double-click the MSI to install. The app will appear in your Start menu as **Artificer's Scrollwork**. + +--- + +## Project Structure + +``` +artificers-scrollwork/ +├── src/ React frontend +├── src-tauri/ Rust / Tauri backend +│ ├── src/ +│ │ ├── assets/ .mul / .uop file parsers +│ │ ├── commands/ Tauri IPC command handlers +│ │ └── ipc/ C# sidecar process management +│ └── binaries/ Compiled sidecar binary (git-ignored) +├── sidecar/ C# Roslyn sidecar +│ ├── Parsing/ Script indexer and call chain tracer +│ └── Gumps/ Gump constructor extractor +├── UO Gumps/ Bundled gump BMP art + XML manifests +└── UO artwork/ Bundled static tile BMP art +``` + +--- + +## Updating the Sidecar + +If you change any C# code in `sidecar/`, you must recompile and copy the binary before the changes take effect: + +``` +cd sidecar +dotnet publish -c Release -r win-x64 --self-contained +cd .. +copy sidecar\bin\Release\net8.0\win-x64\publish\asw-sidecar.exe src-tauri\binaries\asw-sidecar-x86_64-pc-windows-msvc.exe +``` + +Then restart dev mode or rebuild the MSI. + +--- + +## License + +MIT — see LICENSE file. diff --git a/UO Gumps/gumps.xml b/UO Gumps/gumps.xml new file mode 100644 index 0000000..82153ad --- /dev/null +++ b/UO Gumps/gumps.xml @@ -0,0 +1,935 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UO Gumps/script_gumps.xml b/UO Gumps/script_gumps.xml new file mode 100644 index 0000000..fc18f3c --- /dev/null +++ b/UO Gumps/script_gumps.xml @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..f41517f --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + + + + Artificer's Scrollwork + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..98b8815 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2073 @@ +{ + "name": "artificers-scrollwork", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "artificers-scrollwork", + "version": "0.1.0", + "dependencies": { + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2", + "@tauri-apps/plugin-shell": "^2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zustand": "^4.5.2" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "typescript": "^5.5.3", + "vite": "^5.3.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tauri-apps/api": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz", + "integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz", + "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.11.2", + "@tauri-apps/cli-darwin-x64": "2.11.2", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.2", + "@tauri-apps/cli-linux-arm64-musl": "2.11.2", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2", + "@tauri-apps/cli-linux-x64-gnu": "2.11.2", + "@tauri-apps/cli-linux-x64-musl": "2.11.2", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.2", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.2", + "@tauri-apps/cli-win32-x64-msvc": "2.11.2" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz", + "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz", + "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz", + "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz", + "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz", + "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz", + "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz", + "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz", + "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz", + "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz", + "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz", + "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz", + "integrity": "sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, + "node_modules/@tauri-apps/plugin-shell": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz", + "integrity": "sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.10.1" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.30", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.30.tgz", + "integrity": "sha512-3ek6mwJL5/VBewBcY4S66cqlCtK3qi4WIq37Z0m/NHw1hjhI7274Mx1qz/+ggSzyBCOEf7eHjBN6INjPAWYfYw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.368", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz", + "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c36c984 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "artificers-scrollwork", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2", + "@tauri-apps/plugin-shell": "^2", + "zustand": "^4.5.2", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "typescript": "^5.5.3", + "vite": "^5.3.4" + } +} diff --git a/sidecar/ArtificersScrollwork.Sidecar.csproj b/sidecar/ArtificersScrollwork.Sidecar.csproj new file mode 100644 index 0000000..378d871 --- /dev/null +++ b/sidecar/ArtificersScrollwork.Sidecar.csproj @@ -0,0 +1,16 @@ + + + Exe + net8.0 + enable + enable + true + true + win-x64 + asw-sidecar + + + + + + diff --git a/sidecar/Gumps/GumpExtractor.cs b/sidecar/Gumps/GumpExtractor.cs new file mode 100644 index 0000000..1e5cbd9 --- /dev/null +++ b/sidecar/Gumps/GumpExtractor.cs @@ -0,0 +1,13 @@ +namespace ArtificersScrollwork.Sidecar.Gumps; + +/// +/// Extracts Add* calls from Gump constructors and produces a GumpDrawList. +/// TODO (Phase 4): Full implementation. +/// +public class GumpExtractor +{ + // Phase 4 implementation placeholder. + // Will walk the constructor body of a Gump class, + // map Add* method calls to DrawCall records, + // and detect dynamic regions (loop/conditional bodies). +} diff --git a/sidecar/Models/ClassInfo.cs b/sidecar/Models/ClassInfo.cs new file mode 100644 index 0000000..ab15fa2 --- /dev/null +++ b/sidecar/Models/ClassInfo.cs @@ -0,0 +1,22 @@ +namespace ArtificersScrollwork.Sidecar.Models; + +public record ClassInfo( + string Name, + string Namespace, + string FilePath, + string? BaseClass, + List Interfaces, + List Properties, + List Methods, + List Attributes, + bool IsGump, + bool IsMobile, + bool IsItem +); + +public record PropertyInfo( + string Name, + string Type, + bool HasGetter, + bool HasSetter +); diff --git a/sidecar/Models/DrawCall.cs b/sidecar/Models/DrawCall.cs new file mode 100644 index 0000000..679e17d --- /dev/null +++ b/sidecar/Models/DrawCall.cs @@ -0,0 +1,37 @@ +namespace ArtificersScrollwork.Sidecar.Models; + +/// Discriminated union of all Gump draw call types. +public abstract record DrawCall(string Type); + +public record BackgroundDrawCall(int X, int Y, int W, int H, int GumpId) + : DrawCall("background"); + +public record ImageDrawCall(int X, int Y, int GumpId, int? Hue = null) + : DrawCall("image"); + +public record LabelDrawCall(int X, int Y, int Hue, string Text) + : DrawCall("label"); + +public record ButtonDrawCall(int X, int Y, int NormalId, int PressedId, int ButtonId) + : DrawCall("button"); + +public record HtmlDrawCall(int X, int Y, int W, int H, string Text, bool HasBackground, bool HasScrollbar) + : DrawCall("html"); + +public record ItemDrawCall(int X, int Y, int ItemId, int? Hue = null) + : DrawCall("item"); + +public record AlphaRegionDrawCall(int X, int Y, int W, int H) + : DrawCall("alpha_region"); + +public record TiledImageDrawCall(int X, int Y, int W, int H, int GumpId) + : DrawCall("tiled_image"); + +public record CheckboxDrawCall(int X, int Y, int InactiveId, int ActiveId, bool Checked, int SwitchId) + : DrawCall("checkbox"); + +public record RadioDrawCall(int X, int Y, int InactiveId, int ActiveId, bool Checked, int ReturnValue) + : DrawCall("radio"); + +public record TextEntryDrawCall(int X, int Y, int W, int H, int Hue, int EntryId, string InitialText) + : DrawCall("text_entry"); diff --git a/sidecar/Models/FlowNode.cs b/sidecar/Models/FlowNode.cs new file mode 100644 index 0000000..ddf952f --- /dev/null +++ b/sidecar/Models/FlowNode.cs @@ -0,0 +1,11 @@ +namespace ArtificersScrollwork.Sidecar.Models; + +public record FlowNode( + string Id, + string Type, // method_call | condition | gump_send | return | property_access + string Label, + List Children, + string? FakeInputKey = null, + string? ResolvedGump = null, + int? AssetRef = null +); diff --git a/sidecar/Models/MethodInfo.cs b/sidecar/Models/MethodInfo.cs new file mode 100644 index 0000000..0e5833b --- /dev/null +++ b/sidecar/Models/MethodInfo.cs @@ -0,0 +1,16 @@ +namespace ArtificersScrollwork.Sidecar.Models; + +public record MethodInfo( + string Name, + string ReturnType, + List Parameters, + bool IsOverride, + bool IsVirtual, + bool CallsGump, + string? GumpClass +); + +public record ParameterInfo( + string Name, + string Type +); diff --git a/sidecar/Models/ScriptIndex.cs b/sidecar/Models/ScriptIndex.cs new file mode 100644 index 0000000..98bf12f --- /dev/null +++ b/sidecar/Models/ScriptIndex.cs @@ -0,0 +1,17 @@ +namespace ArtificersScrollwork.Sidecar.Models; + +public record ScriptIndex( + int ClassCount, + int FileCount, + List Classes +); + +public record ClassSummary( + string Name, + string Namespace, + string FilePath, + string? BaseClass, + bool IsGump, + bool IsMobile, + bool IsItem +); diff --git a/sidecar/Parsing/CallChainTracer.cs b/sidecar/Parsing/CallChainTracer.cs new file mode 100644 index 0000000..2ba87ea --- /dev/null +++ b/sidecar/Parsing/CallChainTracer.cs @@ -0,0 +1,365 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using ArtificersScrollwork.Sidecar.Models; + +namespace ArtificersScrollwork.Sidecar.Parsing; + +/// +/// Traces method call chains across ServUO scripts using Roslyn and produces a FlowNode tree. +/// Entry point: TraceMethod(className, methodName). +/// Max recursion depth: 8 levels. +/// +public class CallChainTracer +{ + private const int MaxDepth = 8; + + private readonly CSharpCompilation _compilation; + private readonly Dictionary _classMap; + + public CallChainTracer(CSharpCompilation compilation) + { + _compilation = compilation; + _classMap = BuildClassMap(); + } + + // ── Public entry point ──────────────────────────────────────────────────── + + public FlowNode TraceMethod(string className, string methodName) + { + if (!_classMap.TryGetValue(className, out var entry)) + throw new KeyNotFoundException($"Class '{className}' not found"); + + var methodSyntax = entry.Syntax.Members + .OfType() + .FirstOrDefault(m => m.Identifier.Text == methodName) + ?? throw new KeyNotFoundException($"Method '{methodName}' not found in '{className}'"); + + var rootNode = new FlowNode( + Id: NewId(), + Type: "method_call", + Label: $"{className}.{methodName}", + Children: TraceMethodBody(methodSyntax, entry.Model, depth: 0), + FakeInputKey: null, + ResolvedGump: null, + AssetRef: null + ); + + return rootNode; + } + + // ── Internal traversal ──────────────────────────────────────────────────── + + private List TraceMethodBody( + MethodDeclarationSyntax method, + SemanticModel model, + int depth) + { + if (depth >= MaxDepth) return []; + if (method.Body is null && method.ExpressionBody is null) return []; + + SyntaxNode body = (SyntaxNode?)method.Body ?? method.ExpressionBody!; + return TraceStatements(body.ChildNodes(), model, depth); + } + + private List TraceStatements( + IEnumerable nodes, + SemanticModel model, + int depth) + { + var result = new List(); + foreach (var node in nodes) + { + var children = TraceNode(node, model, depth); + result.AddRange(children); + } + return result; + } + + private List TraceNode(SyntaxNode node, SemanticModel model, int depth) + { + return node switch + { + IfStatementSyntax ifStmt => [TraceIf(ifStmt, model, depth)], + SwitchStatementSyntax sw => [TraceSwitch(sw, model, depth)], + ReturnStatementSyntax ret => [TraceReturn(ret, model, depth)], + ExpressionStatementSyntax expr => TraceExpressionStatement(expr, model, depth), + LocalDeclarationStatementSyntax local => TraceLocal(local, model, depth), + ForEachStatementSyntax forEach => [TraceForEach(forEach, model, depth)], + ForStatementSyntax forStmt => [TraceFor(forStmt, model, depth)], + BlockSyntax block => TraceStatements(block.Statements, model, depth), + _ => [], + }; + } + + // ── Statement handlers ──────────────────────────────────────────────────── + + private FlowNode TraceIf(IfStatementSyntax ifStmt, SemanticModel model, int depth) + { + var conditionText = ifStmt.Condition.ToString().Trim(); + var fakeKey = ExtractFakeInputKey(ifStmt.Condition); + + var trueChildren = TraceNode(ifStmt.Statement, model, depth + 1); + var falseChildren = ifStmt.Else is not null + ? TraceNode(ifStmt.Else.Statement, model, depth + 1) + : []; + + var branches = new List(); + if (trueChildren.Count > 0) + branches.Add(new FlowNode(NewId(), "branch_true", "true", trueChildren)); + if (falseChildren.Count > 0) + branches.Add(new FlowNode(NewId(), "branch_false", "false", falseChildren)); + + return new FlowNode( + Id: NewId(), + Type: "condition", + Label: $"if ({Truncate(conditionText, 60)})", + Children: branches, + FakeInputKey: fakeKey, + ResolvedGump: null, + AssetRef: null + ); + } + + private FlowNode TraceSwitch(SwitchStatementSyntax sw, SemanticModel model, int depth) + { + var conditionText = sw.Expression.ToString().Trim(); + var branches = new List(); + + foreach (var section in sw.Sections) + { + var label = string.Join(", ", section.Labels.Select(l => l.ToString().Trim())); + var sectionChildren = TraceStatements(section.Statements, model, depth + 1); + if (sectionChildren.Count > 0) + branches.Add(new FlowNode(NewId(), "branch_case", label, sectionChildren)); + } + + return new FlowNode( + Id: NewId(), + Type: "condition", + Label: $"switch ({Truncate(conditionText, 60)})", + Children: branches, + FakeInputKey: ExtractFakeInputKey(sw.Expression), + ResolvedGump: null, + AssetRef: null + ); + } + + private FlowNode TraceReturn(ReturnStatementSyntax ret, SemanticModel model, int depth) + { + var label = ret.Expression is not null + ? $"return {Truncate(ret.Expression.ToString(), 50)}" + : "return"; + + return new FlowNode(NewId(), "return", label, []); + } + + private List TraceExpressionStatement( + ExpressionStatementSyntax expr, + SemanticModel model, + int depth) + { + return TraceExpression(expr.Expression, model, depth); + } + + private List TraceLocal( + LocalDeclarationStatementSyntax local, + SemanticModel model, + int depth) + { + // Only interesting if the initializer contains a method call + var nodes = new List(); + foreach (var variable in local.Declaration.Variables) + { + if (variable.Initializer?.Value is InvocationExpressionSyntax invoc) + nodes.AddRange(TraceInvocation(invoc, model, depth, label: variable.Identifier.Text + " = …")); + } + return nodes; + } + + private FlowNode TraceForEach(ForEachStatementSyntax forEach, SemanticModel model, int depth) + { + var label = $"foreach ({forEach.Type} {forEach.Identifier} in {Truncate(forEach.Expression.ToString(), 40)})"; + var children = TraceNode(forEach.Statement, model, depth + 1); + return new FlowNode(NewId(), "condition", label, children, FakeInputKey: null); + } + + private FlowNode TraceFor(ForStatementSyntax forStmt, SemanticModel model, int depth) + { + var label = $"for (…; {Truncate(forStmt.Condition?.ToString() ?? "", 40)}; …)"; + var children = TraceNode(forStmt.Statement, model, depth + 1); + return new FlowNode(NewId(), "condition", label, children, FakeInputKey: null); + } + + // ── Expression handlers ─────────────────────────────────────────────────── + + private List TraceExpression(ExpressionSyntax expr, SemanticModel model, int depth) + { + return expr switch + { + InvocationExpressionSyntax invoc => TraceInvocation(invoc, model, depth), + AssignmentExpressionSyntax assign => TraceAssignment(assign, model, depth), + ConditionalExpressionSyntax ternary => TraceExpressionList([ternary.WhenTrue, ternary.WhenFalse], model, depth), + AwaitExpressionSyntax awaitExpr => TraceExpression(awaitExpr.Expression, model, depth), + _ => [], + }; + } + + private List TraceExpressionList(IEnumerable exprs, SemanticModel model, int depth) + { + return exprs.SelectMany(e => TraceExpression(e, model, depth)).ToList(); + } + + private List TraceAssignment(AssignmentExpressionSyntax assign, SemanticModel model, int depth) + { + return TraceExpression(assign.Right, model, depth); + } + + private List TraceInvocation( + InvocationExpressionSyntax invoc, + SemanticModel model, + int depth, + string? label = null) + { + var invocStr = invoc.ToString(); + + // ── SendGump check ──────────────────────────────────────────────────── + if (IsSendGump(invocStr)) + { + var gumpClass = ExtractGumpClass(invoc); + return [new FlowNode( + Id: NewId(), + Type: "gump_send", + Label: label ?? invocStr.Truncate(80), + Children: [], + FakeInputKey: null, + ResolvedGump: gumpClass, + AssetRef: null + )]; + } + + // ── Property / member access (not a real call chain node) ──────────── + if (IsPropertyAccess(invoc)) + { + return [new FlowNode(NewId(), "property_access", label ?? Truncate(invocStr, 80), [])]; + } + + // ── Try to resolve and recurse into the called method ───────────────── + if (depth < MaxDepth) + { + var calledMethod = TryResolveMethod(invoc, model); + if (calledMethod is not null) + { + var resolvedLabel = label ?? BuildCallLabel(invoc); + var children = TraceMethodBody(calledMethod.Syntax, calledMethod.Model, depth + 1); + if (children.Count > 0) + { + return [new FlowNode(NewId(), "method_call", resolvedLabel, children)]; + } + } + } + + // ── Leaf call node ──────────────────────────────────────────────────── + var callLabel = label ?? BuildCallLabel(invoc); + return [new FlowNode(NewId(), "method_call", callLabel, [])]; + } + + // ── Helpers ─────────────────────────────────────────────────────────────── + + private Dictionary BuildClassMap() + { + var map = new Dictionary( + StringComparer.OrdinalIgnoreCase); + + foreach (var tree in _compilation.SyntaxTrees) + { + var model = _compilation.GetSemanticModel(tree); + foreach (var cls in tree.GetRoot().DescendantNodes().OfType()) + { + var symbol = model.GetDeclaredSymbol(cls); + if (symbol is not null && !map.ContainsKey(symbol.Name)) + map[symbol.Name] = (cls, model); + } + } + + return map; + } + + private record ResolvedMethod(MethodDeclarationSyntax Syntax, SemanticModel Model); + + private ResolvedMethod? TryResolveMethod(InvocationExpressionSyntax invoc, SemanticModel model) + { + try + { + var symbolInfo = model.GetSymbolInfo(invoc); + var symbol = symbolInfo.Symbol as IMethodSymbol; + if (symbol is null) return null; + + var containingClass = symbol.ContainingType?.Name; + if (containingClass is null) return null; + + if (!_classMap.TryGetValue(containingClass, out var entry)) return null; + + var methodSyntax = entry.Syntax.Members + .OfType() + .FirstOrDefault(m => m.Identifier.Text == symbol.Name); + + return methodSyntax is not null ? new ResolvedMethod(methodSyntax, entry.Model) : null; + } + catch + { + return null; + } + } + + private static bool IsSendGump(string invocStr) => + invocStr.Contains("SendGump", StringComparison.OrdinalIgnoreCase); + + private static bool IsPropertyAccess(InvocationExpressionSyntax invoc) + { + var memberAccess = invoc.Expression as MemberAccessExpressionSyntax; + return memberAccess?.Name.Identifier.Text is "ToString" or "GetType"; + } + + private static string? ExtractGumpClass(InvocationExpressionSyntax invoc) + { + var newExpr = invoc.DescendantNodes().OfType().FirstOrDefault(); + return newExpr?.Type.ToString(); + } + + private static string? ExtractFakeInputKey(ExpressionSyntax expr) + { + // If the condition references a simple identifier or member access, use it as the key + return expr switch + { + IdentifierNameSyntax id => id.Identifier.Text, + MemberAccessExpressionSyntax ma => ma.Name.Identifier.Text, + BinaryExpressionSyntax bin => ExtractFakeInputKey(bin.Left), + PrefixUnaryExpressionSyntax pre => ExtractFakeInputKey(pre.Operand), + _ => null, + }; + } + + private static string BuildCallLabel(InvocationExpressionSyntax invoc) + { + var name = invoc.Expression switch + { + MemberAccessExpressionSyntax ma => $"{ma.Expression}.{ma.Name}", + IdentifierNameSyntax id => id.Identifier.Text, + _ => invoc.Expression.ToString(), + }; + return Truncate(name, 80); + } + + private static string Truncate(string s, int max) => + s.Length <= max ? s : s[..max] + "…"; + + private static int _counter; + private static string NewId() => $"n{System.Threading.Interlocked.Increment(ref _counter)}"; +} + +internal static class StringExtensions +{ + public static string Truncate(this string s, int max) => + s.Length <= max ? s : s[..max] + "…"; +} diff --git a/sidecar/Parsing/RoslynParser.cs b/sidecar/Parsing/RoslynParser.cs new file mode 100644 index 0000000..ba9ac1d --- /dev/null +++ b/sidecar/Parsing/RoslynParser.cs @@ -0,0 +1,151 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using ArtificersScrollwork.Sidecar.Models; + +namespace ArtificersScrollwork.Sidecar.Parsing; + +/// +/// Walks a syntax tree and extracts ClassInfo for every class declaration. +/// +public class ClassWalker : CSharpSyntaxWalker +{ + private readonly SemanticModel _model; + private readonly string _filePath; + + public List Classes { get; } = new(); + + public ClassWalker(SemanticModel model, string filePath) + { + _model = model; + _filePath = filePath; + } + + public override void VisitClassDeclaration(ClassDeclarationSyntax node) + { + var symbol = _model.GetDeclaredSymbol(node); + if (symbol is null) + { + base.VisitClassDeclaration(node); + return; + } + + var baseClass = symbol.BaseType?.Name; + var interfaces = symbol.Interfaces.Select(i => i.Name).ToList(); + var ns = symbol.ContainingNamespace?.ToDisplayString() ?? ""; + var attrs = symbol.GetAttributes().Select(a => a.AttributeClass?.Name ?? "").ToList(); + + // Heuristics for class category + bool isGump = IsGump(symbol); + bool isMobile = IsMobile(symbol); + bool isItem = IsItem(symbol); + + // Methods + var methods = node.Members + .OfType() + .Select(m => ParseMethod(m, symbol)) + .ToList(); + + // Properties + var properties = node.Members + .OfType() + .Select(p => new Models.PropertyInfo( + p.Identifier.Text, + p.Type.ToString(), + p.AccessorList?.Accessors.Any(a => a.IsKind(SyntaxKind.GetAccessorDeclaration)) ?? false, + p.AccessorList?.Accessors.Any(a => a.IsKind(SyntaxKind.SetAccessorDeclaration)) ?? false + )) + .ToList(); + + Classes.Add(new ClassInfo( + symbol.Name, + ns, + _filePath, + baseClass, + interfaces, + properties, + methods, + attrs, + isGump, + isMobile, + isItem + )); + + base.VisitClassDeclaration(node); + } + + private Models.MethodInfo ParseMethod(MethodDeclarationSyntax m, INamedTypeSymbol classSymbol) + { + var isOverride = m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.OverrideKeyword)); + var isVirtual = m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.VirtualKeyword)); + + var parameters = m.ParameterList.Parameters + .Select(p => new ParameterInfo(p.Identifier.Text, p.Type?.ToString() ?? "")) + .ToList(); + + // Check if this method sends a Gump + bool callsGump = false; + string? gumpClass = null; + + if (m.Body is not null) + { + var sendGumps = m.Body.DescendantNodes() + .OfType() + .Where(inv => inv.ToString().Contains("SendGump")) + .FirstOrDefault(); + + if (sendGumps is not null) + { + callsGump = true; + // Try to extract the Gump class name from `new XxxGump(...)` + var newExpr = sendGumps.DescendantNodes() + .OfType() + .FirstOrDefault(); + gumpClass = newExpr?.Type.ToString(); + } + } + + return new Models.MethodInfo( + m.Identifier.Text, + m.ReturnType.ToString(), + parameters, + isOverride, + isVirtual, + callsGump, + gumpClass + ); + } + + private static bool IsGump(INamedTypeSymbol symbol) + { + var current = symbol.BaseType; + while (current is not null) + { + if (current.Name is "Gump" or "GumpPlus") return true; + current = current.BaseType; + } + return false; + } + + private static bool IsMobile(INamedTypeSymbol symbol) + { + var current = symbol.BaseType; + while (current is not null) + { + if (current.Name is "Mobile" or "BaseCreature" or "BaseVendor") return true; + current = current.BaseType; + } + return false; + } + + private static bool IsItem(INamedTypeSymbol symbol) + { + var current = symbol.BaseType; + while (current is not null) + { + if (current.Name is "Item" or "BaseWeapon" or "BaseArmor" or "BaseClothing") return true; + current = current.BaseType; + } + return false; + } +} diff --git a/sidecar/Parsing/ScriptIndexer.cs b/sidecar/Parsing/ScriptIndexer.cs new file mode 100644 index 0000000..d20411e --- /dev/null +++ b/sidecar/Parsing/ScriptIndexer.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using ArtificersScrollwork.Sidecar.Models; + +namespace ArtificersScrollwork.Sidecar.Parsing; + +/// +/// Walks a ServUO Scripts directory, parses all .cs files with Roslyn, +/// and builds a searchable in-memory index of classes and methods. +/// +public class ScriptIndexer +{ + private readonly Dictionary _classes = + new(StringComparer.OrdinalIgnoreCase); + + /// The Roslyn compilation built during indexing. Used by CallChainTracer. + public CSharpCompilation? Compilation { get; private set; } + + public ScriptIndex IndexDirectory(string path) + { + var files = Directory + .EnumerateFiles(path, "*.cs", SearchOption.AllDirectories) + .ToList(); + + var syntaxTrees = files + .Select(f => + { + try { return CSharpSyntaxTree.ParseText(File.ReadAllText(f), path: f); } + catch { return null; } + }) + .Where(t => t is not null) + .Select(t => t!) + .ToList(); + + Compilation = CSharpCompilation.Create( + "ServUOAnalysis", + syntaxTrees, + references: null, + options: new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + reportSuppressedDiagnostics: false + ) + ); + + _classes.Clear(); + + foreach (var tree in syntaxTrees) + { + SemanticModel model; + try { model = Compilation.GetSemanticModel(tree); } + catch { continue; } + + var walker = new ClassWalker(model, tree.FilePath); + try { walker.Visit(tree.GetRoot()); } + catch { continue; } + + foreach (var cls in walker.Classes) + _classes[cls.Name] = cls; + } + + var summaries = _classes.Values + .Select(c => new ClassSummary( + c.Name, c.Namespace, c.FilePath, c.BaseClass, + c.IsGump, c.IsMobile, c.IsItem)) + .OrderBy(c => c.Namespace) + .ThenBy(c => c.Name) + .ToList(); + + return new ScriptIndex(_classes.Count, files.Count, summaries); + } + + public ClassInfo? GetClass(string name) => + _classes.TryGetValue(name, out var cls) ? cls : null; + + public List Search(string query) => + _classes.Values + .Where(c => + c.Name.Contains(query, StringComparison.OrdinalIgnoreCase) || + c.Namespace.Contains(query, StringComparison.OrdinalIgnoreCase) || + c.Methods.Any(m => m.Name.Contains(query, StringComparison.OrdinalIgnoreCase))) + .Select(c => new ClassSummary( + c.Name, c.Namespace, c.FilePath, c.BaseClass, + c.IsGump, c.IsMobile, c.IsItem)) + .Take(100) + .ToList(); +} diff --git a/sidecar/Program.cs b/sidecar/Program.cs new file mode 100644 index 0000000..306b67b --- /dev/null +++ b/sidecar/Program.cs @@ -0,0 +1,140 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using ArtificersScrollwork.Sidecar.Parsing; +using ArtificersScrollwork.Sidecar.Gumps; + +/// +/// Entry point for the ASW C# sidecar. +/// Reads newline-delimited JSON requests from stdin, writes responses to stdout. +/// +/// Request: { "id": "uuid", "command": "cmd_name", "args": { ... } } +/// Response: { "id": "uuid", "ok": true, "data": { ... } } +/// | { "id": "uuid", "ok": false, "error": "message" } +/// + +var options = new JsonSerializerOptions +{ + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + WriteIndented = false, +}; + +ScriptIndexer? indexer = null; +CallChainTracer? tracer = null; + +while (true) +{ + var line = Console.ReadLine(); + if (line is null) break; + + JsonObject? request; + try { request = JsonSerializer.Deserialize(line, options); } + catch { continue; } + if (request is null) continue; + + var id = request["id"]?.GetValue() ?? ""; + var command = request["command"]?.GetValue() ?? ""; + var reqArgs = request["args"] as JsonObject ?? new JsonObject(); + + try + { + JsonNode? data = command switch + { + "index_scripts" => HandleIndexScripts(reqArgs), + "get_class" => HandleGetClass(reqArgs), + "trace_method" => HandleTraceMethod(reqArgs), + "extract_gump" => HandleExtractGump(reqArgs), + "search" => HandleSearch(reqArgs), + _ => throw new InvalidOperationException($"Unknown command: {command}"), + }; + + WriteResponse(id, true, data, null, options); + } + catch (Exception ex) + { + WriteResponse(id, false, null, ex.Message, options); + } +} + +// ── Command handlers ────────────────────────────────────────────────────────── + +JsonNode HandleIndexScripts(JsonObject args) +{ + var path = args["path"]?.GetValue() + ?? throw new ArgumentException("Missing 'path' argument"); + + indexer = new ScriptIndexer(); + var index = indexer.IndexDirectory(path); + + // Build a compilation for the tracer + tracer = new CallChainTracer(indexer.Compilation + ?? throw new InvalidOperationException("Compilation not available after indexing")); + + return JsonSerializer.SerializeToNode(index, options)!; +} + +JsonNode HandleGetClass(JsonObject args) +{ + EnsureIndexer(); + var className = args["class"]?.GetValue() + ?? throw new ArgumentException("Missing 'class' argument"); + + var info = indexer!.GetClass(className) + ?? throw new KeyNotFoundException($"Class '{className}' not found in index"); + + return JsonSerializer.SerializeToNode(info, options)!; +} + +JsonNode HandleTraceMethod(JsonObject args) +{ + EnsureIndexer(); + + if (tracer is null) + throw new InvalidOperationException("Tracer not initialized — send index_scripts first"); + + var className = args["class"]?.GetValue() + ?? throw new ArgumentException("Missing 'class' argument"); + var methodName = args["method"]?.GetValue() + ?? throw new ArgumentException("Missing 'method' argument"); + + var flowNode = tracer.TraceMethod(className, methodName); + return JsonSerializer.SerializeToNode(flowNode, options)!; +} + +JsonNode HandleExtractGump(JsonObject args) +{ + EnsureIndexer(); + throw new NotImplementedException("extract_gump not yet implemented (Phase 4)"); +} + +JsonNode HandleSearch(JsonObject args) +{ + EnsureIndexer(); + var query = args["query"]?.GetValue() + ?? throw new ArgumentException("Missing 'query' argument"); + + var results = indexer!.Search(query); + return JsonSerializer.SerializeToNode(results, options)!; +} + +void EnsureIndexer() +{ + if (indexer is null) + throw new InvalidOperationException("Scripts not indexed — send index_scripts first"); +} + +static void WriteResponse(string id, bool ok, JsonNode? data, string? error, JsonSerializerOptions opts) +{ + var response = new JsonObject + { + ["id"] = id, + ["ok"] = ok, + }; + + if (ok && data is not null) + response["data"] = data; + else if (!ok) + response["error"] = error ?? "Unknown error"; + + Console.WriteLine(JsonSerializer.Serialize(response, opts)); + Console.Out.Flush(); +} diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..50d76ab --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "artificers-scrollwork" +version = "0.1.0" +edition = "2021" + +[lib] +name = "artificers_scrollwork_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-dialog = "2" +tauri-plugin-shell = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +rusqlite = { version = "0.31", features = ["bundled"] } +anyhow = "1" +uuid = { version = "1", features = ["v4"] } +byteorder = "1" +flate2 = "1" +image = { version = "0.25", features = ["dds"] } +once_cell = "1" +tokio = { version = "1", features = ["full"] } +walkdir = "2" + +[features] +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..21bd509 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "identifier": "default", + "description": "Default capabilities for Artificer's Scrollwork", + "windows": ["main"], + "permissions": [ + "core:default", + "dialog:default", + "shell:default" + ] +} diff --git a/src-tauri/gen/schemas/acl-manifests.json b/src-tauri/gen/schemas/acl-manifests.json new file mode 100644 index 0000000..9a4e12e --- /dev/null +++ b/src-tauri/gen/schemas/acl-manifests.json @@ -0,0 +1 @@ +{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-supports-multiple-windows":{"identifier":"deny-supports-multiple-windows","description":"Denies the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["supports_multiple_windows"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-icon-with-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-icon-with-as-template":{"identifier":"allow-set-icon-with-as-template","description":"Enables the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_with_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-icon-with-as-template":{"identifier":"deny-set-icon-with-as-template","description":"Denies the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_with_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-activity-name","allow-scene-identifier","allow-internal-toggle-maximize"]},"permissions":{"allow-activity-name":{"identifier":"allow-activity-name","description":"Enables the activity_name command without any pre-configured scope.","commands":{"allow":["activity_name"],"deny":[]}},"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-scene-identifier":{"identifier":"allow-scene-identifier","description":"Enables the scene_identifier command without any pre-configured scope.","commands":{"allow":["scene_identifier"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-activity-name":{"identifier":"deny-activity-name","description":"Denies the activity_name command without any pre-configured scope.","commands":{"allow":[],"deny":["activity_name"]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-scene-identifier":{"identifier":"deny-scene-identifier","description":"Denies the scene_identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["scene_identifier"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)","commands":{"allow":["message"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)","commands":{"allow":["message"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)","commands":{"allow":[],"deny":["message"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)","commands":{"allow":[],"deny":["message"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}} \ No newline at end of file diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json new file mode 100644 index 0000000..6c5e5d5 --- /dev/null +++ b/src-tauri/gen/schemas/capabilities.json @@ -0,0 +1 @@ +{"default":{"identifier":"default","description":"Default capabilities for Artificer's Scrollwork","local":true,"windows":["main"],"permissions":["core:default","dialog:default","shell:default"]}} \ No newline at end of file diff --git a/src-tauri/gen/schemas/desktop-schema.json b/src-tauri/gen/schemas/desktop-schema.json new file mode 100644 index 0000000..6eba1dc --- /dev/null +++ b/src-tauri/gen/schemas/desktop-schema.json @@ -0,0 +1,2678 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", + "type": "string", + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`", + "type": "string", + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." + }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." + }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." + }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Enables the supports_multiple_windows command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-supports-multiple-windows", + "markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope." + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." + }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." + }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." + }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Denies the supports_multiple_windows command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-supports-multiple-windows", + "markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope." + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", + "type": "string", + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", + "type": "string", + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", + "type": "string", + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", + "type": "string", + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", + "type": "string", + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`", + "type": "string", + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_with_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-with-as-template", + "markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_with_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-with-as-template", + "markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", + "type": "string", + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`", + "type": "string", + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`" + }, + { + "description": "Enables the activity_name command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-activity-name", + "markdownDescription": "Enables the activity_name command without any pre-configured scope." + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." + }, + { + "description": "Enables the scene_identifier command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scene-identifier", + "markdownDescription": "Enables the scene_identifier command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." + }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." + }, + { + "description": "Denies the activity_name command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-activity-name", + "markdownDescription": "Denies the activity_name command without any pre-configured scope." + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." + }, + { + "description": "Denies the scene_identifier command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scene-identifier", + "markdownDescription": "Denies the scene_identifier command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." + }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)" + }, + { + "description": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)" + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)" + }, + { + "description": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)" + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/gen/schemas/windows-schema.json b/src-tauri/gen/schemas/windows-schema.json new file mode 100644 index 0000000..6eba1dc --- /dev/null +++ b/src-tauri/gen/schemas/windows-schema.json @@ -0,0 +1,2678 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", + "type": "string", + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`", + "type": "string", + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." + }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." + }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." + }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Enables the supports_multiple_windows command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-supports-multiple-windows", + "markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope." + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." + }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." + }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." + }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Denies the supports_multiple_windows command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-supports-multiple-windows", + "markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope." + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", + "type": "string", + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", + "type": "string", + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", + "type": "string", + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", + "type": "string", + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", + "type": "string", + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`", + "type": "string", + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_with_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-with-as-template", + "markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_with_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-with-as-template", + "markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", + "type": "string", + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`", + "type": "string", + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`" + }, + { + "description": "Enables the activity_name command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-activity-name", + "markdownDescription": "Enables the activity_name command without any pre-configured scope." + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." + }, + { + "description": "Enables the scene_identifier command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scene-identifier", + "markdownDescription": "Enables the scene_identifier command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." + }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." + }, + { + "description": "Denies the activity_name command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-activity-name", + "markdownDescription": "Denies the activity_name command without any pre-configured scope." + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." + }, + { + "description": "Denies the scene_identifier command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scene-identifier", + "markdownDescription": "Denies the scene_identifier command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." + }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)" + }, + { + "description": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)" + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)" + }, + { + "description": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)" + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..217a5b8 Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..32cd82f Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..f3d1555 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/icon-512.png b/src-tauri/icons/icon-512.png new file mode 100644 index 0000000..3bbd441 Binary files /dev/null and b/src-tauri/icons/icon-512.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..1049f87 Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..c40359c Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..1049f87 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/assets/art.rs b/src-tauri/src/assets/art.rs new file mode 100644 index 0000000..ed72646 --- /dev/null +++ b/src-tauri/src/assets/art.rs @@ -0,0 +1,289 @@ +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use serde::{Deserialize, Serialize}; +use std::io::Cursor; +use std::path::Path; + +use super::mul_reader; +use super::uop_reader; + +const STATIC_OFFSET: usize = 0x4000; + +/// RGBA pixel data for a decoded art image. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ArtImage { + pub width: u32, + pub height: u32, + /// Raw RGBA bytes, row-major. + pub pixels: Vec, +} + +// ── BMP artwork folder ──────────────────────────────────────────────────────── + +/// Find the "UO artwork" folder, checking resource dir then walking up from the exe. +/// In dev mode the exe is deep in target/debug/, so we walk up to the repo root. +pub fn find_artwork_dir(app: &tauri::AppHandle) -> Option { + use tauri::Manager; + + // 1. Try Tauri resource dir (works in production bundles) + if let Ok(resource_dir) = app.path().resource_dir() { + let candidate = resource_dir.join("UO artwork"); + if candidate.is_dir() { + return Some(candidate); + } + } + + // 2. Walk up from the exe (works in dev mode where resource_dir points at src-tauri/) + if let Ok(exe) = std::env::current_exe() { + let mut dir = exe.parent().map(|p| p.to_path_buf()); + for _ in 0..8 { + if let Some(ref d) = dir { + let candidate = d.join("UO artwork"); + if candidate.is_dir() { + return Some(candidate); + } + dir = d.parent().map(|p| p.to_path_buf()); + } else { + break; + } + } + } + + None +} + +/// Decode a BMP from the "UO artwork" folder by item_id. +/// Filename pattern: "Item 0x{id:04X}.bmp" +/// Black (0,0,0) pixels are made transparent — UO art exports use black for transparency. +pub fn decode_static_bmp(artwork_dir: &Path, item_id: usize) -> Result { + let filename = format!("Item 0x{:04X}.bmp", item_id); + let path = artwork_dir.join(&filename); + if !path.exists() { + return Err(anyhow!("no art data")); + } + decode_bmp_file(&path) +} + +fn decode_bmp_file(path: &Path) -> Result { + let img = image::open(path) + .map_err(|e| anyhow!("BMP decode: {}", e))?; + let mut rgba = img.to_rgba8(); + // UO art BMP exports use pure black as the transparent colour. + for pixel in rgba.pixels_mut() { + if pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0 { + pixel[3] = 0; + } + } + Ok(ArtImage { + width: rgba.width(), + height: rgba.height(), + pixels: rgba.into_raw(), + }) +} + +// ── Public decode entry points ──────────────────────────────────────────────── + +/// Try every known art source for `item_id` in priority order. +/// Returns the first that succeeds, or Err("no art data") if none. +pub fn decode_static_any( + uo_root: &Path, + item_id: usize, +) -> Result { + let mul_path = uo_root.join("art.mul"); + let idx_path = uo_root.join("artidx.mul"); + let uop_path = uo_root.join("artLegacyMUL.uop"); + let tileart = uo_root.join("tileart.uop"); + + // 1. Classic art.mul + if mul_path.exists() && idx_path.exists() { + if let Ok(img) = decode_static(&mul_path, &idx_path, item_id) { + return Ok(img); + } + } + + // 2. artLegacyMUL.uop (Classic UOP packaging) + if uop_path.exists() { + if let Ok(img) = decode_static_uop(&uop_path, item_id) { + return Ok(img); + } + } + + // 3. tileart.uop (Enhanced Client) — tries multiple hash patterns + if tileart.exists() { + if let Ok(img) = decode_static_tileart_uop(&tileart, item_id) { + return Ok(img); + } + } + + Err(anyhow!("no art data")) +} + +/// Decode from art.mul + artidx.mul (Classic Client). +pub fn decode_static(mul_path: &Path, idx_path: &Path, item_id: usize) -> Result { + let entries = mul_reader::read_index(idx_path)?; + let index = STATIC_OFFSET + item_id; + + let entry = entries + .get(index) + .ok_or_else(|| anyhow!("no art data"))?; + + let raw = mul_reader::read_entry(mul_path, entry)? + .ok_or_else(|| anyhow!("no art data"))?; + + decode_art_data(&raw) +} + +/// Decode from artLegacyMUL.uop. +/// Hash pattern: "build/artlegacymul/{art_index:08}.tga" +pub fn decode_static_uop(uop_path: &Path, item_id: usize) -> Result { + let art_index = STATIC_OFFSET + item_id; + let pattern = format!("build/artlegacymul/{:08}.tga", art_index); + let hash = uop_reader::uop_hash(&pattern); + + let index = uop_reader::read_uop_index(uop_path)?; + let entry = index + .get(&hash) + .ok_or_else(|| anyhow!("no art data"))? + .clone(); + + let raw = uop_reader::read_uop_entry(uop_path, &entry)?; + decode_art_data(&raw) +} + +/// Decode from tileart.uop (Enhanced Client). +/// Tries several hash patterns; the data can be DDS or classic 16-bit art. +pub fn decode_static_tileart_uop(uop_path: &Path, item_id: usize) -> Result { + let index = uop_reader::read_uop_index(uop_path)?; + + // Try every plausible hash pattern the EC might use. + let candidates = [ + format!("build/tileart/{:08}.bin", item_id + STATIC_OFFSET), + format!("build/tileart/{:08}.bin", item_id), + format!("build/tileart/{:08x}.bin", item_id + STATIC_OFFSET), + format!("build/tileart/{:08x}.bin", item_id), + format!("build/tileartlegacy/{:08}.bin", item_id + STATIC_OFFSET), + format!("build/tileartlegacy/{:08}.bin", item_id), + format!("build/worldart/{:08}.dds", item_id + STATIC_OFFSET), + format!("build/worldart/{:08}.dds", item_id), + ]; + + for pattern in &candidates { + let hash = uop_reader::uop_hash(pattern); + if let Some(entry) = index.get(&hash) { + let raw = uop_reader::read_uop_entry(uop_path, entry)?; + if raw.is_empty() { + continue; + } + // DDS magic: "DDS " = 0x44 44 53 20 + if raw.starts_with(b"DDS ") { + if let Ok(img) = decode_dds(&raw) { + return Ok(img); + } + } + // Try classic art format + if let Ok(img) = decode_art_data(&raw) { + return Ok(img); + } + } + } + + Err(anyhow!("no art data")) +} + +// ── Format decoders ─────────────────────────────────────────────────────────── + +/// Decode a DDS file to RGBA using the `image` crate. +fn decode_dds(data: &[u8]) -> Result { + let cursor = Cursor::new(data); + let img = image::load(cursor, image::ImageFormat::Dds) + .map_err(|e| anyhow!("DDS decode: {}", e))?; + let rgba = img.to_rgba8(); + Ok(ArtImage { + width: rgba.width(), + height: rgba.height(), + pixels: rgba.into_raw(), + }) +} + +/// Decode raw art.mul / UOP art data (16-bit 1-5-5-5 ARGB, run-length encoded). +pub fn decode_art_data(data: &[u8]) -> Result { + // Minimum: 2 unknown + 2 width + 2 height = 6 bytes + if data.len() < 6 { + return Err(anyhow!("no art data")); + } + + let mut cursor = Cursor::new(data); + let _unknown = cursor.read_u16::()?; + let width = cursor.read_u16::()? as u32; + let height = cursor.read_u16::()? as u32; + + if width == 0 || height == 0 || width > 1024 || height > 1024 { + return Err(anyhow!("no art data")); + } + + // Row lookup table: `height` u16 offsets. Must all fit in the buffer. + let row_table_bytes = height as usize * 2; + if data.len() < 6 + row_table_bytes { + return Err(anyhow!("no art data")); + } + + let mut row_offsets = vec![0u32; height as usize]; + for o in row_offsets.iter_mut() { + *o = cursor.read_u16::()? as u32; + } + let data_start = cursor.position() as usize; + + let mut pixels = vec![0u8; (width * height * 4) as usize]; + + for y in 0..height as usize { + let row_byte_offset = row_offsets[y] as usize * 2; + let mut x = 0usize; + let mut pos = data_start + row_byte_offset; + + loop { + // Need 4 bytes: 2 for run, 2 for count + if pos + 4 > data.len() { + break; + } + let run = u16::from_le_bytes([data[pos], data[pos + 1]]); + let count = u16::from_le_bytes([data[pos + 2], data[pos + 3]]); + pos += 4; + + if run == 0 && count == 0 { + break; + } + + x += run as usize; + + for _ in 0..count as usize { + if pos + 2 > data.len() || x >= width as usize { + break; + } + let color = u16::from_le_bytes([data[pos], data[pos + 1]]); + pos += 2; + let rgba = argb16_to_rgba32(color); + let idx = (y * width as usize + x) * 4; + pixels[idx..idx + 4].copy_from_slice(&rgba); + x += 1; + } + } + } + + Ok(ArtImage { width, height, pixels }) +} + +/// Convert 16-bit UO ARGB (1-5-5-5) to 32-bit RGBA. +pub fn argb16_to_rgba32(color: u16) -> [u8; 4] { + if color == 0 { + return [0, 0, 0, 0]; + } + let r = ((color >> 10) & 0x1F) as u8; + let g = ((color >> 5) & 0x1F) as u8; + let b = ( color & 0x1F) as u8; + [ + (r << 3) | (r >> 2), + (g << 3) | (g >> 2), + (b << 3) | (b >> 2), + 255, + ] +} diff --git a/src-tauri/src/assets/cliloc.rs b/src-tauri/src/assets/cliloc.rs new file mode 100644 index 0000000..c967e1d --- /dev/null +++ b/src-tauri/src/assets/cliloc.rs @@ -0,0 +1,34 @@ +use anyhow::Result; +use byteorder::{LittleEndian, ReadBytesExt}; +use std::collections::HashMap; +use std::io::{BufReader, Read}; +use std::path::Path; + +/// Read cliloc.enu and return a map from string ID to localized string. +pub fn read_cliloc(path: &Path) -> Result> { + let file = std::fs::File::open(path)?; + let mut reader = BufReader::new(file); + + // Header: u32 unknown, u16 unknown + let _header1 = reader.read_u32::()?; + let _header2 = reader.read_u16::()?; + + let mut map = HashMap::new(); + + loop { + let id = match reader.read_u32::() { + Ok(v) => v, + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e.into()), + }; + let _flag = reader.read_u8()?; + let length = reader.read_u16::()?; + + let mut text_buf = vec![0u8; length as usize]; + reader.read_exact(&mut text_buf)?; + let text = String::from_utf8_lossy(&text_buf).to_string(); + map.insert(id, text); + } + + Ok(map) +} diff --git a/src-tauri/src/assets/gumpart.rs b/src-tauri/src/assets/gumpart.rs new file mode 100644 index 0000000..b16d1e7 --- /dev/null +++ b/src-tauri/src/assets/gumpart.rs @@ -0,0 +1,172 @@ +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use std::io::Cursor; +use std::path::Path; + +use super::art::ArtImage; +use super::art::argb16_to_rgba32; +use super::mul_reader; +use super::uop_reader; + +// ── Gump BMP folder ─────────────────────────────────────────────────────────── + +/// Collect all candidate "UO Gumps" directories: resource_dir + walk-up from exe. +fn gump_dir_candidates(app: &tauri::AppHandle) -> Vec { + use tauri::Manager; + let mut candidates = Vec::new(); + + if let Ok(resource_dir) = app.path().resource_dir() { + let c = resource_dir.join("UO Gumps"); + if c.is_dir() { candidates.push(c); } + } + + if let Ok(exe) = std::env::current_exe() { + let mut dir = exe.parent().map(|p| p.to_path_buf()); + for _ in 0..10 { + if let Some(ref d) = dir { + let c = d.join("UO Gumps"); + if c.is_dir() && !candidates.contains(&c) { candidates.push(c); } + dir = d.parent().map(|p| p.to_path_buf()); + } else { break; } + } + } + + candidates +} + +/// Find the "UO Gumps" folder containing gumps.xml. +pub fn find_gump_dir(app: &tauri::AppHandle) -> Option { + // Prefer any candidate that has gumps.xml + let candidates = gump_dir_candidates(app); + candidates.into_iter().find(|c| c.join("gumps.xml").exists()) +} + +/// Find the "UO Gumps" folder containing script_gumps.xml. +/// Falls back to any UO Gumps dir if the file isn't found anywhere. +pub fn find_gump_dir_for_scripts(app: &tauri::AppHandle) -> Option { + let candidates = gump_dir_candidates(app); + // Prefer a candidate that actually has script_gumps.xml + if let Some(hit) = candidates.iter().find(|c| c.join("script_gumps.xml").exists()) { + return Some(hit.clone()); + } + // Fallback: return first candidate at all + candidates.into_iter().next() +} + +/// Decode a BMP from the "UO Gumps" folder by gump_id. +/// Filename pattern: "Gump 0x{id:04X}.bmp" +/// Black (0,0,0) pixels are made transparent. +pub fn decode_gump_bmp(gump_dir: &Path, gump_id: usize) -> Result { + let filename = format!("Gump 0x{:04X}.bmp", gump_id); + let path = gump_dir.join(&filename); + if !path.exists() { + return Err(anyhow!("no gump data")); + } + decode_gump_bmp_file(&path) +} + +fn decode_gump_bmp_file(path: &Path) -> Result { + let img = image::open(path) + .map_err(|e| anyhow!("BMP decode: {}", e))?; + let mut rgba = img.to_rgba8(); + for pixel in rgba.pixels_mut() { + if pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0 { + pixel[3] = 0; + } + } + Ok(ArtImage { + width: rgba.width(), + height: rgba.height(), + pixels: rgba.into_raw(), + }) +} + +/// Decode a gump image by ID from gumpart.mul. +pub fn decode_gump(mul_path: &Path, idx_path: &Path, gump_id: usize) -> Result { + let entries = mul_reader::read_index(idx_path)?; + + let entry = entries + .get(gump_id) + .ok_or_else(|| anyhow!("Gump ID {} out of range", gump_id))?; + + let raw = mul_reader::read_entry(mul_path, entry)? + .ok_or_else(|| anyhow!("Gump ID {} has no data", gump_id))?; + + // Entry extra field: width in high 16 bits, height in low 16 bits + let width = ((entry.extra >> 16) & 0xFFFF) as u32; + let height = (entry.extra & 0xFFFF) as u32; + + decode_gump_data(&raw, width, height) +} + +/// Decode a gump image from gumpartLegacyMUL.uop (Enhanced Client). +/// Hash pattern: "build/gumpartlegacymul/{id:08}.tga" +/// Width and height are encoded in the UOP entry's decompressed data header. +pub fn decode_gump_uop(uop_path: &Path, gump_id: usize) -> Result { + let pattern = format!("build/gumpartlegacymul/{:08}.tga", gump_id); + let hash = uop_reader::uop_hash(&pattern); + + let index = uop_reader::read_uop_index(uop_path)?; + let entry = index + .get(&hash) + .ok_or_else(|| anyhow!("Gump ID {} not found in gumpartLegacyMUL.uop", gump_id))? + .clone(); + + let raw = uop_reader::read_uop_entry(uop_path, &entry)?; + + // In the UOP format the width/height are encoded in the extra field of the + // index, but in gumpartLegacyMUL.uop they are stored as the first 8 bytes + // of the decompressed data: u32 width, u32 height. + if raw.len() < 8 { + return Err(anyhow!("Gump UOP entry too small: {} bytes", raw.len())); + } + let width = u32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]); + let height = u32::from_le_bytes([raw[4], raw[5], raw[6], raw[7]]); + decode_gump_data(&raw[8..], width, height) +} + +fn decode_gump_data(data: &[u8], width: u32, height: u32) -> Result { + if width == 0 || height == 0 { + return Err(anyhow!("Invalid gump dimensions: {}x{}", width, height)); + } + + let mut cursor = Cursor::new(data); + let mut pixels = vec![0u8; (width * height * 4) as usize]; + + // Row lookup: one u32 per row, offset in u32 units from start of data + let mut row_offsets = vec![0u32; height as usize]; + for o in row_offsets.iter_mut() { + *o = cursor.read_u32::()?; + } + + for y in 0..height as usize { + let pos = (row_offsets[y] as usize) * 4; + let mut x = 0usize; + let mut p = pos; + + loop { + if p + 4 > data.len() { + break; + } + let color = u16::from_le_bytes([data[p], data[p + 1]]); + let count = u16::from_le_bytes([data[p + 2], data[p + 3]]); + p += 4; + + if count == 0 { + break; + } + + let rgba = argb16_to_rgba32(color); + for _ in 0..count as usize { + if x >= width as usize { + break; + } + let idx = (y * width as usize + x) * 4; + pixels[idx..idx + 4].copy_from_slice(&rgba); + x += 1; + } + } + } + + Ok(ArtImage { width, height, pixels }) +} diff --git a/src-tauri/src/assets/hues.rs b/src-tauri/src/assets/hues.rs new file mode 100644 index 0000000..18af4ba --- /dev/null +++ b/src-tauri/src/assets/hues.rs @@ -0,0 +1,60 @@ +use anyhow::Result; +use byteorder::{LittleEndian, ReadBytesExt}; +use serde::{Deserialize, Serialize}; +use std::io::{Cursor, Read}; +use std::path::Path; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HueEntry { + pub id: u32, + pub name: String, + /// 32 colors as RGBA u32 values. + pub colors: Vec, +} + +/// Read all hues from hues.mul. +/// Format: 375 blocks × 8 hues = 3000 hues. +/// Each hue: 32×u16 colors + u16 tableStart + u16 tableEnd + 20-byte name. +pub fn read_hues(hues_path: &Path) -> Result> { + let data = std::fs::read(hues_path)?; + let mut cursor = Cursor::new(&data); + let mut hues = Vec::new(); + + for block in 0..375u32 { + // Block header (4 bytes, skip) + let _header = cursor.read_u32::()?; + + for h in 0..8u32 { + let id = block * 8 + h; + let mut raw_colors = [0u16; 32]; + for c in raw_colors.iter_mut() { + *c = cursor.read_u16::()?; + } + let _table_start = cursor.read_u16::()?; + let _table_end = cursor.read_u16::()?; + + let mut name_buf = [0u8; 20]; + cursor.read_exact(&mut name_buf)?; + let name = String::from_utf8_lossy(&name_buf) + .trim_end_matches('\0') + .to_string(); + + let colors = raw_colors + .iter() + .map(|&c| { + let r = ((c >> 10) & 0x1F) as u32; + let g = ((c >> 5) & 0x1F) as u32; + let b = (c & 0x1F) as u32; + let r = (r << 3) | (r >> 2); + let g = (g << 3) | (g >> 2); + let b = (b << 3) | (b >> 2); + (r << 24) | (g << 16) | (b << 8) | 0xFF + }) + .collect(); + + hues.push(HueEntry { id, name, colors }); + } + } + + Ok(hues) +} diff --git a/src-tauri/src/assets/mod.rs b/src-tauri/src/assets/mod.rs new file mode 100644 index 0000000..9d23f64 --- /dev/null +++ b/src-tauri/src/assets/mod.rs @@ -0,0 +1,7 @@ +pub mod art; +pub mod cliloc; +pub mod gumpart; +pub mod hues; +pub mod mul_reader; +pub mod tiledata; +pub mod uop_reader; diff --git a/src-tauri/src/assets/mul_reader.rs b/src-tauri/src/assets/mul_reader.rs new file mode 100644 index 0000000..6856ed1 --- /dev/null +++ b/src-tauri/src/assets/mul_reader.rs @@ -0,0 +1,75 @@ +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use std::fs::File; +use std::io::{BufReader, Read, Seek, SeekFrom}; +use std::path::Path; + +/// A single entry from a .idx file. +#[derive(Debug, Clone, Copy)] +pub struct IndexEntry { + pub offset: i32, + pub length: i32, + pub extra: i32, +} + +/// Read all index entries from a .idx file. +pub fn read_index(idx_path: &Path) -> Result> { + let file = File::open(idx_path)?; + let mut reader = BufReader::new(file); + let mut entries = Vec::new(); + + loop { + let offset = match reader.read_i32::() { + Ok(v) => v, + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e.into()), + }; + let length = reader.read_i32::()?; + let extra = reader.read_i32::()?; + entries.push(IndexEntry { offset, length, extra }); + } + + Ok(entries) +} + +/// Read the raw bytes for a given index entry from the .mul data file. +/// Returns `None` if the entry is marked as non-existent or invalid. +pub fn read_entry(mul_path: &Path, entry: &IndexEntry) -> Result>> { + // offset < 0 or length <= 0 means "no entry" per UO convention. + // Also guard against absurdly large lengths (corrupt/sparse entries). + if entry.offset < 0 || entry.length <= 0 || entry.length > 0x100_0000 { + return Ok(None); + } + + let file_len = std::fs::metadata(mul_path)?.len(); + let start = entry.offset as u64; + let len = entry.length as usize; + + // Entry extends past EOF — treat as empty rather than hard error. + if start >= file_len || start + len as u64 > file_len { + return Ok(None); + } + + let mut file = File::open(mul_path)?; + file.seek(SeekFrom::Start(start))?; + + let mut buf = vec![0u8; len]; + file.read_exact(&mut buf)?; + Ok(Some(buf)) +} + +/// Convenience: open a mul/idx pair and return the index entries. +pub fn open_mul_pair(root: &Path, base_name: &str) -> Result<(Vec, std::path::PathBuf)> { + let idx_path = root.join(format!("{}idx.mul", base_name)); + let mul_path = root.join(format!("{}.mul", base_name)); + + if !idx_path.exists() { + return Err(anyhow!("Index file not found: {:?}", idx_path)); + } + if !mul_path.exists() { + return Err(anyhow!("Data file not found: {:?}", mul_path)); + } + + let entries = read_index(&idx_path)?; + Ok((entries, mul_path)) +} diff --git a/src-tauri/src/assets/tiledata.rs b/src-tauri/src/assets/tiledata.rs new file mode 100644 index 0000000..7e6143c --- /dev/null +++ b/src-tauri/src/assets/tiledata.rs @@ -0,0 +1,83 @@ +use anyhow::Result; +use byteorder::{LittleEndian, ReadBytesExt}; +use serde::{Deserialize, Serialize}; +use std::io::{Cursor, Read}; +use std::path::Path; + +/// Item tile metadata from tiledata.mul. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ItemTileData { + pub id: u32, + pub flags: u64, + pub weight: u8, + pub quality: u8, + pub anim_id: u16, + pub height: u8, + pub hue: u8, + pub name: String, +} + +/// Read all item tile entries from tiledata.mul. +/// Land tiles (first 428 blocks × 32 entries) are skipped — we only care about items. +pub fn read_item_tiles(tiledata_path: &Path) -> Result> { + let data = std::fs::read(tiledata_path)?; + let mut cursor = Cursor::new(&data); + + // Skip land tiles: 512 blocks of 32 land entries + // Each land block: 4-byte header + 32 × (8 flags + 2 texid + 20 name) = 4 + 32*30 = 964 bytes + // Actually: 4 + 32*(4+2+20) = 4 + 832 = 836 for old format; new (HS+) uses 8-byte flags + // Use the newer format (64-bit flags) as ServUO targets it. + // Land block size: 4 (header) + 32 * (8 flags + 2 texid + 20 name) = 4 + 960 = 964 + let land_block_size: u64 = 4 + 32 * (8 + 2 + 20); + let land_blocks: u64 = 512; + cursor.set_position(land_blocks * land_block_size); + + let mut items = Vec::new(); + let mut id = 0u32; + + while (cursor.position() as usize) < data.len() { + // Block header (4 bytes, skip) + let mut header = [0u8; 4]; + if cursor.read_exact(&mut header).is_err() { + break; + } + + for _ in 0..32 { + if (cursor.position() as usize) >= data.len() { + break; + } + + let flags = cursor.read_u64::()?; + let weight = cursor.read_u8()?; + let quality = cursor.read_u8()?; + let _unknown1 = cursor.read_u16::()?; + let _unknown2 = cursor.read_u8()?; + let _quantity = cursor.read_u8()?; + let anim_id = cursor.read_u16::()?; + let _unknown3 = cursor.read_u8()?; + let hue = cursor.read_u8()?; + let _unknown4 = cursor.read_u16::()?; + let height = cursor.read_u8()?; + + let mut name_buf = [0u8; 20]; + cursor.read_exact(&mut name_buf)?; + let name = String::from_utf8_lossy(&name_buf) + .trim_end_matches('\0') + .to_string(); + + items.push(ItemTileData { + id, + flags, + weight, + quality, + anim_id, + height, + hue, + name, + }); + id += 1; + } + } + + Ok(items) +} diff --git a/src-tauri/src/assets/uop_reader.rs b/src-tauri/src/assets/uop_reader.rs new file mode 100644 index 0000000..dd2ee21 --- /dev/null +++ b/src-tauri/src/assets/uop_reader.rs @@ -0,0 +1,187 @@ +use anyhow::{anyhow, Result}; +use byteorder::{LittleEndian, ReadBytesExt}; +use flate2::read::ZlibDecoder; +use std::collections::HashMap; +use std::fs::File; +use std::io::{BufReader, Read, Seek, SeekFrom}; +use std::path::Path; + +const UOP_MAGIC: u32 = 0x0050594D; + +#[derive(Debug, Clone)] +pub struct UopEntry { + pub data_offset: u64, + pub header_length: u32, + pub compressed_length: u32, + pub decompressed_length: u32, + pub hash: u64, + pub compression: u16, +} + +/// Read the UOP file and return a map from hash → UopEntry. +pub fn read_uop_index(path: &Path) -> Result> { + let file = File::open(path)?; + let mut reader = BufReader::new(file); + + // Header + let magic = reader.read_u32::()?; + if magic != UOP_MAGIC { + return Err(anyhow!("Not a valid UOP file: {:?}", path)); + } + + let _version = reader.read_u32::()?; + let _signature = reader.read_u32::()?; + let mut next_block = reader.read_u64::()?; + let _max_files = reader.read_u32::()?; + + // Skip tag (36 bytes) + let mut tag_buf = [0u8; 36]; + reader.read_exact(&mut tag_buf)?; + + let mut map = HashMap::new(); + + while next_block != 0 { + reader.seek(SeekFrom::Start(next_block))?; + next_block = reader.read_u64::()?; + let file_count = reader.read_u32::()?; + + for _ in 0..file_count { + let data_offset = reader.read_u64::()?; + let header_length = reader.read_u32::()?; + let compressed_length = reader.read_u32::()?; + let decompressed_length = reader.read_u32::()?; + let hash = reader.read_u64::()?; + let _crc = reader.read_u32::()?; + let compression = reader.read_u16::()?; + + if data_offset == 0 { + continue; + } + + map.insert( + hash, + UopEntry { + data_offset, + header_length, + compressed_length, + decompressed_length, + hash, + compression, + }, + ); + } + } + + Ok(map) +} + +/// Read decompressed data for a UOP entry. +pub fn read_uop_entry(path: &Path, entry: &UopEntry) -> Result> { + let mut file = File::open(path)?; + let offset = entry.data_offset + entry.header_length as u64; + file.seek(SeekFrom::Start(offset))?; + + let mut compressed = vec![0u8; entry.compressed_length as usize]; + file.read_exact(&mut compressed)?; + + if entry.compression == 1 { + let mut decoder = ZlibDecoder::new(&compressed[..]); + let mut decompressed = Vec::with_capacity(entry.decompressed_length as usize); + decoder.read_to_end(&mut decompressed)?; + Ok(decompressed) + } else { + Ok(compressed) + } +} + +/// Hash function for UOP filenames (per ClassicUO UOFileUop.cs). +pub fn uop_hash(s: &str) -> u64 { + let s = s.to_lowercase(); + let bytes = s.as_bytes(); + + let (mut eax, mut ebx, mut ecx, mut edx, mut esi, mut edi) = ( + 0u32, + bytes.len() as u32, + 0u32, + 0u32, + 0u32, + 0u32, + ); + + ecx = 0x9E3779B9u32; + edi = ecx; + esi = ecx; + + let mut i = 0usize; + while i + 12 <= bytes.len() { + edi = edi.wrapping_add( + (bytes[i] as u32) + | ((bytes[i + 1] as u32) << 8) + | ((bytes[i + 2] as u32) << 16) + | ((bytes[i + 3] as u32) << 24), + ); + esi = esi.wrapping_add( + (bytes[i + 4] as u32) + | ((bytes[i + 5] as u32) << 8) + | ((bytes[i + 6] as u32) << 16) + | ((bytes[i + 7] as u32) << 24), + ); + edx = edx + .wrapping_add( + (bytes[i + 8] as u32) + | ((bytes[i + 9] as u32) << 8) + | ((bytes[i + 10] as u32) << 16) + | ((bytes[i + 11] as u32) << 24), + ) + .wrapping_add(ebx); + edx ^= (esi >> 28).wrapping_add(edi << 4); + edx = edx.wrapping_add(esi); + esi ^= (edx >> 26).wrapping_add(edx << 6); + esi = esi.wrapping_add(edi); + edi ^= (esi >> 24).wrapping_add(esi << 8); + edi = edi.wrapping_add(edx); + edx ^= (edi >> 16).wrapping_add(edi << 16); + edx = edx.wrapping_add(esi); + esi ^= (edx >> 13).wrapping_add(edx << 19); + esi = esi.wrapping_add(edi); + edi ^= (esi >> 28).wrapping_add(esi << 4); + edi = edi.wrapping_add(edx); + i += 12; + } + + let remaining = bytes.len() - i; + if remaining > 0 { + eax = 0; + if remaining >= 11 { eax = eax.wrapping_add((bytes[i + 10] as u32) << 24); } + if remaining >= 10 { eax = eax.wrapping_add((bytes[i + 9] as u32) << 16); } + if remaining >= 9 { eax = eax.wrapping_add((bytes[i + 8] as u32) << 8); } + if remaining >= 8 { edi = edi.wrapping_add((bytes[i + 7] as u32) << 24); } + if remaining >= 7 { edi = edi.wrapping_add((bytes[i + 6] as u32) << 16); } + if remaining >= 6 { edi = edi.wrapping_add((bytes[i + 5] as u32) << 8); } + if remaining >= 5 { edi = edi.wrapping_add(bytes[i + 4] as u32); } + if remaining >= 4 { esi = esi.wrapping_add((bytes[i + 3] as u32) << 24); } + if remaining >= 3 { esi = esi.wrapping_add((bytes[i + 2] as u32) << 16); } + if remaining >= 2 { esi = esi.wrapping_add((bytes[i + 1] as u32) << 8); } + if remaining >= 1 { esi = esi.wrapping_add(bytes[i] as u32); } + esi = esi.wrapping_add(eax); + + edi ^= (esi >> 28).wrapping_add(esi << 4); + edi = edi.wrapping_add(esi); + esi ^= (edi >> 26).wrapping_add(edi << 6); + esi = esi.wrapping_add(edi); + edi ^= (esi >> 24).wrapping_add(esi << 8); + edi = edi.wrapping_add(esi); + esi ^= (edi >> 16).wrapping_add(edi << 16); + esi = esi.wrapping_add(edi); + edi ^= (esi >> 13).wrapping_add(esi << 19); + edi = edi.wrapping_add(esi); + esi ^= (edi >> 28).wrapping_add(edi << 4); + esi = esi.wrapping_add(edi); + } + + let _ = eax; + let _ = ecx; + let _ = edx; + + ((edi as u64) << 32) | (esi as u64) +} diff --git a/src-tauri/src/commands/asset_commands.rs b/src-tauri/src/commands/asset_commands.rs new file mode 100644 index 0000000..dbae759 --- /dev/null +++ b/src-tauri/src/commands/asset_commands.rs @@ -0,0 +1,214 @@ +use crate::assets::{art, gumpart, tiledata}; +use crate::db::DbState; +use rusqlite::{params, OptionalExtension}; +use serde::{Deserialize, Serialize}; +use std::path::Path; +use tauri::State; + +#[derive(Debug, Serialize, Deserialize)] +pub struct TileRow { + pub id: u32, + pub name: String, + pub flags: i64, + pub weight: u8, + pub quality: u8, + pub height: u8, + pub hue: u8, + pub anim_id: u16, +} + +/// List static tiles from the SQLite index, paginated. +#[tauri::command] +pub fn list_static_tiles( + offset: u32, + limit: u32, + search: Option, + state: State, +) -> Result, String> { + let conn = state.0.lock().map_err(|e| e.to_string())?; + + let rows = if let Some(q) = search { + let pattern = format!("%{}%", q); + let mut stmt = conn + .prepare( + "SELECT id, name, flags, weight, quality, height, hue, anim_id + FROM static_tiles WHERE name LIKE ?1 ORDER BY id LIMIT ?2 OFFSET ?3", + ) + .map_err(|e| e.to_string())?; + let rows: Vec = stmt + .query_map(params![pattern, limit, offset], row_to_tile) + .map_err(|e| e.to_string())? + .collect::, _>>() + .map_err(|e| e.to_string())?; + rows + } else { + let mut stmt = conn + .prepare( + "SELECT id, name, flags, weight, quality, height, hue, anim_id + FROM static_tiles ORDER BY id LIMIT ?1 OFFSET ?2", + ) + .map_err(|e| e.to_string())?; + let rows: Vec = stmt + .query_map(params![limit, offset], row_to_tile) + .map_err(|e| e.to_string())? + .collect::, _>>() + .map_err(|e| e.to_string())?; + rows + }; + + Ok(rows) +} + +fn row_to_tile(row: &rusqlite::Row) -> rusqlite::Result { + Ok(TileRow { + id: row.get(0)?, + name: row.get(1)?, + flags: row.get(2)?, + weight: row.get(3)?, + quality: row.get(4)?, + height: row.get(5)?, + hue: row.get(6)?, + anim_id: row.get(7)?, + }) +} + +/// Get a single static tile by ID. +#[tauri::command] +pub fn get_static_tile(id: u32, state: State) -> Result, String> { + let conn = state.0.lock().map_err(|e| e.to_string())?; + let mut stmt = conn + .prepare( + "SELECT id, name, flags, weight, quality, height, hue, anim_id + FROM static_tiles WHERE id = ?1", + ) + .map_err(|e| e.to_string())?; + + let result = stmt + .query_row(params![id], row_to_tile) + .optional() + .map_err(|e| e.to_string())?; + + Ok(result) +} + +/// Get art image pixels for a static tile by item ID. +/// Priority: bundled BMP artwork folder → art.mul → artLegacyMUL.uop → tileart.uop +#[tauri::command] +pub fn get_tile_art( + app: tauri::AppHandle, + item_id: u32, + uo_root: String, + _state: State, +) -> Result { + // 1. Bundled BMP artwork (highest quality, always try first) + if let Some(artwork_dir) = art::find_artwork_dir(&app) { + if let Ok(img) = art::decode_static_bmp(&artwork_dir, item_id as usize) { + return Ok(ArtImageResult { + width: img.width, + height: img.height, + pixels: img.pixels, + }); + } + } + + // 2. Fall back to MUL / UOP client files + let root = Path::new(&uo_root); + let img = art::decode_static_any(root, item_id as usize) + .map_err(|e| e.to_string())?; + + Ok(ArtImageResult { + width: img.width, + height: img.height, + pixels: img.pixels, + }) +} + +/// Get a gump image by gump ID. +/// Tries gumpart.mul (Classic), then gumpartLegacyMUL.uop (EC / Classic UOP). +#[tauri::command] +pub fn get_gump_image( + gump_id: u32, + uo_root: String, +) -> Result { + let root = Path::new(&uo_root); + let mul_path = root.join("gumpart.mul"); + let idx_path = root.join("gumpartidx.mul"); + let uop_path = root.join("gumpartLegacyMUL.uop"); + + let img = if mul_path.exists() && idx_path.exists() { + gumpart::decode_gump(&mul_path, &idx_path, gump_id as usize) + .map_err(|e| e.to_string())? + } else if uop_path.exists() { + gumpart::decode_gump_uop(&uop_path, gump_id as usize) + .map_err(|e| e.to_string())? + } else { + return Err( + "No gump art source found. Requires gumpart.mul+gumpartidx.mul \ + or gumpartLegacyMUL.uop." + .to_string(), + ); + }; + + Ok(ArtImageResult { + width: img.width, + height: img.height, + pixels: img.pixels, + }) +} + +/// Walk tiledata.mul and populate the static_tiles table. +#[tauri::command] +pub fn index_assets(uo_root: String, state: State) -> Result { + let root = Path::new(&uo_root); + let tiledata_path = root.join("tiledata.mul"); + + if !tiledata_path.exists() { + return Err("tiledata.mul not found in UO root".to_string()); + } + + let tiles = tiledata::read_item_tiles(&tiledata_path).map_err(|e| e.to_string())?; + let count = tiles.len() as u32; + + let mut conn = state.0.lock().map_err(|e| e.to_string())?; + + // Wrap everything in a single transaction — without this, 39k+ individual + // auto-committed inserts each flush to disk and take minutes to complete. + let tx = conn.transaction().map_err(|e| e.to_string())?; + + tx.execute("DELETE FROM static_tiles", []) + .map_err(|e| e.to_string())?; + + { + let mut stmt = tx + .prepare( + "INSERT INTO static_tiles (id, name, flags, weight, quality, height, hue, anim_id) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", + ) + .map_err(|e| e.to_string())?; + + for tile in &tiles { + stmt.execute(params![ + tile.id, + tile.name, + tile.flags as i64, + tile.weight, + tile.quality, + tile.height, + tile.hue, + tile.anim_id, + ]) + .map_err(|e| e.to_string())?; + } + } + + tx.commit().map_err(|e| e.to_string())?; + + Ok(count) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ArtImageResult { + pub width: u32, + pub height: u32, + pub pixels: Vec, +} diff --git a/src-tauri/src/commands/config_commands.rs b/src-tauri/src/commands/config_commands.rs new file mode 100644 index 0000000..518dc2b --- /dev/null +++ b/src-tauri/src/commands/config_commands.rs @@ -0,0 +1,25 @@ +use crate::config::{self, ValidationResult}; +use crate::db::DbState; +use tauri::State; + +#[tauri::command] +pub fn get_config(key: String, state: State) -> Result, String> { + let conn = state.0.lock().map_err(|e| e.to_string())?; + crate::db::get_config(&conn, &key).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_config(key: String, value: String, state: State) -> Result<(), String> { + let conn = state.0.lock().map_err(|e| e.to_string())?; + crate::db::set_config(&conn, &key, &value).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn validate_uo_root(path: String) -> Result, String> { + Ok(config::validate_uo_root(&path)) +} + +#[tauri::command] +pub fn validate_scripts_path(path: String) -> Result, String> { + Ok(config::validate_scripts_path(&path)) +} diff --git a/src-tauri/src/commands/gump_commands.rs b/src-tauri/src/commands/gump_commands.rs new file mode 100644 index 0000000..5383b6c --- /dev/null +++ b/src-tauri/src/commands/gump_commands.rs @@ -0,0 +1,158 @@ +use crate::assets::gumpart; +use crate::assets::art::ArtImage; +use crate::commands::asset_commands::ArtImageResult; +use serde::{Deserialize, Serialize}; +use std::path::Path; + +/// A single entry from gumps.xml (art gump with numeric ID). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GumpEntry { + pub id: u32, + pub name: String, + pub tags: Vec, +} + +/// A single entry from script_gumps.xml (C# Gump class from ServUO Scripts). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ScriptGumpEntry { + pub class_name: String, + pub file_path: String, + pub tags: Vec, +} + +/// Parse gumps.xml from the "UO Gumps" folder and return all entries. +/// Supports both decimal IDs and 0x-prefixed hex IDs. +#[tauri::command] +pub fn list_gumps(app: tauri::AppHandle) -> Result, String> { + let gump_dir = gumpart::find_gump_dir(&app) + .ok_or_else(|| "UO Gumps folder not found".to_string())?; + + let xml_path = gump_dir.join("gumps.xml"); + if !xml_path.exists() { + return Err("gumps.xml not found in UO Gumps folder".to_string()); + } + + let content = std::fs::read_to_string(&xml_path) + .map_err(|e| format!("Failed to read gumps.xml: {}", e))?; + + let mut entries = Vec::new(); + + for line in content.lines() { + let trimmed = line.trim(); + if !trimmed.starts_with(" = if let Some(t) = tags { + t.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() + } else { + Vec::new() + }; + entries.push(GumpEntry { id, name, tags: tags_vec }); + } + } + + Ok(entries) +} + +fn parse_attr(line: &str, attr: &str) -> Option { + let needle = format!("{}=\"", attr); + let start = line.find(&needle)? + needle.len(); + let rest = &line[start..]; + let end = rest.find('"')?; + Some(rest[..end].to_string()) +} + +fn parse_gump_id(s: &str) -> Option { + if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) { + u32::from_str_radix(hex, 16).ok() + } else { + s.parse::().ok() + } +} + +/// Get a gump image by gump ID. +/// Priority: bundled BMP → gumpart.mul → gumpartLegacyMUL.uop +#[tauri::command] +pub fn get_gump_art( + app: tauri::AppHandle, + gump_id: u32, + uo_root: String, +) -> Result { + // 1. Bundled BMP + if let Some(gump_dir) = gumpart::find_gump_dir(&app) { + if let Ok(img) = gumpart::decode_gump_bmp(&gump_dir, gump_id as usize) { + return Ok(art_image_to_result(img)); + } + } + + // 2. Fall back to MUL / UOP client files + let root = Path::new(&uo_root); + let mul_path = root.join("gumpart.mul"); + let idx_path = root.join("gumpartidx.mul"); + let uop_path = root.join("gumpartLegacyMUL.uop"); + + let img = if mul_path.exists() && idx_path.exists() { + gumpart::decode_gump(&mul_path, &idx_path, gump_id as usize) + .map_err(|e| e.to_string())? + } else if uop_path.exists() { + gumpart::decode_gump_uop(&uop_path, gump_id as usize) + .map_err(|e| e.to_string())? + } else { + return Err("No gump art source found".to_string()); + }; + + Ok(art_image_to_result(img)) +} + +/// Parse script_gumps.xml from the "UO Gumps" folder and return all script gump entries. +#[tauri::command] +pub fn list_script_gumps(app: tauri::AppHandle) -> Result, String> { + let gump_dir = gumpart::find_gump_dir_for_scripts(&app) + .ok_or_else(|| "UO Gumps folder not found".to_string())?; + + let xml_path = gump_dir.join("script_gumps.xml"); + if !xml_path.exists() { + return Err(format!("script_gumps.xml not found — searched: {}", gump_dir.display())); + } + + let content = std::fs::read_to_string(&xml_path) + .map_err(|e| format!("Failed to read script_gumps.xml: {}", e))?; + + let mut entries = Vec::new(); + + for line in content.lines() { + let trimmed = line.trim(); + if !trimmed.starts_with(" = if let Some(t) = tags { + t.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() + } else { + Vec::new() + }; + entries.push(ScriptGumpEntry { class_name, file_path, tags: tags_vec }); + } + } + + Ok(entries) +} + +fn art_image_to_result(img: ArtImage) -> ArtImageResult { + ArtImageResult { + width: img.width, + height: img.height, + pixels: img.pixels, + } +} diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs new file mode 100644 index 0000000..b813be6 --- /dev/null +++ b/src-tauri/src/commands/mod.rs @@ -0,0 +1,4 @@ +pub mod asset_commands; +pub mod config_commands; +pub mod gump_commands; +pub mod script_commands; diff --git a/src-tauri/src/commands/script_commands.rs b/src-tauri/src/commands/script_commands.rs new file mode 100644 index 0000000..4c5b6c3 --- /dev/null +++ b/src-tauri/src/commands/script_commands.rs @@ -0,0 +1,318 @@ +use crate::db::DbState; +use crate::ipc::sidecar::SidecarState; +use rusqlite::params; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use tauri::State; + +// ── Shared data shapes ──────────────────────────────────────────────────────── + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ClassSummary { + pub name: String, + pub namespace: String, + pub file_path: String, + pub base_class: Option, + pub interfaces: Vec, + pub is_gump: bool, + pub is_mobile: bool, + pub is_item: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MethodSummary { + pub id: i64, + pub class_name: String, + pub name: String, + pub return_type: String, + pub parameters: Vec, + pub is_override: bool, + pub is_virtual: bool, + pub calls_gump: bool, + pub gump_class: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ParameterInfo { + pub name: String, + #[serde(rename = "type")] + pub ty: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ClassDetail { + #[serde(rename = "class")] + pub class_info: ClassSummary, + pub methods: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NamespaceTree { + pub namespace: String, + pub classes: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FlowNode { + pub id: String, + #[serde(rename = "type")] + pub node_type: String, + pub label: String, + pub children: Vec, + pub fake_input_key: Option, + pub resolved_gump: Option, + pub asset_ref: Option, +} + +// ── Commands ────────────────────────────────────────────────────────────────── + +/// Walk the ServUO Scripts directory, build the index via the C# sidecar, +/// and persist class/method summaries to SQLite. +#[tauri::command] +pub fn index_scripts( + scripts_path: String, + db: State, + sidecar: State, +) -> Result { + // Ask sidecar to index + let result = sidecar + .send("index_scripts", json!({ "path": scripts_path })) + .map_err(|e| e.to_string())?; + + let classes: Vec = result["classes"] + .as_array() + .cloned() + .unwrap_or_default(); + + let count = classes.len() as u32; + + // Persist to SQLite + let conn = db.0.lock().map_err(|e| e.to_string())?; + conn.execute("DELETE FROM script_methods", []).map_err(|e| e.to_string())?; + conn.execute("DELETE FROM script_classes", []).map_err(|e| e.to_string())?; + + for cls in &classes { + let name = cls["name"].as_str().unwrap_or(""); + let namespace = cls["namespace"].as_str().unwrap_or(""); + let file_path = cls["file_path"].as_str().unwrap_or(""); + let base_class = cls["base_class"].as_str(); + let interfaces = cls["interfaces"] + .as_array() + .and_then(|a| serde_json::to_string(a).ok()) + .unwrap_or_else(|| "[]".to_string()); + let attributes = cls["attributes"] + .as_array() + .and_then(|a| serde_json::to_string(a).ok()) + .unwrap_or_else(|| "[]".to_string()); + let is_gump = cls["is_gump"].as_bool().unwrap_or(false) as i32; + let is_mobile = cls["is_mobile"].as_bool().unwrap_or(false) as i32; + let is_item = cls["is_item"].as_bool().unwrap_or(false) as i32; + + conn.execute( + "INSERT OR REPLACE INTO script_classes + (name, namespace, file_path, base_class, interfaces, attributes, is_gump, is_mobile, is_item) + VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)", + params![name, namespace, file_path, base_class, interfaces, attributes, + is_gump, is_mobile, is_item], + ).map_err(|e| e.to_string())?; + } + + Ok(count) +} + +/// Return the full script namespace tree (namespaces with their class summaries). +#[tauri::command] +pub fn get_script_tree(db: State) -> Result, String> { + let conn = db.0.lock().map_err(|e| e.to_string())?; + + let mut stmt = conn + .prepare( + "SELECT name, namespace, file_path, base_class, interfaces, is_gump, is_mobile, is_item + FROM script_classes ORDER BY namespace, name", + ) + .map_err(|e| e.to_string())?; + + let rows = stmt + .query_map([], |row| { + let interfaces_json: String = row.get(4)?; + let interfaces: Vec = + serde_json::from_str(&interfaces_json).unwrap_or_default(); + Ok(ClassSummary { + name: row.get(0)?, + namespace: row.get(1)?, + file_path: row.get(2)?, + base_class: row.get(3)?, + interfaces, + is_gump: row.get::<_, i32>(5)? != 0, + is_mobile: row.get::<_, i32>(6)? != 0, + is_item: row.get::<_, i32>(7)? != 0, + }) + }) + .map_err(|e| e.to_string())? + .collect::, _>>() + .map_err(|e| e.to_string())?; + + // Group by namespace + let mut map: std::collections::BTreeMap> = + std::collections::BTreeMap::new(); + for cls in rows { + map.entry(cls.namespace.clone()).or_default().push(cls); + } + + Ok(map + .into_iter() + .map(|(namespace, classes)| NamespaceTree { namespace, classes }) + .collect()) +} + +/// Ask the sidecar for full ClassInfo (with methods) and return it. +/// Also caches methods to SQLite on first fetch. +#[tauri::command] +pub fn get_class_detail( + class_name: String, + db: State, + sidecar: State, +) -> Result { + let result = sidecar + .send("get_class", json!({ "class": class_name })) + .map_err(|e| e.to_string())?; + + let cls_val = &result; + let conn = db.0.lock().map_err(|e| e.to_string())?; + + // Parse class summary from sidecar response + let interfaces: Vec = cls_val["interfaces"] + .as_array() + .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .unwrap_or_default(); + + let class = ClassSummary { + name: cls_val["name"].as_str().unwrap_or("").to_string(), + namespace: cls_val["namespace"].as_str().unwrap_or("").to_string(), + file_path: cls_val["file_path"].as_str().unwrap_or("").to_string(), + base_class: cls_val["base_class"].as_str().map(String::from), + interfaces: interfaces.clone(), + is_gump: cls_val["is_gump"].as_bool().unwrap_or(false), + is_mobile: cls_val["is_mobile"].as_bool().unwrap_or(false), + is_item: cls_val["is_item"].as_bool().unwrap_or(false), + }; + + // Parse methods + let methods_val = cls_val["methods"].as_array().cloned().unwrap_or_default(); + let mut methods = Vec::new(); + + // Clear existing methods for this class and re-insert + conn.execute("DELETE FROM script_methods WHERE class_name = ?1", params![class.name]) + .map_err(|e| e.to_string())?; + + for m in &methods_val { + let params_json = serde_json::to_string(&m["parameters"]).unwrap_or_default(); + let method_name = m["name"].as_str().unwrap_or("").to_string(); + let return_type = m["return_type"].as_str().unwrap_or("void").to_string(); + let is_override = m["is_override"].as_bool().unwrap_or(false); + let is_virtual = m["is_virtual"].as_bool().unwrap_or(false); + let calls_gump = m["calls_gump"].as_bool().unwrap_or(false); + let gump_class = m["gump_class"].as_str().map(String::from); + + conn.execute( + "INSERT INTO script_methods + (class_name, name, return_type, parameters, is_override, is_virtual, calls_gump, gump_class) + VALUES (?1,?2,?3,?4,?5,?6,?7,?8)", + params![ + class.name, + method_name, + return_type, + params_json, + is_override as i32, + is_virtual as i32, + calls_gump as i32, + gump_class, + ], + ) + .map_err(|e| e.to_string())?; + let row_id = conn.last_insert_rowid(); + + let parameters: Vec = m["parameters"] + .as_array() + .map(|a| { + a.iter() + .map(|p| ParameterInfo { + name: p["name"].as_str().unwrap_or("").to_string(), + ty: p["type"].as_str().unwrap_or("").to_string(), + }) + .collect() + }) + .unwrap_or_default(); + + methods.push(MethodSummary { + id: row_id, + class_name: class.name.clone(), + name: m["name"].as_str().unwrap_or("").to_string(), + return_type: m["return_type"].as_str().unwrap_or("void").to_string(), + parameters, + is_override, + is_virtual, + calls_gump, + gump_class: m["gump_class"].as_str().map(String::from), + }); + } + + Ok(ClassDetail { class_info: class, methods }) +} + +/// Full-text search across indexed class and method names. +#[tauri::command] +pub fn search_scripts(query: String, db: State) -> Result, String> { + let conn = db.0.lock().map_err(|e| e.to_string())?; + let pattern = format!("%{}%", query); + + let mut stmt = conn + .prepare( + "SELECT DISTINCT c.name, c.namespace, c.file_path, c.base_class, + c.interfaces, c.is_gump, c.is_mobile, c.is_item + FROM script_classes c + LEFT JOIN script_methods m ON m.class_name = c.name + WHERE c.name LIKE ?1 OR c.namespace LIKE ?1 OR m.name LIKE ?1 + ORDER BY c.name LIMIT 100", + ) + .map_err(|e| e.to_string())?; + + let rows = stmt + .query_map(params![pattern], |row| { + let interfaces_json: String = row.get(4)?; + let interfaces: Vec = + serde_json::from_str(&interfaces_json).unwrap_or_default(); + Ok(ClassSummary { + name: row.get(0)?, + namespace: row.get(1)?, + file_path: row.get(2)?, + base_class: row.get(3)?, + interfaces, + is_gump: row.get::<_, i32>(5)? != 0, + is_mobile: row.get::<_, i32>(6)? != 0, + is_item: row.get::<_, i32>(7)? != 0, + }) + }) + .map_err(|e| e.to_string())? + .collect::, _>>() + .map_err(|e| e.to_string())?; + + Ok(rows) +} + +/// Trace the call chain for a method via the C# sidecar and return a FlowNode tree. +#[tauri::command] +pub fn trace_method( + class_name: String, + method_name: String, + sidecar: State, +) -> Result { + let result = sidecar + .send( + "trace_method", + json!({ "class": class_name, "method": method_name }), + ) + .map_err(|e| e.to_string())?; + + serde_json::from_value::(result).map_err(|e| e.to_string()) +} diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs new file mode 100644 index 0000000..41a90d4 --- /dev/null +++ b/src-tauri/src/config.rs @@ -0,0 +1,137 @@ +use serde::{Deserialize, Serialize}; +use std::path::Path; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ValidationResult { + pub file: String, + pub status: ValidationStatus, + pub message: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "lowercase")] +pub enum ValidationStatus { + Ok, + Warn, + Error, +} + +/// Validate a UO root directory. +/// Returns one ValidationResult per expected file. +pub fn validate_uo_root(path: &str) -> Vec { + let root = Path::new(path); + let mut results = Vec::new(); + + // Required — one of each pair must exist + let required_pairs: &[(&str, &str)] = &[ + ("art.mul", "artLegacyMUL.uop"), + ("gumpart.mul", "gumpartLegacyMUL.uop"), + ]; + + for (a, b) in required_pairs { + let a_exists = root.join(a).exists(); + let b_exists = root.join(b).exists(); + if a_exists || b_exists { + let found = if a_exists { a } else { b }; + results.push(ValidationResult { + file: format!("{} / {}", a, b), + status: ValidationStatus::Ok, + message: Some(format!("Found: {}", found)), + }); + } else { + results.push(ValidationResult { + file: format!("{} / {}", a, b), + status: ValidationStatus::Error, + message: Some("Neither file found".to_string()), + }); + } + } + + // Strictly required singles + for file in &["tiledata.mul", "hues.mul", "cliloc.enu", "unifont.mul"] { + let exists = root.join(file).exists(); + results.push(ValidationResult { + file: file.to_string(), + status: if exists { + ValidationStatus::Ok + } else { + ValidationStatus::Error + }, + message: if exists { + None + } else { + Some("File not found".to_string()) + }, + }); + } + + // Optional — warn if missing + let optional: &[(&str, Option<&str>)] = &[ + ("anim.mul", None), + ("multi.mul", None), + ("map0.mul", None), + ("radarcol.mul", None), + ]; + + for (file, alt) in optional { + let exists = root.join(file).exists() + || alt.map(|a| root.join(a).exists()).unwrap_or(false); + results.push(ValidationResult { + file: file.to_string(), + status: if exists { + ValidationStatus::Ok + } else { + ValidationStatus::Warn + }, + message: if exists { + None + } else { + Some("Optional file missing — some features unavailable".to_string()) + }, + }); + } + + results +} + +/// Validate a ServUO Scripts path. +pub fn validate_scripts_path(path: &str) -> Vec { + let p = Path::new(path); + if !p.exists() { + return vec![ValidationResult { + file: path.to_string(), + status: ValidationStatus::Error, + message: Some("Path does not exist".to_string()), + }]; + } + + // Check for at least one .cs file + let has_cs = walkdir::WalkDir::new(p) + .max_depth(5) + .into_iter() + .filter_map(|e| e.ok()) + .any(|e| e.path().extension().map(|x| x == "cs").unwrap_or(false)); + + vec![ValidationResult { + file: path.to_string(), + status: if has_cs { + ValidationStatus::Ok + } else { + ValidationStatus::Warn + }, + message: if has_cs { + None + } else { + Some("No .cs files found — is this the right folder?".to_string()) + }, + }] +} + +/// Detect whether the UO root uses MUL or UOP format. +pub fn detect_asset_format(uo_root: &str) -> &'static str { + if Path::new(uo_root).join("artLegacyMUL.uop").exists() { + "uop" + } else { + "mul" + } +} diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs new file mode 100644 index 0000000..e54d611 --- /dev/null +++ b/src-tauri/src/db.rs @@ -0,0 +1,99 @@ +use anyhow::Result; +use rusqlite::{params, Connection, OptionalExtension}; +use std::path::PathBuf; +use std::sync::Mutex; +use tauri::{AppHandle, Manager}; + +pub struct DbState(pub Mutex); + +pub fn db_path(app: &AppHandle) -> PathBuf { + app.path() + .app_data_dir() + .expect("Failed to get app data dir") + .join("asw.db") +} + +pub fn init(app: &AppHandle) -> Result<()> { + let path = db_path(app); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } + + let conn = Connection::open(&path)?; + // WAL mode: much faster for bulk writes; allows concurrent reads during writes + conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;")?; + conn.execute_batch(SCHEMA)?; + + app.manage(DbState(Mutex::new(conn))); + Ok(()) +} + +pub fn get_config(conn: &Connection, key: &str) -> Result> { + let mut stmt = conn.prepare("SELECT value FROM config WHERE key = ?1")?; + let result = stmt.query_row(params![key], |row| row.get(0)).optional()?; + Ok(result) +} + +pub fn set_config(conn: &Connection, key: &str, value: &str) -> Result<()> { + conn.execute( + "INSERT OR REPLACE INTO config (key, value) VALUES (?1, ?2)", + params![key, value], + )?; + Ok(()) +} + +const SCHEMA: &str = r#" +CREATE TABLE IF NOT EXISTS config ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS static_tiles ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + flags INTEGER NOT NULL, + weight INTEGER NOT NULL, + quality INTEGER NOT NULL, + height INTEGER NOT NULL, + hue INTEGER NOT NULL, + anim_id INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS mobile_entries ( + body_id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + flags INTEGER NOT NULL +); + +CREATE INDEX IF NOT EXISTS idx_static_tiles_name ON static_tiles(name); + +CREATE TABLE IF NOT EXISTS script_classes ( + name TEXT PRIMARY KEY, + namespace TEXT NOT NULL, + file_path TEXT NOT NULL, + base_class TEXT, + interfaces TEXT NOT NULL DEFAULT '[]', + attributes TEXT NOT NULL DEFAULT '[]', + is_gump INTEGER NOT NULL DEFAULT 0, + is_mobile INTEGER NOT NULL DEFAULT 0, + is_item INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS script_methods ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + class_name TEXT NOT NULL, + name TEXT NOT NULL, + return_type TEXT NOT NULL, + parameters TEXT NOT NULL DEFAULT '[]', + is_override INTEGER NOT NULL DEFAULT 0, + is_virtual INTEGER NOT NULL DEFAULT 0, + calls_gump INTEGER NOT NULL DEFAULT 0, + gump_class TEXT, + FOREIGN KEY (class_name) REFERENCES script_classes(name) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS idx_script_classes_namespace ON script_classes(namespace); +CREATE INDEX IF NOT EXISTS idx_script_classes_name ON script_classes(name); +CREATE INDEX IF NOT EXISTS idx_script_methods_class ON script_methods(class_name); +CREATE INDEX IF NOT EXISTS idx_script_methods_name ON script_methods(name); +"#; diff --git a/src-tauri/src/ipc/mod.rs b/src-tauri/src/ipc/mod.rs new file mode 100644 index 0000000..14bc97a --- /dev/null +++ b/src-tauri/src/ipc/mod.rs @@ -0,0 +1 @@ +pub mod sidecar; diff --git a/src-tauri/src/ipc/sidecar.rs b/src-tauri/src/ipc/sidecar.rs new file mode 100644 index 0000000..7b9e6cf --- /dev/null +++ b/src-tauri/src/ipc/sidecar.rs @@ -0,0 +1,159 @@ +use anyhow::{anyhow, Result}; +use serde_json::{json, Value}; +use std::io::{BufRead, BufReader, BufWriter, Write}; +use std::path::PathBuf; +use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio}; +use std::sync::Mutex; +use uuid::Uuid; + +// ── Binary location ─────────────────────────────────────────────────────────── + +fn find_sidecar_binary() -> Result { + // Dev override via env var + if let Ok(p) = std::env::var("ASW_SIDECAR_PATH") { + let path = PathBuf::from(&p); + if path.exists() { + return Ok(path); + } + return Err(anyhow!("ASW_SIDECAR_PATH set but binary not found at: {}", p)); + } + + // Walk upward from the current executable to find the project root, + // then look for the sidecar in known build output locations. + // In dev mode the exe is at: /src-tauri/target/debug/ + // In production the exe is at: / + if let Ok(exe) = std::env::current_exe() { + // Collect ancestor directories up to 8 levels deep + let mut dir = exe.parent().map(|p| p.to_path_buf()); + for _ in 0..8 { + if let Some(ref d) = dir { + // Production (MSI install): Tauri places sidecar next to the exe. + // Tauri bundles with the target-triple suffix; check both forms. + for name in &[ + "asw-sidecar-x86_64-pc-windows-msvc.exe", + "asw-sidecar.exe", + ] { + let candidate = d.join(name); + if candidate.exists() { + return Ok(candidate); + } + } + // Dev: check binaries/ subfolder and sidecar publish output + for suffix in &[ + "binaries/asw-sidecar-x86_64-pc-windows-msvc.exe", + "binaries/asw-sidecar.exe", + "sidecar/bin/Release/net8.0/win-x64/publish/asw-sidecar.exe", + "sidecar/bin/Debug/net8.0/win-x64/asw-sidecar.exe", + ] { + let candidate = d.join(suffix); + if candidate.exists() { + return Ok(candidate); + } + } + dir = d.parent().map(|p| p.to_path_buf()); + } else { + break; + } + } + } + + Err(anyhow!( + "asw-sidecar binary not found. Build it first:\n\ + cd sidecar && dotnet publish -c Release -r win-x64 --self-contained\n\ + Or set ASW_SIDECAR_PATH env var." + )) +} + +// ── Handle (inner, behind Mutex) ───────────────────────────────────────────── + +struct SidecarHandle { + _child: Child, + stdin: BufWriter, + stdout: BufReader, +} + +impl SidecarHandle { + fn spawn() -> Result { + let binary = find_sidecar_binary()?; + + let mut child = Command::new(&binary) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .map_err(|e| anyhow!("Failed to spawn sidecar {:?}: {}", binary, e))?; + + let stdin = BufWriter::new(child.stdin.take().ok_or(anyhow!("No stdin"))?); + let stdout = BufReader::new(child.stdout.take().ok_or(anyhow!("No stdout"))?); + + Ok(SidecarHandle { _child: child, stdin, stdout }) + } + + fn send(&mut self, command: &str, args: Value) -> Result { + let id = Uuid::new_v4().to_string(); + let request = json!({ "id": id, "command": command, "args": args }); + + let line = serde_json::to_string(&request)?; + writeln!(self.stdin, "{}", line)?; + self.stdin.flush()?; + + // Read lines until we get the response matching this request ID + let mut buf = String::new(); + loop { + buf.clear(); + let n = self.stdout.read_line(&mut buf)?; + if n == 0 { + return Err(anyhow!("Sidecar closed stdout unexpectedly")); + } + let trimmed = buf.trim(); + if trimmed.is_empty() { + continue; + } + let response: Value = match serde_json::from_str(trimmed) { + Ok(v) => v, + Err(_) => continue, + }; + if response["id"].as_str() == Some(&id) { + return if response["ok"].as_bool().unwrap_or(false) { + Ok(response["data"].clone()) + } else { + Err(anyhow!( + "{}", + response["error"].as_str().unwrap_or("sidecar error") + )) + }; + } + } + } +} + +// ── Public state type ───────────────────────────────────────────────────────── + +pub struct SidecarState { + handle: Mutex>, +} + +impl SidecarState { + pub fn new() -> Self { + SidecarState { handle: Mutex::new(None) } + } + + /// Ensure the sidecar is running and send a command. + pub fn send(&self, command: &str, args: Value) -> Result { + let mut guard = self.handle.lock().map_err(|_| anyhow!("Sidecar lock poisoned"))?; + + // Spawn if not running + if guard.is_none() { + *guard = Some(SidecarHandle::spawn()?); + } + + guard.as_mut().unwrap().send(command, args) + } + + /// Kill and restart the sidecar (e.g. after a re-index). + pub fn restart(&self) -> Result<()> { + let mut guard = self.handle.lock().map_err(|_| anyhow!("Sidecar lock poisoned"))?; + *guard = Some(SidecarHandle::spawn()?); + Ok(()) + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..6bf49ca --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,47 @@ +mod assets; +mod commands; +mod config; +mod db; +mod ipc; + +use crate::ipc::sidecar::SidecarState; +use tauri::Manager; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_shell::init()) + .setup(|app| { + let app_handle = app.handle().clone(); + db::init(&app_handle).expect("Failed to initialize database"); + app.manage(SidecarState::new()); + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ + // Config + commands::config_commands::get_config, + commands::config_commands::set_config, + commands::config_commands::validate_uo_root, + commands::config_commands::validate_scripts_path, + // Assets (Phase 1) + commands::asset_commands::get_static_tile, + commands::asset_commands::list_static_tiles, + commands::asset_commands::get_tile_art, + commands::asset_commands::get_gump_image, + // Gumps (Phase 4) + commands::gump_commands::list_gumps, + commands::gump_commands::get_gump_art, + commands::gump_commands::list_script_gumps, + commands::asset_commands::index_assets, + // Scripts (Phase 2) + commands::script_commands::index_scripts, + commands::script_commands::get_script_tree, + commands::script_commands::get_class_detail, + commands::script_commands::search_scripts, + // Flow (Phase 3) + commands::script_commands::trace_method, + ]) + .run(tauri::generate_context!()) + .expect("error while running Artificer's Scrollwork"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..9f63d3b --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + artificers_scrollwork_lib::run() +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..1501949 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "Artificer's Scrollwork", + "version": "0.1.0", + "identifier": "com.whitlocktech.artificers-scrollwork", + "build": { + "beforeDevCommand": "npm run dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "npm run build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "title": "Artificer's Scrollwork", + "width": 1400, + "height": 900, + "minWidth": 1100, + "minHeight": 700, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "msi", + "externalBin": [ + "binaries/asw-sidecar" + ], + "resources": { + "../UO artwork": "UO artwork", + "../UO Gumps": "UO Gumps" + }, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..da243ff --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,37 @@ +import { useEffect } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore } from './store/appStore'; +import AppShell from './components/layout/AppShell'; +import ConfigScreen from './components/config/ConfigScreen'; + +export default function App() { + const { isConfigured, setIsConfigured, setUoRoot, setServuoScripts, setCenterMode } = + useAppStore(); + + useEffect(() => { + async function loadConfig() { + try { + const uo = await invoke('get_config', { key: 'uo_root' }); + const scripts = await invoke('get_config', { key: 'seruo_scripts' }); + + if (uo && scripts) { + setUoRoot(uo); + setServuoScripts(scripts); + setIsConfigured(true); + } else { + setCenterMode('config'); + } + } catch (e) { + console.error('Failed to load config:', e); + setCenterMode('config'); + } + } + loadConfig(); + }, []); + + if (!isConfigured) { + return ; + } + + return ; +} diff --git a/src/components/asset/ItemPreview.module.css b/src/components/asset/ItemPreview.module.css new file mode 100644 index 0000000..87d2ee0 --- /dev/null +++ b/src/components/asset/ItemPreview.module.css @@ -0,0 +1,63 @@ +.preview { + display: flex; + flex-direction: column; + height: 100%; +} + +.header { + display: flex; + align-items: baseline; + justify-content: space-between; + padding: 12px 16px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); +} + +.name { + font-size: 16px; + color: var(--accent-gold); + letter-spacing: 0.04em; +} + +.id { + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + color: var(--text-muted); +} + +.canvas { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-elevated); + padding: 24px; +} + +.artCanvas { + image-rendering: pixelated; + max-width: 100%; + max-height: 100%; +} + +.loading, +.error { + font-size: 13px; + color: var(--text-secondary); + font-style: italic; +} + +.error { + color: #ff8080; +} + +.noArt { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + color: var(--text-muted); + font-size: 12px; + letter-spacing: 0.05em; + text-transform: uppercase; +} diff --git a/src/components/asset/ItemPreview.tsx b/src/components/asset/ItemPreview.tsx new file mode 100644 index 0000000..d0fc005 --- /dev/null +++ b/src/components/asset/ItemPreview.tsx @@ -0,0 +1,90 @@ +import { useEffect, useRef, useState } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore } from '../../store/appStore'; +import type { TileInfo } from '../../types/assets'; +import styles from './ItemPreview.module.css'; + +interface ArtImageResult { + width: number; + height: number; + pixels: number[]; +} + +interface Props { + tile: TileInfo; +} + +export default function ItemPreview({ tile }: Props) { + const { uoRoot } = useAppStore(); + const canvasRef = useRef(null); + const [loading, setLoading] = useState(false); + // null = ok, 'none' = no art (show placeholder), string = real error + const [artState, setArtState] = useState(null); + + useEffect(() => { + if (!uoRoot) return; + + async function loadArt() { + setLoading(true); + setArtState(null); + try { + const result = await invoke('get_tile_art', { + itemId: tile.id, + uoRoot, + }); + + const canvas = canvasRef.current; + if (!canvas) return; + + canvas.width = result.width; + canvas.height = result.height; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const imageData = ctx.createImageData(result.width, result.height); + imageData.data.set(result.pixels); + ctx.putImageData(imageData, 0, 0); + } catch { + // Any failure (no art data, unsupported format, buffer error, etc.) + // → show placeholder silently. The art source may just not have this item. + setArtState('none'); + } finally { + setLoading(false); + } + } + + loadArt(); + }, [tile.id, uoRoot]); + + return ( +
+
+ {tile.name || '(unnamed)'} + #{tile.id} +
+ +
+ {loading &&
Loading art…
} + {artState === 'none' && ( +
+ + + + + No art data +
+ )} + {artState && artState !== 'none' && ( +
{artState}
+ )} + +
+
+ ); +} diff --git a/src/components/asset/StaticBrowser.module.css b/src/components/asset/StaticBrowser.module.css new file mode 100644 index 0000000..139142e --- /dev/null +++ b/src/components/asset/StaticBrowser.module.css @@ -0,0 +1,58 @@ +.list { + display: flex; + flex-direction: column; +} + +.row { + display: flex; + align-items: center; + gap: 10px; + padding: 6px 12px; + background: none; + border: none; + border-bottom: 1px solid var(--border); + text-align: left; + cursor: pointer; + transition: background 0.1s; +} + +.row:hover { + background: var(--bg-hover); +} + +.id { + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + color: var(--text-muted); + min-width: 48px; +} + +.name { + font-size: 13px; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.loadMore { + padding: 10px; + background: none; + border: none; + border-top: 1px solid var(--border); + color: var(--accent-gold); + font-size: 13px; + cursor: pointer; +} + +.loadMore:hover { + background: var(--bg-hover); +} + +.empty { + padding: 20px; + color: var(--text-muted); + font-size: 13px; + font-style: italic; + line-height: 1.6; +} diff --git a/src/components/asset/StaticBrowser.tsx b/src/components/asset/StaticBrowser.tsx new file mode 100644 index 0000000..7140385 --- /dev/null +++ b/src/components/asset/StaticBrowser.tsx @@ -0,0 +1,107 @@ +import { useEffect, useState, useCallback } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore } from '../../store/appStore'; +import type { TileInfo } from '../../types/assets'; +import styles from './StaticBrowser.module.css'; + +interface TileRow { + id: number; + name: string; + flags: number; + weight: number; + quality: number; + height: number; + hue: number; + anim_id: number; +} + +const PAGE_SIZE = 100; + +interface Props { + search: string; +} + +export default function StaticBrowser({ search }: Props) { + const { setSelectedTile, setCenterMode } = useAppStore(); + const [tiles, setTiles] = useState([]); + const [offset, setOffset] = useState(0); + const [loading, setLoading] = useState(false); + const [hasMore, setHasMore] = useState(true); + + const loadTiles = useCallback(async (newOffset: number, q: string) => { + setLoading(true); + try { + const rows = await invoke('list_static_tiles', { + offset: newOffset, + limit: PAGE_SIZE, + search: q || null, + }); + if (newOffset === 0) { + setTiles(rows); + } else { + setTiles((prev) => [...prev, ...rows]); + } + setHasMore(rows.length === PAGE_SIZE); + } catch { + // Index not yet built — show empty state + setTiles([]); + setHasMore(false); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + setOffset(0); + loadTiles(0, search); + }, [search, loadTiles]); + + function selectTile(row: TileRow) { + const tile: TileInfo = { + id: row.id, + name: row.name, + flags: row.flags, + weight: row.weight, + quality: row.quality, + height: row.height, + hue: row.hue, + animId: row.anim_id, + }; + setSelectedTile(tile); + setCenterMode('asset_static'); + } + + if (!loading && tiles.length === 0) { + return ( +
+ No tiles indexed yet. +
+ Click [Index] in the toolbar after setting up your paths. +
+ ); + } + + return ( +
+ {tiles.map((t) => ( + + ))} + {hasMore && ( + + )} +
+ ); +} diff --git a/src/components/config/ConfigScreen.module.css b/src/components/config/ConfigScreen.module.css new file mode 100644 index 0000000..d426dd5 --- /dev/null +++ b/src/components/config/ConfigScreen.module.css @@ -0,0 +1,131 @@ +.screen { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-base); + padding: 40px; +} + +.card { + width: 100%; + max-width: 640px; + background: var(--bg-panel); + border: 1px solid var(--border-accent); + padding: 32px; + display: flex; + flex-direction: column; + gap: 24px; +} + +.title { + font-size: 22px; + color: var(--accent-gold); + letter-spacing: 0.06em; +} + +.subtitle { + font-size: 14px; + color: var(--text-secondary); + margin-top: -16px; +} + +.field { + display: flex; + flex-direction: column; + gap: 8px; +} + +.label { + font-family: 'Cinzel', serif; + font-size: 12px; + color: var(--text-secondary); + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.row { + display: flex; + gap: 8px; +} + +.pathInput { + flex: 1; + font-family: 'JetBrains Mono', monospace; + font-size: 12px; +} + +.btn { + padding: 6px 14px; + background: var(--bg-elevated); + border: 1px solid var(--border); + color: var(--text-primary); + font-size: 13px; + transition: border-color 0.15s, color 0.15s; + white-space: nowrap; +} + +.btn:hover { + border-color: var(--border-accent); + color: var(--accent-gold); +} + +.btnPrimary { + background: var(--accent-gold); + border-color: var(--accent-gold); + color: var(--bg-base); + font-family: 'Cinzel', serif; + font-weight: 600; +} + +.btnPrimary:hover { + background: var(--accent-gold-bright); + border-color: var(--accent-gold-bright); + color: var(--bg-base); +} + +.btnPrimary:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.btnSecondary { + color: var(--text-secondary); +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 10px; +} + +.validationList { + list-style: none; + display: flex; + flex-direction: column; + gap: 3px; + font-size: 12px; + font-family: 'JetBrains Mono', monospace; +} + +.validationItem { + display: flex; + gap: 8px; + align-items: baseline; +} + +.validationFile { + color: var(--text-secondary); +} + +.validationMsg { + color: var(--text-muted); +} + +.error { + background: var(--accent-red); + border: 1px solid #b04040; + padding: 10px; + font-size: 13px; + color: #ffd0d0; +} diff --git a/src/components/config/ConfigScreen.tsx b/src/components/config/ConfigScreen.tsx new file mode 100644 index 0000000..7c3f05c --- /dev/null +++ b/src/components/config/ConfigScreen.tsx @@ -0,0 +1,132 @@ +import { useState } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { open } from '@tauri-apps/plugin-dialog'; +import { useAppStore } from '../../store/appStore'; +import styles from './ConfigScreen.module.css'; + +interface ValidationResult { + file: string; + status: 'ok' | 'warn' | 'error'; + message?: string; +} + +interface Props { + onDone?: () => void; +} + +export default function ConfigScreen({ onDone }: Props) { + const { uoRoot, servuoScripts, setUoRoot, setServuoScripts, setIsConfigured } = useAppStore(); + const [uoResults, setUoResults] = useState([]); + const [scriptResults, setScriptResults] = useState([]); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + + async function pickUoRoot() { + const dir = await open({ directory: true, multiple: false, title: 'Select UO Client Folder' }); + if (!dir || Array.isArray(dir)) return; + setUoRoot(dir); + const results = await invoke('validate_uo_root', { path: dir }); + setUoResults(results); + } + + async function pickScriptsPath() { + const dir = await open({ directory: true, multiple: false, title: 'Select ServUO Scripts Folder' }); + if (!dir || Array.isArray(dir)) return; + setServuoScripts(dir); + const results = await invoke('validate_scripts_path', { path: dir }); + setScriptResults(results); + } + + async function handleSave() { + setSaving(true); + setError(null); + try { + await invoke('set_config', { key: 'uo_root', value: uoRoot }); + await invoke('set_config', { key: 'seruo_scripts', value: servuoScripts }); + setIsConfigured(true); + onDone?.(); + } catch (e) { + setError(String(e)); + } finally { + setSaving(false); + } + } + + const canSave = + uoRoot && + servuoScripts && + !uoResults.some((r) => r.status === 'error') && + !scriptResults.some((r) => r.status === 'error'); + + return ( +
+
+

Configuration

+

Set paths to your UO client and ServUO Scripts folder.

+ +
+ +
+ + +
+ {uoResults.length > 0 && } +
+ +
+ +
+ + +
+ {scriptResults.length > 0 && } +
+ + {error &&
{error}
} + +
+ {onDone && ( + + )} + +
+
+
+ ); +} + +function ValidationList({ results }: { results: ValidationResult[] }) { + return ( +
    + {results.map((r, i) => ( +
  • + + {r.status === 'ok' ? '✅' : r.status === 'warn' ? '⚠️' : '❌'} + + {r.file} + {r.message && {r.message}} +
  • + ))} +
+ ); +} diff --git a/src/components/flow/FakeDataInputs.module.css b/src/components/flow/FakeDataInputs.module.css new file mode 100644 index 0000000..5a8f4cc --- /dev/null +++ b/src/components/flow/FakeDataInputs.module.css @@ -0,0 +1,38 @@ +.panel { + padding: 12px; + border-bottom: 1px solid var(--border); +} + +.title { + font-size: 10px; + color: var(--accent-gold); + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 6px; +} + +.hint { + font-size: 11px; + color: var(--text-muted); + font-style: italic; + margin-bottom: 10px; +} + +.row { + display: flex; + flex-direction: column; + gap: 3px; + margin-bottom: 8px; +} + +.label { + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + color: var(--text-secondary); +} + +.input { + width: 100%; + font-size: 12px; + font-family: 'JetBrains Mono', monospace; +} diff --git a/src/components/flow/FakeDataInputs.tsx b/src/components/flow/FakeDataInputs.tsx new file mode 100644 index 0000000..beb159b --- /dev/null +++ b/src/components/flow/FakeDataInputs.tsx @@ -0,0 +1,43 @@ +import { useMemo } from 'react'; +import { useAppStore } from '../../store/appStore'; +import type { FlowNode } from '../../types/scripts'; +import styles from './FakeDataInputs.module.css'; + +function collectFakeKeys(node: FlowNode, keys: Set) { + if (node.fakeInputKey) keys.add(node.fakeInputKey); + for (const child of node.children) collectFakeKeys(child, keys); +} + +export default function FakeDataInputs() { + const { flowRoot, fakeData, setFakeData } = useAppStore(); + + const keys = useMemo(() => { + if (!flowRoot) return []; + const set = new Set(); + collectFakeKeys(flowRoot, set); + return Array.from(set).sort(); + }, [flowRoot]); + + if (keys.length === 0) return null; + + return ( +
+
Fake Data
+
+ Set values to highlight condition branches +
+ {keys.map((key) => ( +
+ + setFakeData(key, e.target.value)} + /> +
+ ))} +
+ ); +} diff --git a/src/components/flow/FlowViewer.module.css b/src/components/flow/FlowViewer.module.css new file mode 100644 index 0000000..095afd4 --- /dev/null +++ b/src/components/flow/FlowViewer.module.css @@ -0,0 +1,99 @@ +.wrapper { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; +} + +.toolbar { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 14px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); + flex-shrink: 0; +} + +.methodLabel { + flex: 1; + font-size: 13px; + color: var(--accent-gold); + letter-spacing: 0.04em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.toolBtn { + padding: 4px 10px; + background: none; + border: 1px solid var(--border); + color: var(--text-secondary); + font-size: 12px; + cursor: pointer; + transition: border-color 0.15s, color 0.15s; +} + +.toolBtn:hover { + border-color: var(--border-accent); + color: var(--accent-gold); +} + +.canvas { + flex: 1; + overflow: hidden; + background: var(--bg-base); + cursor: grab; + position: relative; +} + +.canvas:active { + cursor: grabbing; +} + +.empty { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 14px; + font-style: italic; + padding: 40px; + text-align: center; +} + +.errorWrapper { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 40px; +} + +.errorTitle { + font-family: 'Cinzel', serif; + color: #cc6666; + font-size: 16px; +} + +.errorMsg { + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + color: var(--text-secondary); + max-width: 500px; + text-align: center; +} + +.retryBtn { + padding: 6px 16px; + background: none; + border: 1px solid var(--border-accent); + color: var(--accent-gold); + font-family: 'Cinzel', serif; + font-size: 13px; + cursor: pointer; +} diff --git a/src/components/flow/FlowViewer.tsx b/src/components/flow/FlowViewer.tsx new file mode 100644 index 0000000..ec8e5f3 --- /dev/null +++ b/src/components/flow/FlowViewer.tsx @@ -0,0 +1,284 @@ +import { useEffect, useRef, useState, useCallback } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore } from '../../store/appStore'; +import type { FlowNode } from '../../types/scripts'; +import styles from './FlowViewer.module.css'; + +// ── Layout constants ────────────────────────────────────────────────────────── + +const NODE_W = 220; +const NODE_H = 44; +const H_GAP = 30; // horizontal gap between sibling subtrees +const V_GAP = 60; // vertical gap between levels + +// ── Layout engine ───────────────────────────────────────────────────────────── + +interface LayoutNode { + node: FlowNode; + x: number; + y: number; + width: number; // subtree width +} + +function layoutTree( + node: FlowNode, + depth: number, + xOffset: number +): { positioned: LayoutNode[]; subtreeWidth: number } { + if (node.children.length === 0) { + return { + positioned: [{ node, x: xOffset, y: depth * (NODE_H + V_GAP), width: NODE_W }], + subtreeWidth: NODE_W, + }; + } + + let childX = xOffset; + const allPositioned: LayoutNode[] = []; + let totalWidth = 0; + + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + const result = layoutTree(child, depth + 1, childX); + allPositioned.push(...result.positioned); + childX += result.subtreeWidth + (i < node.children.length - 1 ? H_GAP : 0); + totalWidth += result.subtreeWidth + (i < node.children.length - 1 ? H_GAP : 0); + } + + // Center this node over its children + const subtreeWidth = Math.max(totalWidth, NODE_W); + const nodeX = xOffset + (subtreeWidth - NODE_W) / 2; + + allPositioned.push({ + node, + x: nodeX, + y: depth * (NODE_H + V_GAP), + width: subtreeWidth, + }); + + return { positioned: allPositioned, subtreeWidth }; +} + +function buildEdges(positioned: LayoutNode[]): Array<{ x1: number; y1: number; x2: number; y2: number }> { + const posMap = new Map(); + for (const p of positioned) posMap.set(p.node.id, p); + + const edges: Array<{ x1: number; y1: number; x2: number; y2: number }> = []; + + function walk(node: FlowNode) { + const parent = posMap.get(node.id); + if (!parent) return; + for (const child of node.children) { + const c = posMap.get(child.id); + if (c) { + edges.push({ + x1: parent.x + NODE_W / 2, + y1: parent.y + NODE_H, + x2: c.x + NODE_W / 2, + y2: c.y, + }); + } + walk(child); + } + } + for (const p of positioned) walk(p.node); + return edges; +} + +// ── Node colors by type ─────────────────────────────────────────────────────── + +function nodeStyle(type: FlowNode['type']): { fill: string; stroke: string; text: string } { + switch (type) { + case 'gump_send': + return { fill: '#1a1a2e', stroke: '#8888ff', text: '#aaaaff' }; + case 'condition': + return { fill: '#1e1a10', stroke: '#c8a84b', text: '#e8c86b' }; + case 'branch_true': + return { fill: '#0d1a0d', stroke: '#3a6b3a', text: '#88cc88' }; + case 'branch_false': + return { fill: '#1a0d0d', stroke: '#6b3a3a', text: '#cc8888' }; + case 'branch_case': + return { fill: '#0d1520', stroke: '#3a5a7a', text: '#8aaabb' }; + case 'return': + return { fill: '#1a1010', stroke: '#8b3a3a', text: '#cc6666' }; + case 'property_access': + return { fill: '#141418', stroke: '#4a4060', text: '#8a7ab0' }; + default: + return { fill: '#161410', stroke: '#3a3020', text: '#d4c49a' }; + } +} + +// ── Component ───────────────────────────────────────────────────────────────── + +export default function FlowViewer() { + const { selectedMethod, selectedClass, flowRoot, setFlowRoot, fakeData } = useAppStore(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const svgRef = useRef(null); + const [scale, setScale] = useState(1); + const [pan, setPan] = useState({ x: 20, y: 20 }); + const isPanning = useRef(false); + const panStart = useRef({ x: 0, y: 0 }); + + const traceMethod = useCallback(async () => { + if (!selectedClass || !selectedMethod) return; + setLoading(true); + setError(null); + try { + const node = await invoke('trace_method', { + className: selectedClass.name, + methodName: selectedMethod.name, + }); + setFlowRoot(node); + } catch (e) { + setError(String(e)); + setFlowRoot(null); + } finally { + setLoading(false); + } + }, [selectedClass?.name, selectedMethod?.name]); + + // Auto-trace when selection changes + useEffect(() => { + if (selectedClass && selectedMethod) { + traceMethod(); + } + }, [selectedClass?.name, selectedMethod?.name]); + + // SVG pan & zoom + function onWheel(e: React.WheelEvent) { + e.preventDefault(); + setScale((s) => Math.min(2, Math.max(0.3, s - e.deltaY * 0.001))); + } + + function onMouseDown(e: React.MouseEvent) { + isPanning.current = true; + panStart.current = { x: e.clientX - pan.x, y: e.clientY - pan.y }; + } + + function onMouseMove(e: React.MouseEvent) { + if (!isPanning.current) return; + setPan({ x: e.clientX - panStart.current.x, y: e.clientY - panStart.current.y }); + } + + function onMouseUp() { + isPanning.current = false; + } + + if (!selectedClass || !selectedMethod) { + return ( +
+ Select a method from the left panel to trace its call chain. +
+ ); + } + + if (loading) { + return
Tracing {selectedMethod.name}…
; + } + + if (error) { + return ( +
+
Trace failed
+
{error}
+ +
+ ); + } + + if (!flowRoot) return null; + + const { positioned } = layoutTree(flowRoot, 0, 0); + const edges = buildEdges(positioned); + + const maxX = Math.max(...positioned.map((p) => p.x + NODE_W)) + 40; + const maxY = Math.max(...positioned.map((p) => p.y + NODE_H)) + 40; + + return ( +
+ {/* Toolbar */} +
+ + {selectedClass.name}.{selectedMethod.name} + + + +
+ + {/* SVG canvas */} +
+ + {/* Edges */} + {edges.map((e, i) => ( + + ))} + + {/* Nodes */} + {positioned.map(({ node, x, y }) => { + const s = nodeStyle(node.type); + const isFakeActive = + node.fakeInputKey && fakeData[node.fakeInputKey] !== undefined; + + return ( + + + +
+ {node.type} + {node.label} + {node.resolvedGump && ( +
+ → {node.resolvedGump} +
+ )} +
+
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/components/gump/GumpBrowser.module.css b/src/components/gump/GumpBrowser.module.css new file mode 100644 index 0000000..0ba62ff --- /dev/null +++ b/src/components/gump/GumpBrowser.module.css @@ -0,0 +1,150 @@ +.browser { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; +} + +.subTabs { + display: flex; + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} + +.subTab { + flex: 1; + padding: 6px 0; + background: transparent; + border: none; + border-right: 1px solid var(--border); + color: var(--text-secondary); + font-family: 'Cinzel', serif; + font-size: 11px; + cursor: pointer; + transition: color 0.15s, background 0.15s; +} + +.subTab:last-child { + border-right: none; +} + +.subTab:hover { + color: var(--text-primary); + background: var(--bg-hover); +} + +.subTabActive { + color: var(--accent-gold); + background: var(--bg-elevated); + border-bottom: 2px solid var(--accent-gold); +} + +.tagBar { + display: flex; + flex-wrap: wrap; + gap: 4px; + padding: 6px 8px; + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} + +.tag { + background: var(--bg-elevated); + border: 1px solid var(--border); + color: var(--text-secondary); + font-size: 10px; + padding: 2px 7px; + border-radius: 3px; + cursor: pointer; + font-family: 'Cinzel', serif; + white-space: nowrap; + transition: color 0.15s, border-color 0.15s; +} + +.tag:hover { + color: var(--text-primary); + border-color: var(--border-accent); +} + +.tagActive { + background: var(--bg-hover); + border-color: var(--accent-gold); + color: var(--accent-gold); +} + +.count { + font-size: 10px; + color: var(--text-muted); + padding: 4px 10px; + flex-shrink: 0; + font-family: 'Cinzel', serif; +} + +.list { + flex: 1; + overflow-y: auto; +} + +.item { + display: flex; + flex-direction: column; + gap: 1px; + width: 100%; + padding: 5px 10px; + background: transparent; + border: none; + border-bottom: 1px solid var(--border); + cursor: pointer; + text-align: left; + transition: background 0.1s; +} + +/* Art gump items: id + name side by side */ +.item > .itemId + .itemName { + flex-direction: row; +} + +.item:hover { + background: var(--bg-hover); +} + +.itemId { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 10px; + color: var(--accent-gold); + flex-shrink: 0; + min-width: 48px; +} + +.itemName { + font-family: 'EB Garamond', serif; + font-size: 13px; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.itemPath { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 9px; + color: var(--text-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; +} + +.status { + padding: 16px; + color: var(--text-secondary); + font-family: 'Cinzel', serif; + font-size: 12px; +} + +.error { + padding: 16px; + color: var(--accent-red); + font-family: 'JetBrains Mono', monospace; + font-size: 11px; +} diff --git a/src/components/gump/GumpBrowser.tsx b/src/components/gump/GumpBrowser.tsx new file mode 100644 index 0000000..10d05f5 --- /dev/null +++ b/src/components/gump/GumpBrowser.tsx @@ -0,0 +1,180 @@ +import { useEffect, useState, useMemo } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore, type GumpEntry, type ScriptGumpEntry } from '../../store/appStore'; +import styles from './GumpBrowser.module.css'; + +interface Props { + search: string; +} + +type GumpTab = 'art' | 'scripts'; + +export default function GumpBrowser({ search }: Props) { + const { setSelectedGump, setCenterMode, setSelectedScriptGump } = useAppStore(); + const [gumpTab, setGumpTab] = useState('art'); + + // Art gumps (from gumps.xml) + const [artEntries, setArtEntries] = useState([]); + const [artLoading, setArtLoading] = useState(true); + const [artError, setArtError] = useState(null); + const [artActiveTag, setArtActiveTag] = useState('all'); + + // Script gumps (from script_gumps.xml) + const [scriptEntries, setScriptEntries] = useState([]); + const [scriptLoading, setScriptLoading] = useState(true); + const [scriptError, setScriptError] = useState(null); + const [scriptActiveTag, setScriptActiveTag] = useState('all'); + + useEffect(() => { + invoke('list_gumps') + .then((data) => { setArtEntries(data); setArtLoading(false); }) + .catch((e) => { setArtError(String(e)); setArtLoading(false); }); + + invoke('list_script_gumps') + .then((data) => { setScriptEntries(data); setScriptLoading(false); }) + .catch((e) => { setScriptError(String(e)); setScriptLoading(false); }); + }, []); + + // ── Art tab ──────────────────────────────────────────────────────────────── + const artTopTags = useMemo(() => { + const s = new Set(); + for (const e of artEntries) for (const t of e.tags) s.add(t); + return Array.from(s).sort(); + }, [artEntries]); + + const artFiltered = useMemo(() => { + const q = search.toLowerCase(); + return artEntries.filter((e) => { + const nameMatch = !q || e.name.toLowerCase().includes(q) || String(e.id).includes(q) + || ('0x' + e.id.toString(16)).includes(q.replace('0x', '')); + const tagMatch = artActiveTag === 'all' || e.tags.includes(artActiveTag); + return nameMatch && tagMatch; + }); + }, [artEntries, search, artActiveTag]); + + function selectArtGump(entry: GumpEntry) { + setSelectedGump(entry); + setCenterMode('gump_image'); + } + + // ── Script tab ───────────────────────────────────────────────────────────── + const scriptTopTags = useMemo(() => { + const s = new Set(); + for (const e of scriptEntries) for (const t of e.tags) s.add(t); + return Array.from(s).sort(); + }, [scriptEntries]); + + const scriptFiltered = useMemo(() => { + const q = search.toLowerCase(); + return scriptEntries.filter((e) => { + const nameMatch = !q || e.class_name.toLowerCase().includes(q) + || e.file_path.toLowerCase().includes(q); + const tagMatch = scriptActiveTag === 'all' || e.tags.includes(scriptActiveTag); + return nameMatch && tagMatch; + }); + }, [scriptEntries, search, scriptActiveTag]); + + function selectScriptGump(entry: ScriptGumpEntry) { + setSelectedScriptGump(entry); + setCenterMode('gump_render'); + } + + // ── Render ───────────────────────────────────────────────────────────────── + return ( +
+ {/* Sub-tabs */} +
+ + +
+ + {/* Art tab */} + {gumpTab === 'art' && ( + <> + {artLoading &&
Loading art gumps…
} + {artError &&
{artError}
} + {!artLoading && !artError && ( + <> +
+ + {artTopTags.map((t) => ( + + ))} +
+
{artFiltered.length} gumps
+
+ {artFiltered.map((entry) => ( + + ))} +
+ + )} + + )} + + {/* Scripts tab */} + {gumpTab === 'scripts' && ( + <> + {scriptLoading &&
Loading script gumps…
} + {scriptError &&
{scriptError}
} + {!scriptLoading && !scriptError && ( + <> +
+ + {scriptTopTags.map((t) => ( + + ))} +
+
{scriptFiltered.length} classes
+
+ {scriptFiltered.map((entry) => ( + + ))} +
+ + )} + + )} +
+ ); +} diff --git a/src/components/gump/GumpPreview.module.css b/src/components/gump/GumpPreview.module.css new file mode 100644 index 0000000..d59b710 --- /dev/null +++ b/src/components/gump/GumpPreview.module.css @@ -0,0 +1,84 @@ +.preview { + display: flex; + flex-direction: column; + height: 100%; + padding: 16px; + gap: 12px; + overflow-y: auto; +} + +.header { + display: flex; + align-items: baseline; + gap: 12px; + border-bottom: 1px solid var(--border-accent); + padding-bottom: 8px; +} + +.name { + font-family: 'Cinzel', serif; + font-size: 16px; + color: var(--text-primary); +} + +.id { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 12px; + color: var(--accent-gold); +} + +.tags { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.tag { + font-family: 'Cinzel', serif; + font-size: 10px; + color: var(--text-secondary); + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: 3px; + padding: 2px 7px; +} + +.canvasWrap { + display: flex; + align-items: flex-start; + justify-content: flex-start; + min-height: 80px; + background: var(--bg-base); + border: 1px solid var(--border); + border-radius: 4px; + padding: 8px; +} + +.loading { + color: var(--text-secondary); + font-family: 'Cinzel', serif; + font-size: 12px; + padding: 8px; +} + +.noArt { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 16px; + color: var(--text-muted); + font-size: 12px; + font-family: 'Cinzel', serif; +} + +.artCanvas { + image-rendering: pixelated; + max-width: 100%; +} + +.meta { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 11px; + color: var(--text-muted); +} diff --git a/src/components/gump/GumpPreview.tsx b/src/components/gump/GumpPreview.tsx new file mode 100644 index 0000000..125bb7d --- /dev/null +++ b/src/components/gump/GumpPreview.tsx @@ -0,0 +1,103 @@ +import { useEffect, useRef, useState } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore } from '../../store/appStore'; +import type { GumpEntry } from '../../store/appStore'; +import styles from './GumpPreview.module.css'; + +interface ArtImageResult { + width: number; + height: number; + pixels: number[]; +} + +interface Props { + gump: GumpEntry; +} + +export default function GumpPreview({ gump }: Props) { + const { uoRoot } = useAppStore(); + const canvasRef = useRef(null); + const [loading, setLoading] = useState(false); + const [artState, setArtState] = useState(null); + const [dims, setDims] = useState<{ w: number; h: number } | null>(null); + + useEffect(() => { + setArtState(null); + setDims(null); + if (!uoRoot) return; + + async function loadArt() { + setLoading(true); + try { + const result = await invoke('get_gump_art', { + gumpId: gump.id, + uoRoot, + }); + + const canvas = canvasRef.current; + if (!canvas) return; + + canvas.width = result.width; + canvas.height = result.height; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const imageData = ctx.createImageData(result.width, result.height); + imageData.data.set(result.pixels); + ctx.putImageData(imageData, 0, 0); + setDims({ w: result.width, h: result.height }); + } catch { + setArtState('none'); + } finally { + setLoading(false); + } + } + + loadArt(); + }, [gump.id, uoRoot]); + + const hexId = `0x${gump.id.toString(16).toUpperCase().padStart(4, '0')}`; + + return ( +
+
+ {gump.name} + {hexId} +
+ + {gump.tags.length > 0 && ( +
+ {gump.tags.map((t) => ( + {t} + ))} +
+ )} + +
+ {loading &&
Loading…
} + {artState === 'none' && ( +
+ + + + + No gump art +
+ )} + +
+ + {dims && ( +
+ {dims.w} × {dims.h} +
+ )} +
+ ); +} diff --git a/src/components/gump/ScriptGumpDetail.module.css b/src/components/gump/ScriptGumpDetail.module.css new file mode 100644 index 0000000..4da0c34 --- /dev/null +++ b/src/components/gump/ScriptGumpDetail.module.css @@ -0,0 +1,96 @@ +.container { + display: flex; + flex-direction: column; + height: 100%; + background: var(--bg-base); +} + +.header { + padding: 20px 24px 16px; + border-bottom: 1px solid var(--border-accent); + background: var(--bg-panel); +} + +.className { + display: block; + font-family: 'Cinzel', serif; + font-size: 20px; + color: var(--accent-gold-bright); + letter-spacing: 0.04em; + margin-bottom: 4px; +} + +.friendlyName { + display: block; + font-family: 'EB Garamond', serif; + font-size: 14px; + color: var(--text-secondary); + font-style: italic; +} + +.body { + flex: 1; + padding: 20px 24px; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 20px; +} + +.section { + display: flex; + flex-direction: column; + gap: 6px; +} + +.label { + font-family: 'Cinzel', serif; + font-size: 10px; + color: var(--text-muted); + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.filepath { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 12px; + color: var(--text-secondary); + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: 4px; + padding: 8px 12px; + word-break: break-all; +} + +.tags { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.tag { + background: var(--bg-elevated); + border: 1px solid var(--border-accent); + color: var(--accent-gold); + font-family: 'Cinzel', serif; + font-size: 10px; + padding: 3px 9px; + border-radius: 3px; +} + +.notes { + font-family: 'EB Garamond', serif; + font-size: 14px; + color: var(--text-secondary); + line-height: 1.6; +} + +.empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: var(--text-muted); + font-family: 'Cinzel', serif; + font-size: 13px; +} diff --git a/src/components/gump/ScriptGumpDetail.tsx b/src/components/gump/ScriptGumpDetail.tsx new file mode 100644 index 0000000..479ffcb --- /dev/null +++ b/src/components/gump/ScriptGumpDetail.tsx @@ -0,0 +1,52 @@ +import { useAppStore } from '../../store/appStore'; +import styles from './ScriptGumpDetail.module.css'; + +export default function ScriptGumpDetail() { + const { selectedScriptGump } = useAppStore(); + + if (!selectedScriptGump) { + return
No script gump selected
; + } + + const { class_name, file_path, tags } = selectedScriptGump; + + // Derive a human-readable title from the class name + // e.g. "RunebookGump" → "Runebook Gump" + const friendlyName = class_name.replace(/([A-Z])/g, ' $1').trim(); + + return ( +
+
+ {class_name} + {friendlyName} +
+ +
+
+
Source File
+
{file_path}
+
+ + {tags.length > 0 && ( +
+
Tags
+
+ {tags.map((t) => ( + {t} + ))} +
+
+ )} + +
+
Notes
+
+ This is a ServUO C# Gump class found in the Scripts directory. + Select it in the Scripts tab to trace its method call chain or + inspect its class details. +
+
+
+
+ ); +} diff --git a/src/components/layout/AppShell.module.css b/src/components/layout/AppShell.module.css new file mode 100644 index 0000000..11c07a3 --- /dev/null +++ b/src/components/layout/AppShell.module.css @@ -0,0 +1,54 @@ +.shell { + display: flex; + flex-direction: column; + height: 100vh; + background: var(--bg-base); +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 16px; + height: 42px; + background: var(--bg-panel); + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} + +.title { + font-size: 15px; + color: var(--accent-gold); + letter-spacing: 0.05em; +} + +.headerActions { + display: flex; + gap: 8px; +} + +.headerBtn { + background: none; + border: 1px solid var(--border); + color: var(--text-secondary); + padding: 4px 10px; + font-size: 13px; + font-family: 'Cinzel', serif; + transition: border-color 0.15s, color 0.15s; +} + +.headerBtn:hover { + border-color: var(--border-accent); + color: var(--accent-gold); +} + +.headerBtn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.panels { + display: flex; + flex: 1; + overflow: hidden; +} diff --git a/src/components/layout/AppShell.tsx b/src/components/layout/AppShell.tsx new file mode 100644 index 0000000..9286d89 --- /dev/null +++ b/src/components/layout/AppShell.tsx @@ -0,0 +1,83 @@ +import { useState } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore } from '../../store/appStore'; +import LeftPanel from './LeftPanel'; +import CenterPanel from './CenterPanel'; +import RightPanel from './RightPanel'; +import ConfigScreen from '../config/ConfigScreen'; +import styles from './AppShell.module.css'; + +export default function AppShell() { + const { centerMode, setCenterMode, uoRoot, servuoScripts } = useAppStore(); + const [indexingAssets, setIndexingAssets] = useState(false); + const [indexingScripts, setIndexingScripts] = useState(false); + + async function handleIndexAssets() { + if (!uoRoot) return; + setIndexingAssets(true); + try { + const count = await invoke('index_assets', { uoRoot }); + alert(`Indexed ${count.toLocaleString()} tiles.`); + } catch (e) { + alert(`Asset index failed: ${e}`); + } finally { + setIndexingAssets(false); + } + } + + async function handleIndexScripts() { + if (!servuoScripts) { + alert('Set the ServUO Scripts path in Config first.'); + return; + } + setIndexingScripts(true); + try { + const count = await invoke('index_scripts', { scriptsPath: servuoScripts }); + alert(`Indexed ${count.toLocaleString()} classes.`); + } catch (e) { + alert(`Script index failed: ${e}`); + } finally { + setIndexingScripts(false); + } + } + + return ( +
+
+ Artificer's Scrollwork +
+ + + +
+
+ + {centerMode === 'config' ? ( + setCenterMode('empty')} /> + ) : ( +
+ + + +
+ )} +
+ ); +} diff --git a/src/components/layout/CenterPanel.module.css b/src/components/layout/CenterPanel.module.css new file mode 100644 index 0000000..d5ea9d6 --- /dev/null +++ b/src/components/layout/CenterPanel.module.css @@ -0,0 +1,23 @@ +.panel { + flex: 1; + display: flex; + flex-direction: column; + background: var(--bg-base); + overflow: hidden; +} + +.empty { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 14px; + letter-spacing: 0.05em; +} + +.placeholder { + padding: 40px; + color: var(--text-muted); + font-style: italic; +} diff --git a/src/components/layout/CenterPanel.tsx b/src/components/layout/CenterPanel.tsx new file mode 100644 index 0000000..204011e --- /dev/null +++ b/src/components/layout/CenterPanel.tsx @@ -0,0 +1,35 @@ +import { useAppStore } from '../../store/appStore'; +import ItemPreview from '../asset/ItemPreview'; +import ClassDetail from '../script/ClassDetail'; +import FlowViewer from '../flow/FlowViewer'; +import GumpPreview from '../gump/GumpPreview'; +import ScriptGumpDetail from '../gump/ScriptGumpDetail'; +import styles from './CenterPanel.module.css'; + +export default function CenterPanel() { + const { centerMode, selectedTile, selectedGump, selectedScriptGump } = useAppStore(); + + return ( +
+ {centerMode === 'empty' && ( +
+ Select an item from the left panel +
+ )} + {centerMode === 'asset_static' && selectedTile && ( + + )} + {centerMode === 'script_class' && } + {centerMode === 'flow_method' && } + {centerMode === 'gump_render' && selectedScriptGump && ( + + )} + {centerMode === 'gump_render' && !selectedScriptGump && ( +
Gump Renderer — Phase 4
+ )} + {centerMode === 'gump_image' && selectedGump && ( + + )} +
+ ); +} diff --git a/src/components/layout/LeftPanel.module.css b/src/components/layout/LeftPanel.module.css new file mode 100644 index 0000000..33e87b9 --- /dev/null +++ b/src/components/layout/LeftPanel.module.css @@ -0,0 +1,64 @@ +.panel { + width: 280px; + flex-shrink: 0; + display: flex; + flex-direction: column; + background: var(--bg-panel); + border-right: 1px solid var(--border); + overflow: hidden; +} + +.tabs { + display: flex; + border-bottom: 1px solid var(--border); +} + +.tab { + flex: 1; + padding: 8px 4px; + background: none; + border: none; + border-right: 1px solid var(--border); + color: var(--text-secondary); + font-size: 11px; + font-family: 'Cinzel', serif; + letter-spacing: 0.04em; + transition: background 0.15s, color 0.15s; +} + +.tab:last-child { + border-right: none; +} + +.tab:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.tab.active { + background: var(--bg-elevated); + color: var(--accent-gold); + border-bottom: 2px solid var(--accent-gold); +} + +.search { + padding: 8px; + border-bottom: 1px solid var(--border); +} + +.searchInput { + width: 100%; + font-size: 13px; +} + +.content { + flex: 1; + overflow-y: auto; +} + +.placeholder { + padding: 20px; + color: var(--text-muted); + font-size: 13px; + font-style: italic; +} diff --git a/src/components/layout/LeftPanel.tsx b/src/components/layout/LeftPanel.tsx new file mode 100644 index 0000000..1b7a964 --- /dev/null +++ b/src/components/layout/LeftPanel.tsx @@ -0,0 +1,53 @@ +import { useState } from 'react'; +import { useAppStore, type LeftPanelTab } from '../../store/appStore'; +import StaticBrowser from '../asset/StaticBrowser'; +import ScriptTree from '../script/ScriptTree'; +import GumpBrowser from '../gump/GumpBrowser'; +import styles from './LeftPanel.module.css'; + +const TABS: { id: LeftPanelTab; label: string }[] = [ + { id: 'scripts', label: 'Scripts' }, + { id: 'statics', label: 'Statics' }, + { id: 'mobiles', label: 'Mobiles' }, + { id: 'gumps', label: 'Gumps' }, +]; + +export default function LeftPanel() { + const { activeTab, setActiveTab } = useAppStore(); + const [search, setSearch] = useState(''); + + return ( +
+
+ {TABS.map((t) => ( + + ))} +
+ +
+ setSearch(e.target.value)} + className={styles.searchInput} + /> +
+ +
+ {activeTab === 'statics' && } + {activeTab === 'scripts' && } + {activeTab === 'mobiles' && ( +
Mobile browser — Phase 1 (coming)
+ )} + {activeTab === 'gumps' && } +
+
+ ); +} diff --git a/src/components/layout/RightPanel.module.css b/src/components/layout/RightPanel.module.css new file mode 100644 index 0000000..22d6671 --- /dev/null +++ b/src/components/layout/RightPanel.module.css @@ -0,0 +1,58 @@ +.panel { + width: 320px; + flex-shrink: 0; + background: var(--bg-panel); + border-left: 1px solid var(--border); + overflow-y: auto; + display: flex; + flex-direction: column; +} + +.section { + padding: 12px; + border-bottom: 1px solid var(--border); +} + +.sectionTitle { + font-size: 11px; + color: var(--accent-gold); + letter-spacing: 0.08em; + margin-bottom: 10px; + text-transform: uppercase; +} + +.propList { + display: flex; + flex-direction: column; + gap: 4px; +} + +.propRow { + display: flex; + justify-content: space-between; + font-size: 13px; +} + +.propLabel { + color: var(--text-secondary); +} + +.propValue { + color: var(--text-primary); + font-family: 'JetBrains Mono', monospace; + font-size: 12px; +} + +.subTitle { + font-size: 10px; + color: var(--text-muted); + letter-spacing: 0.06em; + text-transform: uppercase; + margin: 10px 0 6px; +} + +.empty { + color: var(--text-muted); + font-size: 13px; + font-style: italic; +} diff --git a/src/components/layout/RightPanel.tsx b/src/components/layout/RightPanel.tsx new file mode 100644 index 0000000..8c090bc --- /dev/null +++ b/src/components/layout/RightPanel.tsx @@ -0,0 +1,72 @@ +import { useAppStore } from '../../store/appStore'; +import FakeDataInputs from '../flow/FakeDataInputs'; +import styles from './RightPanel.module.css'; + +export default function RightPanel() { + const { selectedTile, selectedMethod, centerMode } = useAppStore(); + + return ( +
+ + {/* Asset properties */} + {centerMode === 'asset_static' && selectedTile && ( +
+
Properties
+
+ + + + + + + + +
+
+ )} + + {/* Method detail */} + {centerMode === 'flow_method' && selectedMethod && ( +
+
Method
+
+ + + + + {selectedMethod.callsGump && ( + + )} +
+ {selectedMethod.parameters.length > 0 && ( + <> +
Parameters
+ {selectedMethod.parameters.map((p, i) => ( + + ))} + + )} +
+ )} + + {/* Fake data inputs for flow conditions */} + {centerMode === 'flow_method' && } + + {/* Default empty state */} + {centerMode === 'empty' && ( +
+
Nothing selected
+
+ )} +
+ ); +} + +function PropRow({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value} +
+ ); +} diff --git a/src/components/script/ClassDetail.module.css b/src/components/script/ClassDetail.module.css new file mode 100644 index 0000000..1f25410 --- /dev/null +++ b/src/components/script/ClassDetail.module.css @@ -0,0 +1,161 @@ +.detail { + display: flex; + flex-direction: column; + height: 100%; + overflow-y: auto; +} + +.header { + padding: 14px 16px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); +} + +.titleRow { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.className { + font-size: 18px; + color: var(--accent-gold); + letter-spacing: 0.04em; +} + +.tag { + font-size: 10px; + font-family: 'Cinzel', serif; + padding: 2px 6px; + border: 1px solid #445; + color: #8888ff; + background: #1a1a2e; + border-radius: 2px; +} + +.tagMobile { + border-color: #344; + color: #88cc88; + background: #1a2e1a; +} + +.tagItem { + border-color: #443; + color: var(--accent-gold); + background: #1e1a10; +} + +.filePath { + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + color: var(--text-muted); + margin-top: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.section { + padding: 12px 16px; + border-bottom: 1px solid var(--border); +} + +.sectionTitle { + font-size: 10px; + color: var(--accent-gold); + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 8px; +} + +.chain { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + flex-wrap: wrap; +} + +.chainItem { + color: var(--text-primary); + font-family: 'JetBrains Mono', monospace; + font-size: 12px; +} + +.arrow { + color: var(--text-muted); +} + +.interfaces { + font-size: 12px; + color: var(--text-secondary); + margin-top: 6px; + font-style: italic; +} + +.loading { + padding: 16px; + color: var(--text-muted); + font-size: 13px; + font-style: italic; +} + +.methodRow { + display: flex; + width: 100%; + padding: 6px 8px; + background: none; + border: none; + border-bottom: 1px solid var(--border); + text-align: left; + cursor: pointer; + transition: background 0.1s; +} + +.methodRow:hover { + background: var(--bg-hover); +} + +.hookRow { + border-left: 2px solid var(--accent-gold); + padding-left: 6px; +} + +.methodSig { + display: flex; + align-items: baseline; + gap: 6px; + flex-wrap: wrap; + font-family: 'JetBrains Mono', monospace; + font-size: 12px; +} + +.returnType { + color: var(--text-muted); +} + +.methodName { + color: var(--text-primary); + font-weight: 500; +} + +.params { + color: var(--text-secondary); + font-size: 11px; +} + +.modBadge { + font-size: 10px; + font-family: 'Cinzel', serif; + padding: 1px 5px; + border: 1px solid var(--border); + color: var(--text-muted); + border-radius: 2px; +} + +.gumpBadge { + border-color: #445; + color: #8888ff; + background: #1a1a2e; +} diff --git a/src/components/script/ClassDetail.tsx b/src/components/script/ClassDetail.tsx new file mode 100644 index 0000000..10116bc --- /dev/null +++ b/src/components/script/ClassDetail.tsx @@ -0,0 +1,124 @@ +import { useEffect, useState } from 'react'; +import { useAppStore } from '../../store/appStore'; +import { useScripts } from '../../hooks/useScripts'; +import type { ClassDetail as ClassDetailType, MethodSummary } from '../../types/scripts'; +import { WELL_KNOWN_HOOKS } from '../../types/scripts'; +import styles from './ClassDetail.module.css'; + +export default function ClassDetail() { + const { selectedClass, setSelectedMethod, setCenterMode } = useAppStore(); + const { getClassDetail } = useScripts(); + const [detail, setDetail] = useState(null); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (!selectedClass) return; + setLoading(true); + setDetail(null); + getClassDetail(selectedClass.name).then((d) => { + setDetail(d); + setLoading(false); + }); + }, [selectedClass?.name]); + + if (!selectedClass) return null; + + const cls = detail?.class ?? selectedClass; + const methods = detail?.methods ?? []; + + // Split well-known hooks from other methods + const hookNames = new Set(WELL_KNOWN_HOOKS.map((h) => h.method)); + const hooks = methods.filter((m) => hookNames.has(m.name)); + const others = methods.filter((m) => !hookNames.has(m.name)); + + function selectMethod(method: MethodSummary) { + setSelectedMethod(method); + setCenterMode('flow_method'); + } + + return ( +
+ {/* Header */} +
+
+ {cls.name} + {cls.isGump && Gump} + {cls.isMobile && Mobile} + {cls.isItem && Item} +
+
{cls.filePath}
+
+ + {/* Inheritance */} +
+
Inheritance
+
+ {cls.baseClass ? ( + {cls.name} + ) : null} + {cls.baseClass && } + {cls.baseClass ?? cls.name} +
+ {cls.interfaces.length > 0 && ( +
+ implements: {cls.interfaces.join(', ')} +
+ )} +
+ + {loading &&
Loading methods…
} + + {/* Well-known hooks */} + {hooks.length > 0 && ( +
+
Hooks
+ {hooks.map((m) => ( + + ))} +
+ )} + + {/* Other methods */} + {others.length > 0 && ( +
+
+ Methods ({others.length}) +
+ {others.map((m) => ( + + ))} +
+ )} +
+ ); +} + +function MethodRow({ + method, + onSelect, + isHook, +}: { + method: MethodSummary; + onSelect: (m: MethodSummary) => void; + isHook?: boolean; +}) { + const paramStr = method.parameters + .map((p) => `${p.type} ${p.name}`) + .join(', '); + + return ( + + ); +} diff --git a/src/components/script/ScriptTree.module.css b/src/components/script/ScriptTree.module.css new file mode 100644 index 0000000..1d407fd --- /dev/null +++ b/src/components/script/ScriptTree.module.css @@ -0,0 +1,129 @@ +.list { + display: flex; + flex-direction: column; +} + +.namespace { + border-bottom: 1px solid var(--border); +} + +.nsHeader { + display: flex; + align-items: center; + gap: 6px; + width: 100%; + padding: 6px 10px; + background: none; + border: none; + text-align: left; + cursor: pointer; + color: var(--text-secondary); + font-size: 12px; + font-family: 'Cinzel', serif; + letter-spacing: 0.04em; + transition: background 0.1s, color 0.1s; +} + +.nsHeader:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.nsChevron { + font-size: 10px; + color: var(--accent-gold); + min-width: 10px; +} + +.nsName { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.nsCount { + font-size: 10px; + color: var(--text-muted); + font-family: 'JetBrains Mono', monospace; +} + +.nsClasses { + display: flex; + flex-direction: column; +} + +.classRow { + display: flex; + align-items: center; + gap: 6px; + padding: 5px 10px 5px 22px; + background: none; + border: none; + border-bottom: 1px solid var(--border); + text-align: left; + cursor: pointer; + transition: background 0.1s; +} + +.classRow:hover { + background: var(--bg-hover); +} + +.className { + flex: 1; + font-size: 13px; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.classNs { + font-size: 10px; + color: var(--text-muted); + font-family: 'JetBrains Mono', monospace; +} + +.badge { + font-size: 9px; + font-family: 'Cinzel', serif; + padding: 1px 4px; + border-radius: 2px; + border: 1px solid; + min-width: 16px; + text-align: center; +} + +.badgeGump { + color: #8888ff; + border-color: #445; + background: #1a1a2e; +} + +.badgeMobile { + color: #88cc88; + border-color: #344; + background: #1a2e1a; +} + +.badgeItem { + color: var(--accent-gold); + border-color: #443; + background: #1e1a10; +} + +.empty { + padding: 20px; + color: var(--text-muted); + font-size: 13px; + font-style: italic; + line-height: 1.6; +} + +.error { + padding: 16px; + color: #ff8080; + font-size: 12px; + font-family: 'JetBrains Mono', monospace; +} diff --git a/src/components/script/ScriptTree.tsx b/src/components/script/ScriptTree.tsx new file mode 100644 index 0000000..fa6a527 --- /dev/null +++ b/src/components/script/ScriptTree.tsx @@ -0,0 +1,147 @@ +import { useEffect, useState } from 'react'; +import { useAppStore } from '../../store/appStore'; +import { useScripts } from '../../hooks/useScripts'; +import type { ClassSummary, NamespaceTree } from '../../types/scripts'; +import styles from './ScriptTree.module.css'; + +interface Props { + search: string; +} + +export default function ScriptTree({ search }: Props) { + const { setSelectedClass, setCenterMode } = useAppStore(); + const { tree, loading, error, loadTree, search: searchScripts } = useScripts(); + const [searchResults, setSearchResults] = useState(null); + const [expanded, setExpanded] = useState>(new Set()); + + useEffect(() => { + loadTree(); + }, []); + + useEffect(() => { + if (!search.trim()) { + setSearchResults(null); + return; + } + const timer = setTimeout(async () => { + const results = await searchScripts(search); + setSearchResults(results); + }, 250); + return () => clearTimeout(timer); + }, [search]); + + function toggleNamespace(ns: string) { + setExpanded((prev) => { + const next = new Set(prev); + next.has(ns) ? next.delete(ns) : next.add(ns); + return next; + }); + } + + function selectClass(cls: ClassSummary) { + setSelectedClass(cls); + setCenterMode('script_class'); + } + + if (loading && tree.length === 0) { + return
Loading scripts…
; + } + if (error) { + return
{error}
; + } + if (!loading && tree.length === 0) { + return ( +
+ No scripts indexed yet. +
+ Set your Scripts path in Config and click [Index Scripts]. +
+ ); + } + + // Search mode + if (searchResults !== null) { + return ( +
+ {searchResults.length === 0 && ( +
No results for "{search}"
+ )} + {searchResults.map((cls) => ( + + ))} +
+ ); + } + + return ( +
+ {tree.map((ns) => ( + toggleNamespace(ns.namespace)} + onSelect={selectClass} + /> + ))} +
+ ); +} + +function NamespaceGroup({ + ns, + expanded, + onToggle, + onSelect, +}: { + ns: NamespaceTree; + expanded: boolean; + onToggle: () => void; + onSelect: (cls: ClassSummary) => void; +}) { + const shortNs = ns.namespace.split('.').pop() ?? ns.namespace; + + return ( +
+ + {expanded && ( +
+ {ns.classes.map((cls) => ( + + ))} +
+ )} +
+ ); +} + +function ClassRow({ + cls, + onSelect, + showNamespace, +}: { + cls: ClassSummary; + onSelect: (cls: ClassSummary) => void; + showNamespace?: boolean; +}) { + const badge = cls.isGump ? 'G' : cls.isMobile ? 'M' : cls.isItem ? 'I' : null; + const badgeClass = cls.isGump + ? styles.badgeGump + : cls.isMobile + ? styles.badgeMobile + : styles.badgeItem; + + return ( + + ); +} diff --git a/src/hooks/useScripts.ts b/src/hooks/useScripts.ts new file mode 100644 index 0000000..588204d --- /dev/null +++ b/src/hooks/useScripts.ts @@ -0,0 +1,40 @@ +import { useState, useCallback } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import type { ClassSummary, ClassDetail, NamespaceTree } from '../types/scripts'; + +export function useScripts() { + const [tree, setTree] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const loadTree = useCallback(async () => { + setLoading(true); + setError(null); + try { + const result = await invoke('get_script_tree'); + setTree(result); + } catch (e) { + setError(String(e)); + } finally { + setLoading(false); + } + }, []); + + const search = useCallback(async (query: string): Promise => { + try { + return await invoke('search_scripts', { query }); + } catch { + return []; + } + }, []); + + const getClassDetail = useCallback(async (className: string): Promise => { + try { + return await invoke('get_class_detail', { className }); + } catch { + return null; + } + }, []); + + return { tree, loading, error, loadTree, search, getClassDetail }; +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..2a3faff --- /dev/null +++ b/src/index.css @@ -0,0 +1,84 @@ +:root { + --bg-base: #0e0d0b; + --bg-panel: #161410; + --bg-elevated: #1e1b16; + --bg-hover: #272318; + --border: #3a3020; + --border-accent: #6b5a2e; + --text-primary: #d4c49a; + --text-secondary: #8a7a5a; + --text-muted: #4a4030; + --accent-gold: #c8a84b; + --accent-gold-bright: #e8c86b; + --accent-red: #8b3a3a; + --accent-green: #3a6b3a; + --scrollbar-thumb: #3a3020; + + font-family: 'EB Garamond', Georgia, serif; + font-size: 16px; + color: var(--text-primary); + background-color: var(--bg-base); +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + min-height: 100vh; + background-color: var(--bg-base); + color: var(--text-primary); + overflow: hidden; +} + +#root { + height: 100vh; + display: flex; + flex-direction: column; +} + +/* Scrollbars */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: var(--bg-panel); +} +::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb); + border-radius: 3px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--border-accent); +} + +/* Typography helpers */ +.font-cinzel { + font-family: 'Cinzel', serif; +} +.font-mono { + font-family: 'JetBrains Mono', 'Fira Code', monospace; +} + +/* Buttons */ +button { + cursor: pointer; + font-family: inherit; +} + +/* Input */ +input, textarea, select { + font-family: inherit; + background: var(--bg-elevated); + color: var(--text-primary); + border: 1px solid var(--border); + border-radius: 3px; + padding: 6px 10px; + outline: none; +} +input:focus, textarea:focus, select:focus { + border-color: var(--border-accent); +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..a2bf01b --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + +); diff --git a/src/store/appStore.ts b/src/store/appStore.ts new file mode 100644 index 0000000..b75733d --- /dev/null +++ b/src/store/appStore.ts @@ -0,0 +1,121 @@ +import { create } from 'zustand'; +import type { TileInfo } from '../types/assets'; +import type { ClassSummary, MethodSummary, FlowNode } from '../types/scripts'; +import type { GumpDrawList } from '../types/gump'; + +export type LeftPanelTab = 'scripts' | 'statics' | 'mobiles' | 'gumps'; + +export type CenterMode = + | 'empty' + | 'config' + | 'asset_static' + | 'asset_mobile' + | 'script_class' + | 'flow_method' + | 'gump_render' + | 'gump_image'; + +export interface GumpEntry { + id: number; + name: string; + tags: string[]; +} + +export interface ScriptGumpEntry { + class_name: string; + file_path: string; + tags: string[]; +} + +interface AppState { + // Config + uoRoot: string; + servuoScripts: string; + assetFormat: 'mul' | 'uop'; + isConfigured: boolean; + setUoRoot: (p: string) => void; + setServuoScripts: (p: string) => void; + setAssetFormat: (f: 'mul' | 'uop') => void; + setIsConfigured: (v: boolean) => void; + + // Navigation + activeTab: LeftPanelTab; + centerMode: CenterMode; + setActiveTab: (tab: LeftPanelTab) => void; + setCenterMode: (mode: CenterMode) => void; + + // Asset browser + selectedTile: TileInfo | null; + setSelectedTile: (tile: TileInfo | null) => void; + + // Script browser + selectedClass: ClassSummary | null; + setSelectedClass: (c: ClassSummary | null) => void; + + // Method / flow + selectedMethod: MethodSummary | null; + setSelectedMethod: (m: MethodSummary | null) => void; + + // Flow tree + flowRoot: FlowNode | null; + setFlowRoot: (node: FlowNode | null) => void; + + // Gump + gumpDrawList: GumpDrawList | null; + setGumpDrawList: (g: GumpDrawList | null) => void; + + // Gump browser (Phase 4) + selectedGump: GumpEntry | null; + setSelectedGump: (g: GumpEntry | null) => void; + + // Script gump browser + selectedScriptGump: ScriptGumpEntry | null; + setSelectedScriptGump: (g: ScriptGumpEntry | null) => void; + + // Fake data for dynamic gumps / flow conditions + fakeData: Record; + setFakeData: (key: string, value: string) => void; + clearFakeData: () => void; +} + +export const useAppStore = create((set) => ({ + uoRoot: '', + servuoScripts: '', + assetFormat: 'mul', + isConfigured: false, + setUoRoot: (p) => set({ uoRoot: p }), + setServuoScripts: (p) => set({ servuoScripts: p }), + setAssetFormat: (f) => set({ assetFormat: f }), + setIsConfigured: (v) => set({ isConfigured: v }), + + activeTab: 'statics', + centerMode: 'empty', + setActiveTab: (tab) => set({ activeTab: tab }), + setCenterMode: (mode) => set({ centerMode: mode }), + + selectedTile: null, + setSelectedTile: (tile) => set({ selectedTile: tile }), + + selectedClass: null, + setSelectedClass: (c) => set({ selectedClass: c, selectedMethod: null, flowRoot: null }), + + selectedMethod: null, + setSelectedMethod: (m) => set({ selectedMethod: m }), + + flowRoot: null, + setFlowRoot: (node) => set({ flowRoot: node }), + + gumpDrawList: null, + setGumpDrawList: (g) => set({ gumpDrawList: g }), + + selectedGump: null, + setSelectedGump: (g) => set({ selectedGump: g }), + + selectedScriptGump: null, + setSelectedScriptGump: (g) => set({ selectedScriptGump: g }), + + fakeData: {}, + setFakeData: (key, value) => + set((s) => ({ fakeData: { ...s.fakeData, [key]: value } })), + clearFakeData: () => set({ fakeData: {} }), +})); diff --git a/src/types/assets.ts b/src/types/assets.ts new file mode 100644 index 0000000..8c4255a --- /dev/null +++ b/src/types/assets.ts @@ -0,0 +1,24 @@ +export interface TileInfo { + id: number; + name: string; + flags: number; + weight: number; + quality: number; + height: number; + hue: number; + animId: number; + artData?: ImageData; +} + +export interface MobileInfo { + bodyId: number; + name: string; + flags: number; + frames: ImageData[]; +} + +export interface HueInfo { + id: number; + name: string; + colors: number[]; +} diff --git a/src/types/flow.ts b/src/types/flow.ts new file mode 100644 index 0000000..95a0fcd --- /dev/null +++ b/src/types/flow.ts @@ -0,0 +1 @@ +export type { FlowNode } from './scripts'; diff --git a/src/types/gump.ts b/src/types/gump.ts new file mode 100644 index 0000000..c7a44fc --- /dev/null +++ b/src/types/gump.ts @@ -0,0 +1,22 @@ +export type DrawCall = + | { type: 'background'; x: number; y: number; w: number; h: number; gumpId: number } + | { type: 'image'; x: number; y: number; gumpId: number; hue?: number } + | { type: 'label'; x: number; y: number; hue: number; text: string } + | { type: 'button'; x: number; y: number; normalId: number; pressedId: number; buttonId: number } + | { type: 'html'; x: number; y: number; w: number; h: number; text: string; hasBackground: boolean; hasScrollbar: boolean } + | { type: 'item'; x: number; y: number; itemId: number; hue?: number } + | { type: 'alpha_region'; x: number; y: number; w: number; h: number } + | { type: 'tiled_image'; x: number; y: number; w: number; h: number; gumpId: number } + | { type: 'checkbox'; x: number; y: number; inactiveId: number; activeId: number; checked: boolean; switchId: number } + | { type: 'radio'; x: number; y: number; inactiveId: number; activeId: number; checked: boolean; returnValue: number } + | { type: 'text_entry'; x: number; y: number; w: number; h: number; hue: number; entryId: number; initialText: string }; + +export interface GumpDrawList { + className: string; + filePath: string; + isDynamic: boolean; + dynamicInputKeys: string[]; + drawCalls: DrawCall[]; + width: number; + height: number; +} diff --git a/src/types/scripts.ts b/src/types/scripts.ts new file mode 100644 index 0000000..1fa8e4e --- /dev/null +++ b/src/types/scripts.ts @@ -0,0 +1,69 @@ +export interface ParameterInfo { + name: string; + type: string; +} + +export interface PropertyInfo { + name: string; + type: string; + hasGetter: boolean; + hasSetter: boolean; +} + +export interface MethodSummary { + id: number; + className: string; + name: string; + returnType: string; + parameters: ParameterInfo[]; + isOverride: boolean; + isVirtual: boolean; + callsGump: boolean; + gumpClass: string | null; +} + +export interface ClassSummary { + name: string; + namespace: string; + filePath: string; + baseClass: string | null; + interfaces: string[]; + isGump: boolean; + isMobile: boolean; + isItem: boolean; +} + +export interface ClassDetail { + class: ClassSummary; + methods: MethodSummary[]; +} + +export interface NamespaceTree { + namespace: string; + classes: ClassSummary[]; +} + +export interface FlowNode { + id: string; + type: 'method_call' | 'condition' | 'branch_true' | 'branch_false' | 'branch_case' + | 'gump_send' | 'return' | 'property_access'; + label: string; + children: FlowNode[]; + fakeInputKey?: string; + resolvedGump?: string; + assetRef?: number; +} + +// Well-known ServUO hooks surfaced as quick-launch entry points +export const WELL_KNOWN_HOOKS: { method: string; label: string }[] = [ + { method: 'OnDoubleClick', label: 'OnDoubleClick' }, + { method: 'OnSingleClick', label: 'OnSingleClick' }, + { method: 'OnDeath', label: 'OnDeath' }, + { method: 'OnDamage', label: 'OnDamage' }, + { method: 'OnDelete', label: 'OnDelete' }, + { method: 'OnMoveOver', label: 'OnMoveOver' }, + { method: 'OnGainedLevel', label: 'OnGainedLevel' }, + { method: 'OnSkillUse', label: 'OnSkillUse' }, + { method: 'OnHit', label: 'OnHit' }, + { method: 'OnMiss', label: 'OnMiss' }, +]; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..60ba3dc --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,6 @@ +/// + +declare module '*.module.css' { + const classes: Record; + export default classes; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a4c834a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..f8c4b60 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig(async () => ({ + plugins: [react()], + clearScreen: false, + server: { + port: 1420, + strictPort: true, + watch: { + ignored: ["**/src-tauri/**"], + }, + }, +}));