fix(desktop): correct packaged shell assets

This commit is contained in:
Sparsh 2026-04-20 15:26:34 +05:30
parent 21fb1b9a0c
commit c5238debf6
6 changed files with 20 additions and 484 deletions

View file

@ -7,11 +7,12 @@ on:
- gitnexus-desktop/**
workflow_dispatch:
permissions: {}
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
cancel-in-progress: true
jobs:
package:
@ -75,4 +76,4 @@ jobs:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_paths }}
if-no-files-found: error
retention-days: 14
retention-days: 14

102
CLAUDE.md
View file

@ -51,104 +51,4 @@ If always-on instructions grow, load deep conventions via conditional reads (e.g
## GitNexus rules
See the `<!-- gitnexus:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **gitnexus** (18208 symbols, 25369 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
## Always Do
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
## When Debugging
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
3. `READ gitnexus://repo/gitnexus/process/{processName}` — trace the full execution flow step by step
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
## When Refactoring
- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`.
- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code.
- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed.
## Never Do
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
## Tools Quick Reference
| Tool | When to use | Command |
|------|-------------|---------|
| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` |
| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` |
| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` |
| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` |
| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` |
| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` |
## Impact Risk Levels
| Depth | Meaning | Action |
|-------|---------|--------|
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
## Resources
| Resource | Use for |
|----------|---------|
| `gitnexus://repo/gitnexus/context` | Codebase overview, check index freshness |
| `gitnexus://repo/gitnexus/clusters` | All functional areas |
| `gitnexus://repo/gitnexus/processes` | All execution flows |
| `gitnexus://repo/gitnexus/process/{name}` | Step-by-step execution trace |
## Self-Check Before Finishing
Before completing any code modification task, verify:
1. `gitnexus_impact` was run for all modified symbols
2. No HIGH/CRITICAL risk warnings were ignored
3. `gitnexus_detect_changes()` confirms changes match expected scope
4. All d=1 (WILL BREAK) dependents were updated
## Keeping the Index Fresh
After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
```bash
npx gitnexus analyze
```
If the index previously included embeddings, preserve them by adding `--embeddings`:
```bash
npx gitnexus analyze --embeddings
```
To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.**
> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`.
## CLI
| Task | Read this skill file |
|------|---------------------|
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |
<!-- gitnexus:end -->` block in **[AGENTS.md](AGENTS.md)** for the canonical MCP tools, impact analysis rules, and index instructions.
See the canonical `gitnexus:start``gitnexus:end` block in [AGENTS.md](AGENTS.md).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -430,7 +430,20 @@ const sendStaticResponse = (assetPath: string | null, response: ServerResponse):
const handlePackagedWebRequest = (request: IncomingMessage, response: ServerResponse): void => {
const requestUrl = request.url ?? '/';
const requestedPath = decodeURIComponent(new URL(requestUrl, 'http://127.0.0.1').pathname);
let requestedPath: string;
try {
requestedPath = decodeURIComponent(new URL(requestUrl, 'http://127.0.0.1').pathname);
} catch (error) {
if (error instanceof URIError) {
response.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
response.end('Bad request');
return;
}
throw error;
}
sendStaticResponse(normalizeStaticPath(GITNEXUS_WEB_PACKAGED_DIR, requestedPath), response);
};
@ -611,7 +624,7 @@ const loadShellRenderer = async (window: BrowserWindow): Promise<void> => {
return;
}
const rendererEntry = path.join(__dirname, '../renderer/src/renderer/index.html');
const rendererEntry = path.join(__dirname, '../renderer/index.html');
console.info(`[gitnexus-desktop] Loading shell renderer file: ${rendererEntry}`);
await window.loadFile(rendererEntry);
};

View file

@ -1,356 +0,0 @@
import { useEffect, useState } from 'react';
type DesktopShellState = {
appName: string;
isAlwaysOnTop: boolean;
isMaximized: boolean;
platform: string;
titleBarHeight: number;
};
type DesktopApi = {
appName: string;
close: () => Promise<void>;
getShellState: () => Promise<DesktopShellState>;
minimize: () => Promise<DesktopShellState | null>;
onWindowStateChanged: (callback: (state: DesktopShellState) => void) => () => void;
platform: string;
titleBarHeight: number;
toggleAlwaysOnTop: () => Promise<DesktopShellState | null>;
toggleMaximize: () => Promise<DesktopShellState | null>;
};
const defaultDesktopState: DesktopShellState = {
appName: 'GitNexus Desktop',
isAlwaysOnTop: false,
isMaximized: false,
platform: 'win32',
titleBarHeight: 38,
};
const fallbackDesktopApi: DesktopApi = {
appName: defaultDesktopState.appName,
close: async () => {},
getShellState: async () => defaultDesktopState,
minimize: async () => defaultDesktopState,
onWindowStateChanged: () => () => {},
platform: defaultDesktopState.platform,
titleBarHeight: defaultDesktopState.titleBarHeight,
toggleAlwaysOnTop: async () => defaultDesktopState,
toggleMaximize: async () => defaultDesktopState,
};
const desktopApi =
(window as Window & { gitnexusDesktop?: DesktopApi }).gitnexusDesktop ?? fallbackDesktopApi;
const styles = `
:root {
color-scheme: dark;
}
.desktop-shell {
--titlebar-height: 38px;
height: 100vh;
display: grid;
grid-template-rows: var(--titlebar-height) 1fr;
background: #0d0d0d;
color: #f4f4f5;
overflow: hidden;
}
.desktop-shell__titlebar {
display: grid;
grid-template-columns: minmax(144px, 1fr) auto minmax(144px, 1fr);
align-items: center;
gap: 12px;
height: var(--titlebar-height);
padding: 0 10px;
background: #0d0d0d;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
-webkit-app-region: drag;
user-select: none;
}
.desktop-shell__side {
display: flex;
align-items: center;
min-width: 144px;
min-height: 100%;
}
.desktop-shell__side--end {
justify-content: flex-end;
}
.desktop-shell__title {
justify-self: center;
font-family: 'Segoe UI Variable', 'Segoe UI', 'SF Pro Text', system-ui, sans-serif;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.06em;
color: rgba(244, 244, 245, 0.9);
white-space: nowrap;
}
.desktop-shell__spacer {
min-width: 118px;
min-height: 1px;
}
.desktop-shell__content {
background: #0d0d0d;
}
.desktop-shell__content::after {
content: '';
display: block;
width: 100%;
height: 100%;
background: #0d0d0d;
}
.window-controls,
.traffic-lights {
display: flex;
align-items: center;
gap: 0;
-webkit-app-region: no-drag;
}
.window-control {
width: 46px;
height: calc(var(--titlebar-height) - 8px);
border: 0;
border-radius: 8px;
background: transparent;
color: rgba(244, 244, 245, 0.88);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 120ms ease, color 120ms ease;
}
.window-control:hover {
background: rgba(255, 255, 255, 0.08);
}
.window-control--close:hover {
background: #c42b1c;
color: #ffffff;
}
.window-control svg {
width: 10px;
height: 10px;
stroke: currentColor;
stroke-width: 1.2;
fill: none;
vector-effect: non-scaling-stroke;
}
.traffic-lights {
gap: 8px;
}
.traffic-light {
width: 12px;
height: 12px;
padding: 0;
border: 0;
border-radius: 999px;
cursor: pointer;
transition: filter 120ms ease, transform 120ms ease;
-webkit-app-region: no-drag;
}
.traffic-light:hover {
filter: brightness(1.04);
}
.traffic-light--close {
background: #ff5f57;
}
.traffic-light--minimize {
background: #febc2e;
}
.traffic-light--maximize {
background: #28c840;
}
@media (max-width: 720px) {
.desktop-shell__titlebar {
grid-template-columns: minmax(116px, 1fr) auto minmax(116px, 1fr);
gap: 8px;
padding: 0 8px;
}
.desktop-shell__side {
min-width: 116px;
}
.window-control {
width: 42px;
}
}
`;
function MinimizeIcon() {
return (
<svg viewBox="0 0 10 10" aria-hidden="true">
<path d="M2 5h6" />
</svg>
);
}
function MaximizeIcon({ isMaximized }: { isMaximized: boolean }) {
if (isMaximized) {
return (
<svg viewBox="0 0 10 10" aria-hidden="true">
<path d="M2.25 3.25h4.5v4.5h-4.5z" />
<path d="M3.25 2.25h4.5v4.5" />
</svg>
);
}
return (
<svg viewBox="0 0 10 10" aria-hidden="true">
<path d="M2.25 2.25h5.5v5.5h-5.5z" />
</svg>
);
}
function CloseIcon() {
return (
<svg viewBox="0 0 10 10" aria-hidden="true">
<path d="M2 2l6 6" />
<path d="M8 2L2 8" />
</svg>
);
}
export default function App() {
const [shellState, setShellState] = useState<DesktopShellState>({
...defaultDesktopState,
appName: desktopApi.appName,
platform: desktopApi.platform,
titleBarHeight: desktopApi.titleBarHeight,
});
useEffect(() => {
let cancelled = false;
void desktopApi.getShellState().then((state) => {
if (!cancelled && state) {
setShellState(state);
}
});
const unsubscribe = desktopApi.onWindowStateChanged((state) => {
if (!cancelled) {
setShellState(state);
}
});
return () => {
cancelled = true;
unsubscribe();
};
}, []);
const isMac = shellState.platform === 'darwin';
const handleMinimize = async (): Promise<void> => {
const nextState = await desktopApi.minimize();
if (nextState) {
setShellState(nextState);
}
};
const handleToggleMaximize = async (): Promise<void> => {
const nextState = await desktopApi.toggleMaximize();
if (nextState) {
setShellState(nextState);
}
};
const handleClose = async (): Promise<void> => {
await desktopApi.close();
};
return (
<>
<style>{styles}</style>
<div className="desktop-shell">
<header className="desktop-shell__titlebar">
<div className="desktop-shell__side">
{isMac ? (
<div className="traffic-lights" aria-label="Window controls">
<button
type="button"
className="traffic-light traffic-light--close"
aria-label="Close window"
onClick={handleClose}
/>
<button
type="button"
className="traffic-light traffic-light--minimize"
aria-label="Minimize window"
onClick={handleMinimize}
/>
<button
type="button"
className="traffic-light traffic-light--maximize"
aria-label={shellState.isMaximized ? 'Restore window' : 'Maximize window'}
onClick={handleToggleMaximize}
/>
</div>
) : (
<div className="desktop-shell__spacer" aria-hidden="true" />
)}
</div>
<div className="desktop-shell__title">{shellState.appName}</div>
<div className="desktop-shell__side desktop-shell__side--end">
{isMac ? (
<div className="desktop-shell__spacer" aria-hidden="true" />
) : (
<div className="window-controls" aria-label="Window controls">
<button
type="button"
className="window-control"
aria-label="Minimize window"
onClick={handleMinimize}
>
<MinimizeIcon />
</button>
<button
type="button"
className="window-control"
aria-label={shellState.isMaximized ? 'Restore window' : 'Maximize window'}
onClick={handleToggleMaximize}
>
<MaximizeIcon isMaximized={shellState.isMaximized} />
</button>
<button
type="button"
className="window-control window-control--close"
aria-label="Close window"
onClick={handleClose}
>
<CloseIcon />
</button>
</div>
)}
</div>
</header>
<main className="desktop-shell__content" aria-hidden="true" />
</div>
</>
);
}

View file

@ -1,22 +0,0 @@
html,
body,
#root {
margin: 0;
min-height: 100%;
}
body {
min-height: 100vh;
font-family: 'Segoe UI', sans-serif;
background: #101114;
color: #f5f7fb;
}
.app-shell {
min-height: 100vh;
display: grid;
place-items: center;
font-size: 2rem;
font-weight: 600;
letter-spacing: 0.04em;
}