chore: rebrand client and add icon tooling
This commit is contained in:
24
BUILD.md
24
BUILD.md
@@ -1,6 +1,6 @@
|
|||||||
# Building OpenCode Client Binaries
|
# Building CodeNomad Binaries
|
||||||
|
|
||||||
This guide explains how to build distributable binaries for OpenCode Client.
|
This guide explains how to build distributable binaries for CodeNomad.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -81,17 +81,17 @@ Binaries are generated in the `release/` directory:
|
|||||||
|
|
||||||
```
|
```
|
||||||
release/
|
release/
|
||||||
├── OpenCode Client-0.1.0-mac-universal.dmg
|
├── CodeNomad-0.1.0-mac-universal.dmg
|
||||||
├── OpenCode Client-0.1.0-mac-universal.zip
|
├── CodeNomad-0.1.0-mac-universal.zip
|
||||||
├── OpenCode Client-0.1.0-win-x64.exe
|
├── CodeNomad-0.1.0-win-x64.exe
|
||||||
├── OpenCode Client-0.1.0-linux-x64.AppImage
|
├── CodeNomad-0.1.0-linux-x64.AppImage
|
||||||
└── ...
|
└── ...
|
||||||
```
|
```
|
||||||
|
|
||||||
## File Naming Convention
|
## File Naming Convention
|
||||||
|
|
||||||
```
|
```
|
||||||
OpenCode Client-{version}-{os}-{arch}.{ext}
|
CodeNomad-{version}-{os}-{arch}.{ext}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **version**: From package.json (e.g., `0.1.0`)
|
- **version**: From package.json (e.g., `0.1.0`)
|
||||||
@@ -215,6 +215,16 @@ Edit `package.json` → `build` section to customize:
|
|||||||
|
|
||||||
See [electron-builder docs](https://www.electron.build/) for details.
|
See [electron-builder docs](https://www.electron.build/) for details.
|
||||||
|
|
||||||
|
## Brand Assets
|
||||||
|
|
||||||
|
- `images/CodeNomad-Icon.png` — primary asset for in-app logo placements and the 1024×1024 master icon used to generate packaged app icons
|
||||||
|
|
||||||
|
To update the binaries:
|
||||||
|
|
||||||
|
1. Run `node scripts/generate-icons.js images/CodeNomad-Icon.png electron/resources` to round the corners and emit fresh `icon.icns`, `icon.ico`, and `icon.png` files.
|
||||||
|
2. (Optional) Pass `--radius` to tweak the corner curvature or `--name` to change the filename prefix.
|
||||||
|
3. If you prefer manual control, export `images/CodeNomad-Icon.png` with your tool of choice and place the generated files in `electron/resources/`.
|
||||||
|
|
||||||
## Clean Build
|
## Clean Build
|
||||||
|
|
||||||
Remove previous builds:
|
Remove previous builds:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# OpenCode Client - Development Progress
|
# CodeNomad - Development Progress
|
||||||
|
|
||||||
## Completed Tasks
|
## Completed Tasks
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# OpenCode Client
|
# CodeNomad
|
||||||
|
|
||||||
A cross-platform desktop application for interacting with OpenCode servers, built with Electron and SolidJS.
|
A cross-platform desktop application for interacting with OpenCode servers, built with Electron and SolidJS.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
OpenCode Client provides a multi-instance, multi-session interface for working with AI-powered coding assistants. It manages OpenCode server processes, handles real-time message streaming, and provides an intuitive UI for coding with AI.
|
CodeNomad provides a multi-instance, multi-session interface for working with AI-powered coding assistants. It manages OpenCode server processes, handles real-time message streaming, and provides an intuitive UI for coding with AI.
|
||||||
|
|
||||||
**🎯 MVP Focus:** This project prioritizes functionality over performance. Performance optimization is intentionally deferred to post-MVP phases. See [docs/MVP-PRINCIPLES.md](docs/MVP-PRINCIPLES.md) for details.
|
**🎯 MVP Focus:** This project prioritizes functionality over performance. Performance optimization is intentionally deferred to post-MVP phases. See [docs/MVP-PRINCIPLES.md](docs/MVP-PRINCIPLES.md) for details.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Tool Call Rendering Implementation
|
# Tool Call Rendering Implementation
|
||||||
|
|
||||||
This document describes how tool calls are rendered in the OpenCode Client, following the patterns established in the TUI.
|
This document describes how tool calls are rendered in the CodeNomad, following the patterns established in the TUI.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ When the time comes:
|
|||||||
|
|
||||||
> **Make it work, then make it better, then make it fast.**
|
> **Make it work, then make it better, then make it fast.**
|
||||||
|
|
||||||
For OpenCode Client MVP:
|
For CodeNomad MVP:
|
||||||
|
|
||||||
- **Phase 1-7:** Make it work, make it better
|
- **Phase 1-7:** Make it work, make it better
|
||||||
- **Phase 8+:** Make it fast
|
- **Phase 8+:** Make it fast
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# OpenCode Client - Project Summary
|
# CodeNomad - Project Summary
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ We have completed the MVP milestones (Phases 1-3) and are now operating in post-
|
|||||||
|
|
||||||
## What We've Created
|
## What We've Created
|
||||||
|
|
||||||
A comprehensive specification and task breakdown for building the OpenCode Client desktop application.
|
A comprehensive specification and task breakdown for building the CodeNomad desktop application.
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# OpenCode Client Architecture
|
# CodeNomad Architecture
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
OpenCode Client is a cross-platform desktop application built with Electron that provides a multi-instance, multi-session interface for interacting with OpenCode servers. Each instance manages its own OpenCode server process and can handle multiple concurrent sessions.
|
CodeNomad is a cross-platform desktop application built with Electron that provides a multi-instance, multi-session interface for interacting with OpenCode servers. Each instance manages its own OpenCode server process and can handle multiple concurrent sessions.
|
||||||
|
|
||||||
## High-Level Architecture
|
## High-Level Architecture
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# OpenCode Client Build Roadmap
|
# CodeNomad Build Roadmap
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This document outlines the phased approach to building the OpenCode Client desktop application. Each phase builds incrementally on the previous, with clear deliverables and milestones.
|
This document outlines the phased approach to building the CodeNomad desktop application. Each phase builds incrementally on the previous, with clear deliverables and milestones.
|
||||||
|
|
||||||
**Status:** MVP (Phases 1-3) is complete. Focus now shifts to post-MVP phases starting with multi-instance support and advanced input refinements.
|
**Status:** MVP (Phases 1-3) is complete. Focus now shifts to post-MVP phases starting with multi-instance support and advanced input refinements.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The OpenCode Client interface consists of a two-level tabbed layout with instance tabs at the top and session tabs below. Each session displays a message stream and prompt input.
|
The CodeNomad interface consists of a two-level tabbed layout with instance tabs at the top and session tabs below. Each session displays a message stream and prompt input.
|
||||||
|
|
||||||
## Layout Structure
|
## Layout Structure
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ Appears when instance starts:
|
|||||||
│ │
|
│ │
|
||||||
│ [Folder Icon] │
|
│ [Folder Icon] │
|
||||||
│ │
|
│ │
|
||||||
│ Welcome to OpenCode Client │
|
│ Start Coding with AI │
|
||||||
│ │
|
│ │
|
||||||
│ Select a folder to start coding with AI │
|
│ Select a folder to start coding with AI │
|
||||||
│ │
|
│ │
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { app, BrowserWindow, dialog, ipcMain, nativeTheme, session } from "electron"
|
import { app, BrowserWindow, dialog, ipcMain, nativeImage, nativeTheme, session } from "electron"
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import { createApplicationMenu } from "./menu"
|
import { createApplicationMenu } from "./menu"
|
||||||
import { setupInstanceIPC } from "./ipc"
|
import { setupInstanceIPC } from "./ipc"
|
||||||
@@ -15,9 +15,18 @@ setupStorageIPC()
|
|||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null
|
let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
|
function getIconPath() {
|
||||||
|
if (app.isPackaged) {
|
||||||
|
return join(process.resourcesPath, "icon.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(app.getAppPath(), "electron/resources/icon.png")
|
||||||
|
}
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
const prefersDark = true //nativeTheme.shouldUseDarkColors
|
const prefersDark = true //nativeTheme.shouldUseDarkColors
|
||||||
const backgroundColor = prefersDark ? "#1a1a1a" : "#ffffff"
|
const backgroundColor = prefersDark ? "#1a1a1a" : "#ffffff"
|
||||||
|
const iconPath = getIconPath()
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 1400,
|
width: 1400,
|
||||||
@@ -25,6 +34,7 @@ function createWindow() {
|
|||||||
minWidth: 800,
|
minWidth: 800,
|
||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
|
icon: iconPath,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: join(__dirname, "../preload/index.js"),
|
preload: join(__dirname, "../preload/index.js"),
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
@@ -65,6 +75,13 @@ app.whenReady().then(() => {
|
|||||||
app.on("browser-window-created", (_, window) => {
|
app.on("browser-window-created", (_, window) => {
|
||||||
window.webContents.session.setSpellCheckerEnabled(false)
|
window.webContents.session.setSpellCheckerEnabled(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (app.dock) {
|
||||||
|
const dockIcon = nativeImage.createFromPath(getIconPath())
|
||||||
|
if (!dockIcon.isEmpty()) {
|
||||||
|
app.dock.setIcon(dockIcon)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[spellcheck] default session enabled:", session.defaultSession.isSpellCheckerEnabled())
|
console.log("[spellcheck] default session enabled:", session.defaultSession.isSpellCheckerEnabled())
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export function createApplicationMenu(mainWindow: BrowserWindow) {
|
|||||||
...(isMac
|
...(isMac
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
label: "OpenCode Client",
|
label: "CodeNomad",
|
||||||
submenu: [
|
submenu: [
|
||||||
{ role: "about" as const },
|
{ role: "about" as const },
|
||||||
{ type: "separator" as const },
|
{ type: "separator" as const },
|
||||||
|
|||||||
BIN
electron/resources/icon.icns
Normal file
BIN
electron/resources/icon.icns
Normal file
Binary file not shown.
BIN
electron/resources/icon.ico
Normal file
BIN
electron/resources/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 422 KiB |
BIN
electron/resources/icon.png
Normal file
BIN
electron/resources/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
images/CodeNomad-Icon-original.png
Normal file
BIN
images/CodeNomad-Icon-original.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
images/CodeNomad-Icon.png
Normal file
BIN
images/CodeNomad-Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
85
package-lock.json
generated
85
package-lock.json
generated
@@ -26,6 +26,8 @@
|
|||||||
"electron": "39.0.0",
|
"electron": "39.0.0",
|
||||||
"electron-builder": "^24.0.0",
|
"electron-builder": "^24.0.0",
|
||||||
"electron-vite": "4.0.1",
|
"electron-vite": "4.0.1",
|
||||||
|
"png2icons": "^2.0.1",
|
||||||
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "8.5.6",
|
"postcss": "8.5.6",
|
||||||
"tailwindcss": "3",
|
"tailwindcss": "3",
|
||||||
"typescript": "^5.3.0",
|
"typescript": "^5.3.0",
|
||||||
@@ -77,7 +79,6 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -2288,7 +2289,6 @@
|
|||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -2464,6 +2464,7 @@
|
|||||||
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
|
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver-utils": "^2.1.0",
|
"archiver-utils": "^2.1.0",
|
||||||
"async": "^3.2.4",
|
"async": "^3.2.4",
|
||||||
@@ -2483,6 +2484,7 @@
|
|||||||
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.4",
|
"glob": "^7.1.4",
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
@@ -2505,6 +2507,7 @@
|
|||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
"inherits": "~2.0.3",
|
"inherits": "~2.0.3",
|
||||||
@@ -2520,7 +2523,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/archiver-utils/node_modules/string_decoder": {
|
"node_modules/archiver-utils/node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
@@ -2528,6 +2532,7 @@
|
|||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
@@ -2746,6 +2751,7 @@
|
|||||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^5.5.0",
|
"buffer": "^5.5.0",
|
||||||
"inherits": "^2.0.4",
|
"inherits": "^2.0.4",
|
||||||
@@ -2821,7 +2827,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.19",
|
"baseline-browser-mapping": "^2.8.19",
|
||||||
"caniuse-lite": "^1.0.30001751",
|
"caniuse-lite": "^1.0.30001751",
|
||||||
@@ -3285,6 +3290,7 @@
|
|||||||
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
|
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer-crc32": "^0.2.13",
|
"buffer-crc32": "^0.2.13",
|
||||||
"crc32-stream": "^4.0.2",
|
"crc32-stream": "^4.0.2",
|
||||||
@@ -3391,6 +3397,7 @@
|
|||||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"crc32": "bin/crc32.njs"
|
"crc32": "bin/crc32.njs"
|
||||||
},
|
},
|
||||||
@@ -3404,6 +3411,7 @@
|
|||||||
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
|
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crc-32": "^1.2.0",
|
"crc-32": "^1.2.0",
|
||||||
"readable-stream": "^3.4.0"
|
"readable-stream": "^3.4.0"
|
||||||
@@ -3636,7 +3644,6 @@
|
|||||||
"integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==",
|
"integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "24.13.3",
|
"app-builder-lib": "24.13.3",
|
||||||
"builder-util": "24.13.1",
|
"builder-util": "24.13.1",
|
||||||
@@ -3821,6 +3828,7 @@
|
|||||||
"integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==",
|
"integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "24.13.3",
|
"app-builder-lib": "24.13.3",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
@@ -3834,6 +3842,7 @@
|
|||||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -3849,6 +3858,7 @@
|
|||||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -3862,6 +3872,7 @@
|
|||||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@@ -4343,7 +4354,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
@@ -5062,7 +5074,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/isbinaryfile": {
|
"node_modules/isbinaryfile": {
|
||||||
"version": "5.0.6",
|
"version": "5.0.6",
|
||||||
@@ -5124,7 +5137,6 @@
|
|||||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
@@ -5230,6 +5242,7 @@
|
|||||||
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
|
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"readable-stream": "^2.0.5"
|
"readable-stream": "^2.0.5"
|
||||||
},
|
},
|
||||||
@@ -5243,6 +5256,7 @@
|
|||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
"inherits": "~2.0.3",
|
"inherits": "~2.0.3",
|
||||||
@@ -5258,7 +5272,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lazystream/node_modules/string_decoder": {
|
"node_modules/lazystream/node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
@@ -5266,6 +5281,7 @@
|
|||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
@@ -5302,35 +5318,40 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.difference": {
|
"node_modules/lodash.difference": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||||
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
|
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.flatten": {
|
"node_modules/lodash.flatten": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
|
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.isplainobject": {
|
"node_modules/lodash.isplainobject": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.union": {
|
"node_modules/lodash.union": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||||
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
|
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lowercase-keys": {
|
"node_modules/lowercase-keys": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -5985,6 +6006,26 @@
|
|||||||
"node": ">=10.4.0"
|
"node": ">=10.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/png2icons": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/png2icons/-/png2icons-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"png2icons": "png2icons-cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||||
@@ -6005,7 +6046,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -6154,7 +6194,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/progress": {
|
"node_modules/progress": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
@@ -6303,6 +6344,7 @@
|
|||||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"string_decoder": "^1.1.1",
|
"string_decoder": "^1.1.1",
|
||||||
@@ -6318,6 +6360,7 @@
|
|||||||
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
|
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimatch": "^5.1.0"
|
"minimatch": "^5.1.0"
|
||||||
}
|
}
|
||||||
@@ -6535,7 +6578,8 @@
|
|||||||
"url": "https://feross.org/support"
|
"url": "https://feross.org/support"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@@ -6601,7 +6645,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
|
||||||
"integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
|
"integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
@@ -6729,7 +6772,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.10.tgz",
|
||||||
"integrity": "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==",
|
"integrity": "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.1.0",
|
"csstype": "^3.1.0",
|
||||||
"seroval": "~1.3.0",
|
"seroval": "~1.3.0",
|
||||||
@@ -6849,6 +6891,7 @@
|
|||||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.2.0"
|
"safe-buffer": "~5.2.0"
|
||||||
}
|
}
|
||||||
@@ -7106,6 +7149,7 @@
|
|||||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bl": "^4.0.3",
|
"bl": "^4.0.3",
|
||||||
"end-of-stream": "^1.4.1",
|
"end-of-stream": "^1.4.1",
|
||||||
@@ -7489,7 +7533,6 @@
|
|||||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
@@ -8152,6 +8195,7 @@
|
|||||||
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
|
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver-utils": "^3.0.4",
|
"archiver-utils": "^3.0.4",
|
||||||
"compress-commons": "^4.1.2",
|
"compress-commons": "^4.1.2",
|
||||||
@@ -8167,6 +8211,7 @@
|
|||||||
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
|
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.2.3",
|
"glob": "^7.2.3",
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/client",
|
"name": "@opencode-ai/client",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "OpenCode desktop client - multi-instance, multi-session AI coding interface",
|
"description": "CodeNomad desktop client - multi-instance, multi-session AI coding interface",
|
||||||
"author": "OpenCode Team",
|
"author": "OpenCode Team",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/main/main.js",
|
"main": "dist/main/main.js",
|
||||||
@@ -41,6 +41,8 @@
|
|||||||
"@tsconfig/bun": "^1.0.9",
|
"@tsconfig/bun": "^1.0.9",
|
||||||
"autoprefixer": "10.4.21",
|
"autoprefixer": "10.4.21",
|
||||||
"electron": "39.0.0",
|
"electron": "39.0.0",
|
||||||
|
"png2icons": "^2.0.1",
|
||||||
|
"pngjs": "^7.0.0",
|
||||||
"electron-builder": "^24.0.0",
|
"electron-builder": "^24.0.0",
|
||||||
"electron-vite": "4.0.1",
|
"electron-vite": "4.0.1",
|
||||||
"postcss": "8.5.6",
|
"postcss": "8.5.6",
|
||||||
@@ -51,7 +53,7 @@
|
|||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "ai.opencode.client",
|
"appId": "ai.opencode.client",
|
||||||
"productName": "OpenCode Client",
|
"productName": "CodeNomad",
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "release",
|
"output": "release",
|
||||||
"buildResources": "electron/resources"
|
"buildResources": "electron/resources"
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ const platform = process.argv[2] || "mac"
|
|||||||
|
|
||||||
console.log(`
|
console.log(`
|
||||||
╔════════════════════════════════════════╗
|
╔════════════════════════════════════════╗
|
||||||
║ OpenCode Client - Binary Builder ║
|
║ CodeNomad - Binary Builder ║
|
||||||
╚════════════════════════════════════════╝
|
╚════════════════════════════════════════╝
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|||||||
155
scripts/generate-icons.js
Normal file
155
scripts/generate-icons.js
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { mkdirSync, readFileSync, writeFileSync } from "fs"
|
||||||
|
import { resolve, join, basename } from "path"
|
||||||
|
import { PNG } from "pngjs"
|
||||||
|
import png2icons from "png2icons"
|
||||||
|
|
||||||
|
function printUsage() {
|
||||||
|
console.log(`\nUsage: node scripts/generate-icons.js <input.png> [outputDir] [--name icon] [--radius 0.22]\n\nOptions:\n --name Base filename for generated assets (default: icon)\n --radius Corner radius ratio between 0 and 0.5 (default: 0.22)\n --help Show this message\n`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const args = [...argv]
|
||||||
|
const options = {
|
||||||
|
name: "icon",
|
||||||
|
radius: 0.22,
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
const token = args[i]
|
||||||
|
if (token === "--help" || token === "-h") {
|
||||||
|
options.help = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token === "--name" && i + 1 < args.length) {
|
||||||
|
options.name = args[i + 1]
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token === "--radius" && i + 1 < args.length) {
|
||||||
|
options.radius = Number(args[i + 1])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!options.input) {
|
||||||
|
options.input = token
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!options.output) {
|
||||||
|
options.output = token
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyRoundedCorners(png, ratio) {
|
||||||
|
const { width, height, data } = png
|
||||||
|
const clamped = Math.max(0, Math.min(ratio, 0.5))
|
||||||
|
if (clamped === 0) return png
|
||||||
|
|
||||||
|
const radius = Math.max(1, Math.min(width, height) * clamped)
|
||||||
|
const radiusSq = radius * radius
|
||||||
|
const rightThreshold = width - radius
|
||||||
|
const bottomThreshold = height - radius
|
||||||
|
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
const idx = (width * y + x) * 4
|
||||||
|
if (data[idx + 3] === 0) continue
|
||||||
|
|
||||||
|
const px = x + 0.5
|
||||||
|
const py = y + 0.5
|
||||||
|
|
||||||
|
const inLeft = px < radius
|
||||||
|
const inRight = px > rightThreshold
|
||||||
|
const inTop = py < radius
|
||||||
|
const inBottom = py > bottomThreshold
|
||||||
|
|
||||||
|
let outside = false
|
||||||
|
|
||||||
|
if (inLeft && inTop) {
|
||||||
|
outside = (px - radius) ** 2 + (py - radius) ** 2 > radiusSq
|
||||||
|
} else if (inRight && inTop) {
|
||||||
|
outside = (px - rightThreshold) ** 2 + (py - radius) ** 2 > radiusSq
|
||||||
|
} else if (inLeft && inBottom) {
|
||||||
|
outside = (px - radius) ** 2 + (py - bottomThreshold) ** 2 > radiusSq
|
||||||
|
} else if (inRight && inBottom) {
|
||||||
|
outside = (px - rightThreshold) ** 2 + (py - bottomThreshold) ** 2 > radiusSq
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outside) {
|
||||||
|
data[idx + 3] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return png
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const args = parseArgs(process.argv.slice(2))
|
||||||
|
|
||||||
|
if (args.help || !args.input) {
|
||||||
|
printUsage()
|
||||||
|
process.exit(args.help ? 0 : 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputPath = resolve(args.input)
|
||||||
|
const outputDir = resolve(args.output || "electron/resources")
|
||||||
|
const baseName = args.name || basename(inputPath, ".png")
|
||||||
|
const radiusRatio = Number.isFinite(args.radius) ? args.radius : 0.22
|
||||||
|
|
||||||
|
let buffer
|
||||||
|
try {
|
||||||
|
buffer = readFileSync(inputPath)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to read ${inputPath}:`, error.message)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let png
|
||||||
|
try {
|
||||||
|
png = PNG.sync.read(buffer)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Input must be a valid PNG:", error.message)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRoundedCorners(png, radiusRatio)
|
||||||
|
|
||||||
|
const roundedBuffer = PNG.sync.write(png)
|
||||||
|
|
||||||
|
try {
|
||||||
|
mkdirSync(outputDir, { recursive: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create output directory:", error.message)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pngPath = join(outputDir, `${baseName}.png`)
|
||||||
|
writeFileSync(pngPath, roundedBuffer)
|
||||||
|
|
||||||
|
const icns = png2icons.createICNS(roundedBuffer, png2icons.BICUBIC, false)
|
||||||
|
if (!icns) {
|
||||||
|
console.error("Failed to create ICNS file. Make sure the source PNG is at least 256x256.")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
writeFileSync(join(outputDir, `${baseName}.icns`), icns)
|
||||||
|
|
||||||
|
const ico = png2icons.createICO(roundedBuffer, png2icons.BICUBIC, false)
|
||||||
|
if (!ico) {
|
||||||
|
console.error("Failed to create ICO file. Make sure the source PNG is at least 256x256.")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
writeFileSync(join(outputDir, `${baseName}.ico`), ico)
|
||||||
|
|
||||||
|
console.log(`\nGenerated assets in ${outputDir}:`)
|
||||||
|
console.log(`- ${baseName}.png`)
|
||||||
|
console.log(`- ${baseName}.icns`)
|
||||||
|
console.log(`- ${baseName}.ico`)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -20,9 +20,6 @@ const AdvancedSettingsModal: Component<AdvancedSettingsModalProps> = (props) =>
|
|||||||
<Dialog.Content class="modal-surface w-full max-w-5xl max-h-[90vh] flex flex-col overflow-hidden">
|
<Dialog.Content class="modal-surface w-full max-w-5xl max-h-[90vh] flex flex-col overflow-hidden">
|
||||||
<header class="px-6 py-4 border-b" style={{ "border-color": "var(--border-base)" }}>
|
<header class="px-6 py-4 border-b" style={{ "border-color": "var(--border-base)" }}>
|
||||||
<Dialog.Title class="text-xl font-semibold text-primary">Advanced Settings</Dialog.Title>
|
<Dialog.Title class="text-xl font-semibold text-primary">Advanced Settings</Dialog.Title>
|
||||||
<Dialog.Description class="text-sm text-secondary mt-1">
|
|
||||||
Configure the OpenCode binary and environment variables used when launching new instances.
|
|
||||||
</Dialog.Description>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="flex-1 overflow-y-auto p-6 space-y-6">
|
<div class="flex-1 overflow-y-auto p-6 space-y-6">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Component } from "solid-js"
|
import { Component } from "solid-js"
|
||||||
import { Folder, Loader2 } from "lucide-solid"
|
import { Loader2 } from "lucide-solid"
|
||||||
|
|
||||||
|
const codeNomadIcon = new URL("../../images/CodeNomad-Icon.png", import.meta.url).href
|
||||||
|
|
||||||
interface EmptyStateProps {
|
interface EmptyStateProps {
|
||||||
onSelectFolder: () => void
|
onSelectFolder: () => void
|
||||||
@@ -11,13 +13,13 @@ const EmptyState: Component<EmptyStateProps> = (props) => {
|
|||||||
<div class="flex h-full w-full items-center justify-center bg-surface-secondary">
|
<div class="flex h-full w-full items-center justify-center bg-surface-secondary">
|
||||||
<div class="max-w-[500px] px-8 py-12 text-center">
|
<div class="max-w-[500px] px-8 py-12 text-center">
|
||||||
<div class="mb-8 flex justify-center">
|
<div class="mb-8 flex justify-center">
|
||||||
<Folder class="h-16 w-16 icon-muted" />
|
<img src={codeNomadIcon} alt="CodeNomad logo" class="h-24 w-auto" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="mb-4 text-2xl font-semibold text-primary">Welcome to OpenCode Client</h1>
|
<h1 class="mb-3 text-3xl font-semibold text-primary">CodeNomad</h1>
|
||||||
|
|
||||||
<p class="mb-8 text-base text-secondary">Select a folder to start coding with AI</p>
|
<p class="mb-8 text-base text-secondary">Select a folder to start coding with AI</p>
|
||||||
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={props.onSelectFolder}
|
onClick={props.onSelectFolder}
|
||||||
disabled={props.isLoading}
|
disabled={props.isLoading}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { recentFolders, removeRecentFolder, preferences, updateLastUsedBinary }
|
|||||||
import AdvancedSettingsModal from "./advanced-settings-modal"
|
import AdvancedSettingsModal from "./advanced-settings-modal"
|
||||||
import Kbd from "./kbd"
|
import Kbd from "./kbd"
|
||||||
|
|
||||||
|
const codeNomadLogo = new URL("../../images/CodeNomad-Icon.png", import.meta.url).href
|
||||||
|
|
||||||
interface FolderSelectionViewProps {
|
interface FolderSelectionViewProps {
|
||||||
onSelectFolder: (folder?: string, binaryPath?: string) => void
|
onSelectFolder: (folder?: string, binaryPath?: string) => void
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
@@ -202,171 +204,169 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
class="flex h-screen w-full items-start justify-center overflow-hidden py-6 relative"
|
class="flex h-screen w-full items-start justify-center overflow-hidden py-6 relative"
|
||||||
style="background-color: var(--surface-secondary)"
|
style="background-color: var(--surface-secondary)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full max-w-3xl h-full max-h-[90vh] px-8 flex flex-col overflow-hidden"
|
class="w-full max-w-3xl h-full px-8 pb-2 flex flex-col overflow-hidden"
|
||||||
aria-busy={isLoading() ? "true" : "false"}
|
aria-busy={isLoading() ? "true" : "false"}
|
||||||
>
|
>
|
||||||
<div class="mb-6 text-center shrink-0">
|
<div class="mb-6 text-center shrink-0">
|
||||||
<div class="mb-3 flex justify-center">
|
<div class="mb-3 flex justify-center">
|
||||||
<Folder class="h-16 w-16 icon-muted" />
|
<img src={codeNomadLogo} alt="CodeNomad logo" class="h-48 w-auto" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<h1 class="mb-2 text-2xl font-semibold text-primary">Welcome to OpenCode</h1>
|
<h1 class="mb-2 text-3xl font-semibold text-primary">CodeNomad</h1>
|
||||||
<p class="text-base text-secondary">Select a folder to start coding with AI</p>
|
<p class="text-base text-secondary">Select a folder to start coding with AI</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="space-y-4 flex-1 min-h-0 overflow-hidden flex flex-col">
|
<div class="space-y-4 flex-1 min-h-0 overflow-hidden flex flex-col">
|
||||||
|
|
||||||
<Show
|
<Show
|
||||||
when={folders().length > 0}
|
when={folders().length > 0}
|
||||||
fallback={
|
fallback={
|
||||||
<div class="panel panel-empty-state flex-1">
|
<div class="panel panel-empty-state flex-1">
|
||||||
<div class="panel-empty-state-icon">
|
<div class="panel-empty-state-icon">
|
||||||
<Clock class="w-12 h-12 mx-auto" />
|
<Clock class="w-12 h-12 mx-auto" />
|
||||||
|
</div>
|
||||||
|
<p class="panel-empty-state-title">No Recent Folders</p>
|
||||||
|
<p class="panel-empty-state-description">Browse for a folder to get started</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="panel-empty-state-title">No Recent Folders</p>
|
}
|
||||||
<p class="panel-empty-state-description">Browse for a folder to get started</p>
|
>
|
||||||
</div>
|
<div class="panel flex flex-col flex-1 min-h-0">
|
||||||
}
|
<div class="panel-header">
|
||||||
>
|
<h2 class="panel-title">Recent Folders</h2>
|
||||||
<div class="panel flex flex-col flex-1 min-h-0">
|
<p class="panel-subtitle">
|
||||||
<div class="panel-header">
|
{folders().length} {folders().length === 1 ? "folder" : "folders"} available
|
||||||
<h2 class="panel-title">Recent Folders</h2>
|
</p>
|
||||||
<p class="panel-subtitle">
|
</div>
|
||||||
{folders().length} {folders().length === 1 ? "folder" : "folders"} available
|
<div class="panel-list panel-list--fill flex-1 min-h-0 overflow-auto" ref={(el) => (recentListRef = el)}>
|
||||||
</p>
|
<For each={folders()}>
|
||||||
</div>
|
{(folder, index) => (
|
||||||
<div class="panel-list panel-list--fill flex-1 min-h-0 overflow-auto" ref={(el) => (recentListRef = el)}>
|
<div
|
||||||
<For each={folders()}>
|
class="panel-list-item"
|
||||||
{(folder, index) => (
|
classList={{
|
||||||
<div
|
"panel-list-item-highlight": focusMode() === "recent" && selectedIndex() === index(),
|
||||||
class="panel-list-item"
|
"panel-list-item-disabled": isLoading(),
|
||||||
classList={{
|
}}
|
||||||
"panel-list-item-highlight": focusMode() === "recent" && selectedIndex() === index(),
|
>
|
||||||
"panel-list-item-disabled": isLoading(),
|
<div class="flex items-center gap-2 w-full px-1">
|
||||||
}}
|
<button
|
||||||
>
|
data-folder-index={index()}
|
||||||
<div class="flex items-center gap-2 w-full px-1">
|
class="panel-list-item-content flex-1"
|
||||||
<button
|
disabled={isLoading()}
|
||||||
data-folder-index={index()}
|
onClick={() => handleFolderSelect(folder.path)}
|
||||||
class="panel-list-item-content flex-1"
|
onMouseEnter={() => {
|
||||||
disabled={isLoading()}
|
if (isLoading()) return
|
||||||
onClick={() => handleFolderSelect(folder.path)}
|
setFocusMode("recent")
|
||||||
onMouseEnter={() => {
|
setSelectedIndex(index())
|
||||||
if (isLoading()) return
|
}}
|
||||||
setFocusMode("recent")
|
>
|
||||||
setSelectedIndex(index())
|
<div class="flex items-center justify-between gap-3 w-full">
|
||||||
}}
|
<div class="flex-1 min-w-0">
|
||||||
>
|
<div class="flex items-center gap-2 mb-1">
|
||||||
<div class="flex items-center justify-between gap-3 w-full">
|
<Folder class="w-4 h-4 flex-shrink-0 icon-muted" />
|
||||||
<div class="flex-1 min-w-0">
|
<span class="text-sm font-medium truncate text-primary">
|
||||||
<div class="flex items-center gap-2 mb-1">
|
{folder.path.split("/").pop()}
|
||||||
<Folder class="w-4 h-4 flex-shrink-0 icon-muted" />
|
</span>
|
||||||
<span class="text-sm font-medium truncate text-primary">
|
</div>
|
||||||
{folder.path.split("/").pop()}
|
<div class="text-xs font-mono truncate pl-6 text-muted">
|
||||||
</span>
|
{getDisplayPath(folder.path)}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs font-mono truncate pl-6 text-muted">
|
<div class="text-xs mt-1 pl-6 text-muted">
|
||||||
{getDisplayPath(folder.path)}
|
{formatRelativeTime(folder.lastAccessed)}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs mt-1 pl-6 text-muted">
|
|
||||||
{formatRelativeTime(folder.lastAccessed)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={focusMode() === "recent" && selectedIndex() === index()}>
|
||||||
|
<kbd class="kbd">↵</kbd>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<Show when={focusMode() === "recent" && selectedIndex() === index()}>
|
</button>
|
||||||
<kbd class="kbd">↵</kbd>
|
<button
|
||||||
</Show>
|
onClick={(e) => handleRemove(folder.path, e)}
|
||||||
</div>
|
disabled={isLoading()}
|
||||||
</button>
|
class="p-2 transition-all hover:bg-red-100 dark:hover:bg-red-900/30 opacity-70 hover:opacity-100 rounded"
|
||||||
<button
|
title="Remove from recent"
|
||||||
onClick={(e) => handleRemove(folder.path, e)}
|
>
|
||||||
disabled={isLoading()}
|
<Trash2 class="w-3.5 h-3.5 transition-colors icon-muted hover:text-red-600 dark:hover:text-red-400" />
|
||||||
class="p-2 transition-all hover:bg-red-100 dark:hover:bg-red-900/30 opacity-70 hover:opacity-100 rounded"
|
</button>
|
||||||
title="Remove from recent"
|
</div>
|
||||||
>
|
|
||||||
<Trash2 class="w-3.5 h-3.5 transition-colors icon-muted hover:text-red-600 dark:hover:text-red-400" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</For>
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<div class="panel shrink-0">
|
|
||||||
<div class="panel-header">
|
|
||||||
<h2 class="panel-title">Browse for Folder</h2>
|
|
||||||
<p class="panel-subtitle">Select any folder on your computer</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-body">
|
|
||||||
<button
|
|
||||||
onClick={handleBrowse}
|
|
||||||
disabled={props.isLoading}
|
|
||||||
class="button-primary w-full flex items-center justify-center text-sm disabled:cursor-not-allowed"
|
|
||||||
onMouseEnter={() => setFocusMode("new")}
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<FolderPlus class="w-4 h-4" />
|
|
||||||
<span>{props.isLoading ? "Opening..." : "Browse Folders"}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<Kbd shortcut="cmd+n" class="ml-2" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Advanced settings section */}
|
|
||||||
<div class="panel-section w-full">
|
|
||||||
<button
|
|
||||||
onClick={() => setIsAdvancedModalOpen(true)}
|
|
||||||
class="panel-section-header w-full justify-between"
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<Settings class="w-4 h-4 icon-muted" />
|
|
||||||
<span class="text-sm font-medium text-secondary">Advanced Settings</span>
|
|
||||||
</div>
|
|
||||||
<ChevronRight class="w-4 h-4 icon-muted" />
|
|
||||||
</button>
|
|
||||||
<div class="panel-section-content text-sm text-muted">
|
|
||||||
Configure the OpenCode binary and environment variables.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 panel panel-footer shrink-0">
|
|
||||||
<div class="panel-footer-hints">
|
|
||||||
<Show when={folders().length > 0}>
|
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<kbd class="kbd">↑</kbd>
|
|
||||||
<kbd class="kbd">↓</kbd>
|
|
||||||
<span>Navigate</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<kbd class="kbd">Enter</kbd>
|
|
||||||
<span>Select</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<kbd class="kbd">Del</kbd>
|
|
||||||
<span>Remove</span>
|
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<Kbd shortcut="cmd+n" />
|
<div class="panel shrink-0">
|
||||||
<span>Browse</span>
|
<div class="panel-header">
|
||||||
|
<h2 class="panel-title">Browse for Folder</h2>
|
||||||
|
<p class="panel-subtitle">Select any folder on your computer</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-body">
|
||||||
|
<button
|
||||||
|
onClick={handleBrowse}
|
||||||
|
disabled={props.isLoading}
|
||||||
|
class="button-primary w-full flex items-center justify-center text-sm disabled:cursor-not-allowed"
|
||||||
|
onMouseEnter={() => setFocusMode("new")}
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<FolderPlus class="w-4 h-4" />
|
||||||
|
<span>{props.isLoading ? "Opening..." : "Browse Folders"}</span>
|
||||||
|
</div>
|
||||||
|
<Kbd shortcut="cmd+n" class="ml-2" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Advanced settings section */}
|
||||||
|
<div class="panel-section w-full">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsAdvancedModalOpen(true)}
|
||||||
|
class="panel-section-header w-full justify-between"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Settings class="w-4 h-4 icon-muted" />
|
||||||
|
<span class="text-sm font-medium text-secondary">Advanced Settings</span>
|
||||||
|
</div>
|
||||||
|
<ChevronRight class="w-4 h-4 icon-muted" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-1 panel panel-footer shrink-0">
|
||||||
|
<div class="panel-footer-hints">
|
||||||
|
<Show when={folders().length > 0}>
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<kbd class="kbd">↑</kbd>
|
||||||
|
<kbd class="kbd">↓</kbd>
|
||||||
|
<span>Navigate</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<kbd class="kbd">Enter</kbd>
|
||||||
|
<span>Select</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<kbd class="kbd">Del</kbd>
|
||||||
|
<span>Remove</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<Kbd shortcut="cmd+n" />
|
||||||
|
<span>Browse</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Show when={isLoading()}>
|
||||||
<Show when={isLoading()}>
|
<div class="folder-loading-overlay">
|
||||||
<div class="folder-loading-overlay">
|
<div class="folder-loading-indicator">
|
||||||
<div class="folder-loading-indicator">
|
<div class="spinner" />
|
||||||
<div class="spinner" />
|
<p class="folder-loading-text">Starting instance…</p>
|
||||||
<p class="folder-loading-text">Starting instance…</p>
|
<p class="folder-loading-subtext">Hang tight while we prepare your workspace.</p>
|
||||||
<p class="folder-loading-subtext">Hang tight while we prepare your workspace.</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Show>
|
||||||
</Show>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<AdvancedSettingsModal
|
<AdvancedSettingsModal
|
||||||
open={isAdvancedModalOpen()}
|
open={isAdvancedModalOpen()}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>OpenCode Client</title>
|
<title>CodeNomad</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Task Management
|
# Task Management
|
||||||
|
|
||||||
This directory contains the task breakdown for building the OpenCode Client.
|
This directory contains the task breakdown for building CodeNomad.
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ packages/opencode-client/
|
|||||||
|
|
||||||
**src/App.tsx:**
|
**src/App.tsx:**
|
||||||
|
|
||||||
- Basic component with "Hello OpenCode Client"
|
- Basic component with "Hello CodeNomad"
|
||||||
- Display environment info
|
- Display environment info
|
||||||
- Basic styling with TailwindCSS
|
- Basic styling with TailwindCSS
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ packages/opencode-client/
|
|||||||
**electron-builder.yml** or in package.json:
|
**electron-builder.yml** or in package.json:
|
||||||
|
|
||||||
- appId: ai.opencode.client
|
- appId: ai.opencode.client
|
||||||
- Product name: OpenCode Client
|
- Product name: CodeNomad
|
||||||
- Build resources: electron/resources
|
- Build resources: electron/resources
|
||||||
- Files to include: dist/, package.json
|
- Files to include: dist/, package.json
|
||||||
- Directories:
|
- Directories:
|
||||||
@@ -237,7 +237,7 @@ release/
|
|||||||
1. Run `bun install`
|
1. Run `bun install`
|
||||||
2. Run `bun run dev`
|
2. Run `bun run dev`
|
||||||
3. Verify Electron window opens
|
3. Verify Electron window opens
|
||||||
4. Verify "Hello OpenCode Client" displays
|
4. Verify "Hello CodeNomad" displays
|
||||||
5. Make a change to App.tsx
|
5. Make a change to App.tsx
|
||||||
6. Verify hot reload updates UI
|
6. Verify hot reload updates UI
|
||||||
7. Run `bun run typecheck`
|
7. Run `bun run typecheck`
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ Create the initial empty state interface that appears when no instances are runn
|
|||||||
|
|
||||||
- Centered container
|
- Centered container
|
||||||
- Large folder icon (from lucide-solid)
|
- Large folder icon (from lucide-solid)
|
||||||
- Heading: "Welcome to OpenCode Client"
|
|
||||||
- Subheading: "Select a folder to start coding with AI"
|
- Subheading: "Select a folder to start coding with AI"
|
||||||
- Primary button: "Select Folder"
|
- Primary button: "Select Folder"
|
||||||
- Helper text: "Keyboard shortcut: Cmd/Ctrl+N"
|
- Helper text: "Keyboard shortcut: Cmd/Ctrl+N"
|
||||||
|
|||||||
Reference in New Issue
Block a user