feat: integrate Vue DevTools for local development and improve debugging capabilities with debugpy

This commit is contained in:
Ivan 2026-08-13 16:35:41 -05:00
parent fe60382d7a
commit beb58b5a9e
No known key found for this signature in database
24 changed files with 1409 additions and 10 deletions

View file

@ -110,6 +110,7 @@ task test:frontend
task test:e2e
task run
task dev
task debug
```
Optional RNS/rngit tooling (requires mesh reachability, often slower than PyPI):
@ -290,6 +291,10 @@ Common overrides (CLI flags usually mirror these):
| `MESHCHAT_DISABLE_CSRF` | Dangerous. Tests/dev only |
| `MESHCHAT_SKIP_STORAGE_LOCK` | Dangerous. Avoid overlapping instances carefully |
| `MESHCHAT_RNS_LOG_LEVEL` | RNS log verbosity |
| `MESHCHAT_DEBUGPY` | `task debug`: listen with debugpy |
| `MESHCHAT_DEBUGPY_PORT` | debugpy port (default 5678, bind 127.0.0.1) |
| `MESHCHAT_DEBUGPY_WAIT` | `1` pauses backend until a debugger attaches |
| `MESHCHAT_VUE_DEVTOOLS` | `0` disables the Vite Vue DevTools overlay |
Restore helpers:

5
.gitignore vendored
View file

@ -1,6 +1,9 @@
# IDE and editor files
.idea
.vscode/
.vscode/*
!.vscode/launch.json
!.vscode/tasks.json
!.vscode/extensions.json
*.swp
*.swo
*~

9
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"recommendations": [
"Vue.volar",
"ms-python.python",
"ms-python.debugpy",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

42
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,42 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Backend: Launch meshchat",
"type": "debugpy",
"request": "launch",
"module": "meshchatx.meshchat",
"args": ["--headless", "--host", "127.0.0.1", "--port", "8000"],
"cwd": "${workspaceFolder}",
"justMyCode": true,
"env": {
"PYTHONUNBUFFERED": "1"
}
},
{
"name": "Backend: Attach debugpy",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "127.0.0.1",
"port": 5678
},
"justMyCode": true
},
{
"name": "Frontend: Chrome (Vite)",
"type": "chrome",
"request": "launch",
"url": "http://127.0.0.1:5173",
"webRoot": "${workspaceFolder}/meshchatx/src/frontend"
}
],
"compounds": [
{
"name": "MeshChatX: Vite + Python",
"configurations": ["Backend: Attach debugpy", "Frontend: Chrome (Vite)"],
"preLaunchTask": "meshchatx: debug",
"stopAll": true
}
]
}

47
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,47 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "meshchatx: debug",
"type": "shell",
"command": "task debug",
"isBackground": true,
"problemMatcher": {
"owner": "meshchatx-dev",
"pattern": {
"regexp": "^$"
},
"background": {
"activeOnStart": true,
"beginsPattern": "\\[dev\\] (Waiting for backend|debugpy)",
"endsPattern": "Local:\\s+https?://127\\.0\\.0\\.1:5173"
}
},
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
},
{
"label": "meshchatx: dev",
"type": "shell",
"command": "task dev",
"isBackground": true,
"problemMatcher": {
"owner": "meshchatx-dev",
"pattern": {
"regexp": "^$"
},
"background": {
"activeOnStart": true,
"beginsPattern": "\\[dev\\] Waiting for backend",
"endsPattern": "Local:\\s+https?://127\\.0\\.0\\.1:5173"
}
},
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
}
]
}

View file

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Added
- **Local developer tooling**: [Vue DevTools](https://devtools.vuejs.org/) overlay on `task dev` (Vite serve at http://127.0.0.1:5173 only, `MESHCHAT_VUE_DEVTOOLS=0` to disable). Production `vite build` keeps `__VUE_PROD_DEVTOOLS__` false. `task debug` wraps the backend with debugpy on `127.0.0.1:5678`. Debugger launch configs: **MeshChatX: Vite + Python**. Playwright e2e forces DevTools off.
- **Public demo mode**: Read-only mesh showcase via `MESHCHAT_DEMO_MODE` or `--demo` (blocked sends and API mutations, in-app demo banner). Optional ALTCHA v3 on login/setup, `MESHCHAT_AUTH_PAGE_HINT` for login page text, and [docker-compose.demo.yml](docker-compose.demo.yml) for Coolify.
- **Database upgrades**: Automatic backup-pre-migrate-*.zip before schema migrations (skip with `MESHCHAT_SKIP_PRE_MIGRATE_BACKUP=1`). CLI `--list-backups` and `--export-backup` to list or copy backups for rollback. Post-migrate quick_check, structured migration logs, pre-migrate zip retention (five by default), storage lock for single-writer volumes, and N-1/N-2 fixture upgrade tests.
- **App sidebar**: Grouped layout is the default. Primary links sit under Communicate, Explore, Network, and App labels, with a More disclosure for Archives, Interfaces, Identities, About, and similar destinations. Appearance setting `app_sidebar_layout` can switch back to Classic (one flat list and the expanded identity footer). The grouped footer is a compact identity chip with announce, QR, and auto-announce interval (15 minutes through 24 hours). Press and hold a nav item or section while the sidebar is expanded to reorder links and groups. A save icon appears in the collapse row. Order is stored in the browser.

View file

@ -175,10 +175,20 @@ tasks:
- uv run python -m meshchatx.meshchat
dev:
desc: Vite HMR + backend (http://localhost:5173)
desc: Vite HMR + backend (http://127.0.0.1:5173) with Vue DevTools
cmds:
- bash scripts/dev-local.sh
debug:
desc: task dev plus Python debugpy on 127.0.0.1:5678 (debugger attach)
cmds:
- MESHCHAT_DEBUGPY=1 bash scripts/dev-local.sh
debug:wait:
desc: task debug, pause backend until a debugger attaches
cmds:
- MESHCHAT_DEBUGPY=1 MESHCHAT_DEBUGPY_WAIT=1 bash scripts/dev-local.sh
dev-fe:
desc: Vite dev server only (pair with task run)
cmds:
@ -395,6 +405,11 @@ tasks:
- "{{.NPM}} exec vitest run --exclude tests/frontend/LoadTimePerformance.test.js --exclude tests/frontend/i18n.test.js"
- "{{.NPM}} exec vitest run --config vitest.electron.config.js"
test:fe:ui:
desc: Vitest UI runner for frontend unit tests
cmds:
- "{{.NPM}} run test:ui"
test:fe:loadtime:
desc: Optional frontend load-time / sidebar performance tests (not run in CI)
cmds:

View file

@ -114,11 +114,21 @@ Adjust the filename for your architecture.
```bash
task install
task dev
```
`task dev` starts the HTTPS backend on `127.0.0.1:8000` and Vite on [http://127.0.0.1:5173](http://127.0.0.1:5173). Open that Vite URL. The [Vue DevTools](https://devtools.vuejs.org/) overlay is injected for this serve only. `vite build` / `task run` never ship it (`__VUE_PROD_DEVTOOLS__` is false). Set `MESHCHAT_VUE_DEVTOOLS=0` to hide the overlay. Click a component in the inspector to open it in the editor (`LAUNCH_EDITOR`, default `code`).
Python breakpoints: `task debug` is the same stack with [debugpy](https://github.com/microsoft/debugpy) listening on `127.0.0.1:5678` (never `0.0.0.0`). Run **MeshChatX: Vite + Python** from the debugger, or start `task debug` and attach **Backend: Attach debugpy**. `task debug:wait` pauses the backend until that attach happens.
A production-like run without HMR:
```bash
pnpm run build-frontend
uv run python -m meshchatx.meshchat --headless --host 127.0.0.1
```
Useful task targets include `task format`, `task lint`, `task test`, and `task build`.
Useful task targets include `task format`, `task lint`, `task test`, `task test:fe:ui`, and `task build`.
## First launch

Binary file not shown.

View file

@ -39,3 +39,37 @@ export function isIgnorableServiceWorkerRegistrationError(error) {
export function serviceWorkerRegisterOptions() {
return { updateViaCache: "none" };
}
/**
* Vite HMR and Vue DevTools break if a leftover PWA worker intercepts modules.
* Register only for production-like browser loads (not Electron, not Vite DEV).
*
* @param {{ isDev?: boolean, isElectron?: boolean }} state
* @returns {boolean}
*/
export function shouldRegisterServiceWorker(state) {
return !state?.isDev && !state?.isElectron;
}
/**
* Drop existing registrations so a previous task run session cannot cache Vite.
*
* @param {{ getRegistrations?: () => Promise<Array<{ unregister?: () => Promise<boolean> }>> }} [serviceWorker]
* @returns {Promise<boolean[]>}
*/
export async function unregisterServiceWorkersIfPresent(serviceWorker) {
if (!serviceWorker || typeof serviceWorker.getRegistrations !== "function") {
return [];
}
const registrations = await serviceWorker.getRegistrations();
if (!Array.isArray(registrations) || registrations.length === 0) {
return [];
}
const results = [];
for (const registration of registrations) {
if (registration && typeof registration.unregister === "function") {
results.push(Boolean(await registration.unregister()));
}
}
return results;
}

View file

@ -25,6 +25,8 @@ import {
decideControllerChangeReload,
isIgnorableServiceWorkerRegistrationError,
serviceWorkerRegisterOptions,
shouldRegisterServiceWorker,
unregisterServiceWorkersIfPresent,
} from "./js/pwa/swClientRegister.js";
import "./js/HeapMonitor.js";
@ -442,7 +444,13 @@ if (networkReady) {
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
return;
}
if (ElectronUtils.isElectron()) {
if (
!shouldRegisterServiceWorker({
isDev: import.meta.env.DEV,
isElectron: ElectronUtils.isElectron(),
})
) {
void unregisterServiceWorkersIfPresent(navigator.serviceWorker);
return;
}
let refreshing = false;

View file

@ -114,11 +114,21 @@ Adjust the filename for your architecture.
```bash
task install
task dev
```
`task dev` starts the HTTPS backend on `127.0.0.1:8000` and Vite on [http://127.0.0.1:5173](http://127.0.0.1:5173). Open that Vite URL. The [Vue DevTools](https://devtools.vuejs.org/) overlay is injected for this serve only. `vite build` / `task run` never ship it (`__VUE_PROD_DEVTOOLS__` is false). Set `MESHCHAT_VUE_DEVTOOLS=0` to hide the overlay. Click a component in the inspector to open it in the editor (`LAUNCH_EDITOR`, default `code`).
Python breakpoints: `task debug` is the same stack with [debugpy](https://github.com/microsoft/debugpy) listening on `127.0.0.1:5678` (never `0.0.0.0`). Run **MeshChatX: Vite + Python** from the debugger, or start `task debug` and attach **Backend: Attach debugpy**. `task debug:wait` pauses the backend until that attach happens.
A production-like run without HMR:
```bash
pnpm run build-frontend
uv run python -m meshchatx.meshchat --headless --host 127.0.0.1
```
Useful task targets include `task format`, `task lint`, `task test`, and `task build`.
Useful task targets include `task format`, `task lint`, `task test`, `task test:fe:ui`, and `task build`.
## First launch

View file

@ -99,6 +99,7 @@
"terser": "^5.49.0",
"typescript": "^6.0.3",
"vite": "^8.1.5",
"vite-plugin-vue-devtools": "^8.2.1",
"vite-plugin-vuetify": "^2.1.3",
"vitest": "^4.1.10",
"vue-eslint-parser": "^10.4.1",

958
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -83,6 +83,7 @@ dev = [
"pytest-cov>=7.1.0",
"pytest-xdist>=3.8.0",
"ruff>=0.14.0",
"debugpy>=1.8.16",
"mutmut>=3.5.0; python_version < '4.0'",
"basedpyright>=1.39.9",
]

View file

@ -6,6 +6,7 @@ set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
export PYTHONUNBUFFERED="${PYTHONUNBUFFERED:-1}"
export MESHCHAT_PORT="${MESHCHAT_PORT:-8000}"
export E2E_BACKEND_PORT="$MESHCHAT_PORT"
@ -18,8 +19,30 @@ cleanup() {
}
trap cleanup EXIT INT TERM
uv run python -m meshchatx.meshchat --headless --host 127.0.0.1 --port "$MESHCHAT_PORT" &
BE_PID=$!
start_backend() {
local -a cmd
if [[ "${MESHCHAT_DEBUGPY:-0}" == "1" ]]; then
local dbg_port="${MESHCHAT_DEBUGPY_PORT:-5678}"
if ! uv run python -c "import debugpy" >/dev/null 2>&1; then
echo "[dev] debugpy is not installed. Run: task deps:backend" >&2
exit 1
fi
cmd=(uv run python -m debugpy --listen "127.0.0.1:${dbg_port}")
if [[ "${MESHCHAT_DEBUGPY_WAIT:-0}" == "1" ]]; then
cmd+=(--wait-for-client)
echo "[dev] debugpy waiting for attach on 127.0.0.1:${dbg_port}"
else
echo "[dev] debugpy listen 127.0.0.1:${dbg_port} (attach Backend: Attach debugpy)"
fi
cmd+=(-m meshchatx.meshchat --headless --host 127.0.0.1 --port "$MESHCHAT_PORT")
"${cmd[@]}" &
else
uv run python -m meshchatx.meshchat --headless --host 127.0.0.1 --port "$MESHCHAT_PORT" &
fi
BE_PID=$!
}
start_backend
BACKEND_URL="https://127.0.0.1:${MESHCHAT_PORT}/api/v1/status"
BACKEND_WAIT_SECS="${DEV_BACKEND_WAIT:-120}"
@ -74,4 +97,7 @@ VITE_HOST="${VITE_DEV_HOST:-127.0.0.1}"
VITE_PORT="${VITE_DEV_PORT:-5173}"
echo "[dev] Starting Vite at http://${VITE_HOST}:${VITE_PORT} (API proxy -> ${BACKEND_URL%/api/v1/status})"
if [[ "${MESHCHAT_VUE_DEVTOOLS:-1}" != "0" ]]; then
echo "[dev] Vue DevTools overlay is on. MESHCHAT_VUE_DEVTOOLS=0 disables it."
fi
pnpm run dev -- --host "$VITE_HOST" --port "$VITE_PORT"

View file

@ -62,6 +62,8 @@ if [[ "$ready" -ne 1 ]]; then
fi
echo "E2E: starting Vite on ${VITE_HOST}:${VITE_PORT}"
# Vue DevTools overlay intercepts clicks and is not part of the product UI.
export MESHCHAT_VUE_DEVTOOLS=0
pnpm exec vite --host "${VITE_HOST}" --port "${VITE_PORT}" &
VITE_PID=$!
wait "$VITE_PID"

54
scripts/vite-dx.mjs Normal file
View file

@ -0,0 +1,54 @@
// SPDX-License-Identifier: 0BSD
/**
* Vite-serve developer experience gates (Vue DevTools, open-in-editor).
* Production vite build must not load these plugins.
*/
/**
* @param {unknown} value
* @returns {boolean}
*/
export function envFlagEnabled(value) {
if (value === undefined || value === null || value === "") {
return false;
}
return ["1", "true", "yes"].includes(String(value).toLowerCase());
}
/**
* Vue DevTools overlay is Vite serve only. Vitest, vite build, and e2e
* (MESHCHAT_VUE_DEVTOOLS=0) stay off. Production bundles never include it.
*
* @param {{ command?: string, env?: NodeJS.ProcessEnv }} [options]
* @returns {boolean}
*/
export function isVueDevToolsEnabled(options = {}) {
const command = options.command;
const env = options.env || process.env;
if (command !== "serve") {
return false;
}
if (env.VITEST) {
return false;
}
const raw = env.MESHCHAT_VUE_DEVTOOLS;
if (raw !== undefined && raw !== "") {
return envFlagEnabled(raw);
}
return true;
}
/**
* Editor for Vue DevTools open-in-editor. LAUNCH_EDITOR wins, else code.
*
* @param {NodeJS.ProcessEnv} [env]
* @returns {string}
*/
export function detectLaunchEditor(env = process.env) {
const explicit = env.LAUNCH_EDITOR;
if (explicit !== undefined && String(explicit).trim() !== "") {
return String(explicit).trim();
}
return "code";
}

View file

@ -52,6 +52,8 @@ describe("boot and load smoothness", () => {
expect(main).toContain("ElectronUtils.isElectron()");
expect(main).toContain("serviceWorkerRegisterOptions");
expect(main).toContain("decideControllerChangeReload");
expect(main).toContain("shouldRegisterServiceWorker");
expect(main).toContain("import.meta.env.DEV");
});
it("App.vue fades non-keepAlive route swaps on canvas background", () => {

View file

@ -78,5 +78,8 @@ describe("service worker build", () => {
expect(main).toContain("controllerchange");
expect(main).toContain("registration.update()");
expect(main).toContain("visibilitychange");
expect(main).toContain("shouldRegisterServiceWorker");
expect(main).toContain("unregisterServiceWorkersIfPresent");
expect(main).toContain("import.meta.env.DEV");
});
});

View file

@ -20,6 +20,8 @@ import {
decideControllerChangeReload,
isIgnorableServiceWorkerRegistrationError,
serviceWorkerRegisterOptions,
shouldRegisterServiceWorker,
unregisterServiceWorkersIfPresent,
} from "../../meshchatx/src/frontend/js/pwa/swClientRegister.js";
import { makeRequest, MemoryCacheStorage, okResponse } from "./helpers/memoryCacheStorage.js";
@ -240,6 +242,41 @@ describe("swClientRegister update lifecycle oracle", () => {
it("oracle: register options force updateViaCache none", () => {
expect(serviceWorkerRegisterOptions()).toEqual({ updateViaCache: "none" });
});
it("oracle: service worker registers only outside Vite DEV and Electron", () => {
const cases = [
{ isDev: false, isElectron: false, expected: true },
{ isDev: true, isElectron: false, expected: false },
{ isDev: false, isElectron: true, expected: false },
{ isDev: true, isElectron: true, expected: false },
];
for (const input of cases) {
expect(shouldRegisterServiceWorker(input), JSON.stringify(input)).toBe(input.expected);
}
});
it("unregisters leftover workers when getRegistrations is present", async () => {
const unregistered = [];
const serviceWorker = {
getRegistrations: async () => [
{
unregister: async () => {
unregistered.push("a");
return true;
},
},
{
unregister: async () => {
unregistered.push("b");
return true;
},
},
],
};
await expect(unregisterServiceWorkersIfPresent(serviceWorker)).resolves.toEqual([true, true]);
expect(unregistered).toEqual(["a", "b"]);
await expect(unregisterServiceWorkersIfPresent({})).resolves.toEqual([]);
});
});
describe("swShellRuntime navigation preload", () => {

View file

@ -0,0 +1,86 @@
// SPDX-License-Identifier: 0BSD
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { detectLaunchEditor, envFlagEnabled, isVueDevToolsEnabled } from "../../scripts/vite-dx.mjs";
const ROOT = resolve(import.meta.dirname, "../..");
describe("vite-dx Vue DevTools gate", () => {
it("oracle: only vite serve enables DevTools by default", () => {
const cases = [
{ command: "serve", env: {}, expected: true },
{ command: "build", env: {}, expected: false },
{ command: "serve", env: { VITEST: "true" }, expected: false },
{ command: "serve", env: { MESHCHAT_VUE_DEVTOOLS: "0" }, expected: false },
{ command: "serve", env: { MESHCHAT_VUE_DEVTOOLS: "false" }, expected: false },
{ command: "serve", env: { MESHCHAT_VUE_DEVTOOLS: "1" }, expected: true },
{ command: "build", env: { MESHCHAT_VUE_DEVTOOLS: "1" }, expected: false },
{ command: undefined, env: {}, expected: false },
];
for (const input of cases) {
expect(isVueDevToolsEnabled({ command: input.command, env: input.env }), JSON.stringify(input)).toBe(
input.expected
);
}
});
it("envFlagEnabled accepts 1/true/yes only", () => {
expect(envFlagEnabled("1")).toBe(true);
expect(envFlagEnabled("true")).toBe(true);
expect(envFlagEnabled("YES")).toBe(true);
expect(envFlagEnabled("0")).toBe(false);
expect(envFlagEnabled("")).toBe(false);
expect(envFlagEnabled(undefined)).toBe(false);
});
it("detectLaunchEditor prefers LAUNCH_EDITOR then code", () => {
expect(detectLaunchEditor({ LAUNCH_EDITOR: "webstorm" })).toBe("webstorm");
expect(detectLaunchEditor({})).toBe("code");
});
it("vite.config.js wires the plugin, production DevTools flag, and localhost server", () => {
const vite = readFileSync(resolve(ROOT, "vite.config.js"), "utf8");
expect(vite).toContain('from "vite-plugin-vue-devtools"');
expect(vite).toContain("isVueDevToolsEnabled({ command })");
expect(vite).toContain('__VUE_PROD_DEVTOOLS__: "false"');
expect(vite).toContain("sourcemap: false");
expect(vite).toContain("devSourcemap: true");
expect(vite).toContain('host: "127.0.0.1"');
expect(vite).toContain("strictPort: true");
expect(vite).toContain("clearScreen: false");
});
it("e2e Vite stack disables Vue DevTools", () => {
const e2e = readFileSync(resolve(ROOT, "scripts/e2e/start-e2e-stack.sh"), "utf8");
expect(e2e).toContain("MESHCHAT_VUE_DEVTOOLS=0");
});
it("package.json lists vite-plugin-vue-devtools as a devDependency", () => {
const pkg = JSON.parse(readFileSync(resolve(ROOT, "package.json"), "utf8"));
expect(pkg.devDependencies["vite-plugin-vue-devtools"]).toBeTruthy();
});
it("debugpy in task debug binds 127.0.0.1 only", () => {
const dev = readFileSync(resolve(ROOT, "scripts/dev-local.sh"), "utf8");
expect(dev).toContain('debugpy --listen "127.0.0.1:${dbg_port}"');
expect(dev).not.toContain("--listen 0.0.0.0");
expect(dev).toContain("MESHCHAT_DEBUGPY");
const taskfile = readFileSync(resolve(ROOT, "Taskfile.yml"), "utf8");
expect(taskfile).toContain("MESHCHAT_DEBUGPY=1 bash scripts/dev-local.sh");
});
it("debugger launch configs attach debugpy on localhost and Chrome to Vite", () => {
const launch = JSON.parse(readFileSync(resolve(ROOT, ".vscode/launch.json"), "utf8"));
const names = launch.configurations.map((c) => c.name);
expect(names).toContain("Backend: Attach debugpy");
expect(names).toContain("Frontend: Chrome (Vite)");
const attach = launch.configurations.find((c) => c.name === "Backend: Attach debugpy");
expect(attach.connect).toEqual({ host: "127.0.0.1", port: 5678 });
const chrome = launch.configurations.find((c) => c.name === "Frontend: Chrome (Vite)");
expect(chrome.url).toBe("http://127.0.0.1:5173");
const compound = launch.compounds.find((c) => c.name === "MeshChatX: Vite + Python");
expect(compound.preLaunchTask).toBe("meshchatx: debug");
});
});

27
uv.lock generated
View file

@ -749,6 +749,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/8f/77135ab8d690030cdb0ebeca879640b5945c4cbf5344ecbc507b4628da24/dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7be4271e38251f1ad726962dec60da887c8ed352d157352e4fc27f56aece5c5d", size = 1629160, upload-time = "2026-06-05T18:56:47.688Z" },
]
[[package]]
name = "debugpy"
version = "1.8.21"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f2/aa/12037145b7a56eaa5b29b41872f7a21b538e807e13f32c4d3c46e59be084/debugpy-1.8.21.tar.gz", hash = "sha256:a3c53278e84c94e11bd87c53970ec391d1a67396c8b22609fcac576520e611a6", size = 1697577, upload-time = "2026-06-01T19:30:35.156Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/fb/cbf306d6e07a313a91e7171a98669054502840931432c227cfd505ee367f/debugpy-1.8.21-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:da456226c7b4c69e35dbe35dcee6623d912000a77816db7856a41af1c72a0264", size = 2203120, upload-time = "2026-06-01T19:30:43.964Z" },
{ url = "https://files.pythonhosted.org/packages/aa/57/aa739bd4ad2cbf96aeb1b20b56918ddd5ae4c28b68709bfcd327f02123ee/debugpy-1.8.21-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:f68b891688e61bdc08b8d364d919ff0051e0b94657b39dcd027bc3173edb7cdc", size = 3059958, upload-time = "2026-06-01T19:30:45.622Z" },
{ url = "https://files.pythonhosted.org/packages/a8/31/453d2c9a23d133fe2c8ec7ca1d816ded52a913487fe3ffef7c01b4b706af/debugpy-1.8.21-cp311-cp311-win32.whl", hash = "sha256:f843a8b08c2edeaf9b1582eed4f25441af21a297c22ff16bf76a662557aa9c9e", size = 5236515, upload-time = "2026-06-01T19:30:47.461Z" },
{ url = "https://files.pythonhosted.org/packages/60/94/6660de2f2d7bf388f229335ba4637646eebabdbf38564cb439a95a9193c9/debugpy-1.8.21-cp311-cp311-win_amd64.whl", hash = "sha256:84c564d8cc701d41843b29a92814c1f1bef6798724ca9d675c284ad9f6a547d7", size = 5256138, upload-time = "2026-06-01T19:30:49.113Z" },
{ url = "https://files.pythonhosted.org/packages/a2/df/bf625547431a9cadc9f4cbfeda38866e2b17f6aed147b625377e87834449/debugpy-1.8.21-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:9f96713896f39c3dff0ee841f47320c3f2983d33c341e009361bb0ebc79adc4e", size = 2483609, upload-time = "2026-06-01T19:30:50.794Z" },
{ url = "https://files.pythonhosted.org/packages/bf/09/59324b903599031ff9faaec1758292409f6561a0ec2492fe4b703327705a/debugpy-1.8.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:c193d474f0a211191f2b4449d2d06157c689013035bd952f3b617e0ef422b176", size = 3968900, upload-time = "2026-06-01T19:30:52.341Z" },
{ url = "https://files.pythonhosted.org/packages/14/cd/27f65b805d7fe005c44e1a36b9183ecdfbcdbf9d3e721a5115d461ecc7ee/debugpy-1.8.21-cp312-cp312-win32.whl", hash = "sha256:4743373c1cac7f9e74a1b9915bf1dbe0e900eca657ffb170ae07ac8363205ae9", size = 5336340, upload-time = "2026-06-01T19:30:54.047Z" },
{ url = "https://files.pythonhosted.org/packages/77/1d/c84e30c0c674184948b66f076ab271c01d940618a2824c23cd035a27bc20/debugpy-1.8.21-cp312-cp312-win_amd64.whl", hash = "sha256:bd7ba9dd3daa7c2f942c6ca8d4695a16bf9ac16b63615261c7982bc74f7ed20c", size = 5374751, upload-time = "2026-06-01T19:30:55.891Z" },
{ url = "https://files.pythonhosted.org/packages/77/6b/d817e1f8cc77aa055d37fba092e0febfdff40fe652d8d53d4cd7a86ad98d/debugpy-1.8.21-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:13678151fc401e2d68c9880b91e28714f797d40422994572b24560ef80910a88", size = 2477398, upload-time = "2026-06-01T19:30:57.644Z" },
{ url = "https://files.pythonhosted.org/packages/48/57/412421516afc3055fa577516f00beec3d663f9b0ab330639547ae6c57720/debugpy-1.8.21-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:ecbd158386c31ffe71d46f72d44d56e66331ab9b16cad649156d514368f23ab2", size = 3962096, upload-time = "2026-06-01T19:30:59.235Z" },
{ url = "https://files.pythonhosted.org/packages/c1/62/2c616337cf6ba7b07ebbc97f02c6c945a8e2f76b365e33ee809c32ee36d1/debugpy-1.8.21-cp313-cp313-win32.whl", hash = "sha256:2c2ae706dec41d99a9ca1f7ebc987a83e65578363be6f6b3ac9067504917fae1", size = 5336288, upload-time = "2026-06-01T19:31:00.79Z" },
{ url = "https://files.pythonhosted.org/packages/f8/99/9175103392f84c4b1bf7622888cdc68da07f0ff7d9e581266428f6776033/debugpy-1.8.21-cp313-cp313-win_amd64.whl", hash = "sha256:aa648733047443eb1d07682c4ef287d36a54507b643ffdf38b09a3ef002c72a0", size = 5376567, upload-time = "2026-06-01T19:31:02.56Z" },
{ url = "https://files.pythonhosted.org/packages/ce/3d/f4bbb323a548bfab2af3d6b4ffd9bf22636e55956a1285d317a1de643aad/debugpy-1.8.21-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9bb2a685287a2ac9b181cde89edcec64845cb51de7faaa75badb9a698bc24782", size = 2477209, upload-time = "2026-06-01T19:31:04.157Z" },
{ url = "https://files.pythonhosted.org/packages/8c/2d/6e7ec524984a1702777868de49a4c53202bddac2a432a76a093469587750/debugpy-1.8.21-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:3d6922439bf33fd38a3e2c447869ebc7b97da5cd3d329ff1ef9bc06c4903437e", size = 3927115, upload-time = "2026-06-01T19:31:05.863Z" },
{ url = "https://files.pythonhosted.org/packages/97/47/d1aa6d64005a98a9144647d99306b419396f9ad7bf1d73c119e17a81fb4d/debugpy-1.8.21-cp314-cp314-win32.whl", hash = "sha256:15d4963bd5ffa48f0da0947fd06757fa7621945048a14ad7705431566d3c0e7c", size = 5336724, upload-time = "2026-06-01T19:31:07.711Z" },
{ url = "https://files.pythonhosted.org/packages/5f/67/b905b90d163af11878c1af8abafa4a25206335e112e284e413454543a6da/debugpy-1.8.21-cp314-cp314-win_amd64.whl", hash = "sha256:fe0744a12353406de0ae8ccff0d0a4a666f00801a3db8fd04e7a5f761cd520e8", size = 5373803, upload-time = "2026-06-01T19:31:09.469Z" },
{ url = "https://files.pythonhosted.org/packages/95/51/67e7cf11a53e40694f720457d5b3a1cdaaa3d5a9a633e482f225456b93ff/debugpy-1.8.21-py2.py3-none-any.whl", hash = "sha256:b1e37d333663c8851516a47364ef473da127f9caebe4417e6df6f5825a7e9a92", size = 5352888, upload-time = "2026-06-01T19:31:25.186Z" },
]
[[package]]
name = "dmgbuild"
version = "1.6.7"
@ -2040,6 +2065,7 @@ dependencies = [
dev = [
{ name = "basedpyright" },
{ name = "cx-freeze" },
{ name = "debugpy" },
{ name = "hypothesis" },
{ name = "jsonschema" },
{ name = "mutmut", marker = "python_full_version < '4'" },
@ -2078,6 +2104,7 @@ requires-dist = [
dev = [
{ name = "basedpyright", specifier = ">=1.39.9" },
{ name = "cx-freeze", specifier = ">=7.0.0" },
{ name = "debugpy", specifier = ">=1.8.16" },
{ name = "hypothesis", specifier = ">=6.152.4" },
{ name = "jsonschema", specifier = ">=4.26.0" },
{ name = "mutmut", marker = "python_full_version < '4'", specifier = ">=3.5.0" },

View file

@ -3,8 +3,10 @@ import fs from "fs";
import { defineConfig } from "vite";
import { MICRON_PARSER_GO_RELEASE_TAG } from "./scripts/micron-parser-go-version.mjs";
import { meshchatxServiceWorkerPlugin } from "./scripts/build/generate_service_worker.mjs";
import { detectLaunchEditor, isVueDevToolsEnabled } from "./scripts/vite-dx.mjs";
import tailwindcss from "@tailwindcss/vite";
import vue from "@vitejs/plugin-vue";
import vueDevTools from "vite-plugin-vue-devtools";
import vuetify from "vite-plugin-vuetify";
const vendorChunkGroups = [
@ -153,9 +155,10 @@ function loadVisualiserWasmIntegrity() {
const micronWasmIntegrity = loadMicronWasmIntegrity();
const visualiserWasmIntegrity = loadVisualiserWasmIntegrity();
export default defineConfig({
export default defineConfig(({ command }) => ({
define: {
__APP_BUILD_TIME__: JSON.stringify(appBuildTimeIso),
__VUE_PROD_DEVTOOLS__: "false",
"import.meta.env.VITE_MICRON_WASM_BUNDLED": JSON.stringify(micronWasmBundled ? "true" : "false"),
"import.meta.env.VITE_MICRON_PARSER_GO_RELEASE": JSON.stringify(MICRON_PARSER_GO_RELEASE_TAG),
"import.meta.env.VITE_VISUALISER_WASM_BUNDLED": JSON.stringify(visualiserWasmBundled ? "true" : "false"),
@ -166,6 +169,13 @@ export default defineConfig({
},
plugins: [
tailwindcss(),
...(isVueDevToolsEnabled({ command })
? [
vueDevTools({
launchEditor: detectLaunchEditor(),
}),
]
: []),
vue({
template: {
compilerOptions: {
@ -177,8 +187,18 @@ export default defineConfig({
meshchatxServiceWorkerPlugin({ buildId: appBuildTimeIso }),
],
css: {
devSourcemap: true,
},
server: {
host: "127.0.0.1",
port: 5173,
strictPort: true,
clearScreen: false,
warmup: {
clientFiles: ["./main.js", "./components/App.vue", "./components/messages/MessagesPage.vue"],
},
proxy: {
"/api": {
target: e2eBackendOrigin,
@ -279,4 +299,4 @@ export default defineConfig({
"micron-parser": path.join(__dirname, "node_modules", "micron-parser", "js", "micron-parser.js"),
},
},
});
}));