fix: restore complete source code and fix launchers

- Copy complete source code packages from original CodeNomad project
- Add root package.json with npm workspace configuration
- Include electron-app, server, ui, tauri-app, and opencode-config packages
- Fix Launch-Windows.bat and Launch-Dev-Windows.bat to work with correct npm scripts
- Fix Launch-Unix.sh to work with correct npm scripts
- Launchers now correctly call npm run dev:electron which launches Electron app
This commit is contained in:
Gemini AI
2025-12-23 12:57:55 +04:00
Unverified
parent f365d64c97
commit b448d11991
249 changed files with 48776 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CodeNomad</title>
<style>
:root {
color-scheme: dark;
}
html,
body {
background-color: #1a1a1a;
color: #e0e0e0;
}
</style>
<script>
;(function () {
try {
document.documentElement.setAttribute('data-theme', 'dark')
} catch (error) {
const rawConsole = globalThis?.["console"]
rawConsole?.warn?.('Failed to apply initial theme', error)
}
})()
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CodeNomad</title>
<script>
;(function () {
try {
document.documentElement.setAttribute('data-theme', 'dark')
} catch (error) {
const rawConsole = globalThis?.["console"]
rawConsole?.warn?.('Failed to apply initial theme', error)
}
})()
</script>
</head>
<body>
<div id="loading-root"></div>
<script type="module" src="./loading/main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,111 @@
:root {
color-scheme: dark;
}
body {
margin: 0;
min-height: 100vh;
background-color: var(--surface-base, #0f141f);
color: var(--text-primary, #cfd4dc);
font-family: var(--font-family-sans, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
display: flex;
align-items: center;
justify-content: center;
padding: 32px;
}
button {
border: none;
background: none;
font: inherit;
color: inherit;
}
.loading-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
max-width: 520px;
width: 100%;
text-align: center;
}
.loading-logo {
width: 180px;
height: auto;
filter: drop-shadow(0 20px 60px rgba(0, 0, 0, 0.45));
}
.loading-heading {
display: flex;
flex-direction: column;
gap: 4px;
}
.loading-title {
font-size: 2.8rem;
font-weight: 600;
margin: 0;
color: var(--text-primary, #f4f6fb);
}
.loading-status {
margin: 0;
font-size: 1rem;
color: var(--text-muted, #aeb3c4);
}
.loading-card {
margin-top: 12px;
width: 100%;
max-width: 420px;
padding: 22px;
border-radius: 18px;
background: rgba(13, 16, 24, 0.85);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.55);
}
.loading-row {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
font-size: 0.95rem;
}
.spinner {
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.18);
border-top-color: #6ce3ff;
animation: spin 0.9s linear infinite;
}
.phrase-controls {
margin-top: 12px;
font-size: 0.9rem;
color: var(--text-muted, #8f96a9);
}
.phrase-controls button {
color: #8fb5ff;
cursor: pointer;
}
.loading-error {
margin-top: 12px;
color: #ff9ea9;
font-size: 0.95rem;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,166 @@
import { Show, createSignal, onCleanup, onMount } from "solid-js"
import { render } from "solid-js/web"
import iconUrl from "../../images/CodeNomad-Icon.png"
import { runtimeEnv, isTauriHost } from "../../lib/runtime-env"
import "../../index.css"
import "./loading.css"
const phrases = [
"Warming up the AI neurons…",
"Convincing the AI to stop daydreaming…",
"Polishing the AIs code goggles…",
"Asking the AI to stop reorganizing your files…",
"Feeding the AI additional coffee…",
"Teaching the AI not to delete node_modules (again)…",
"Telling the AI to act natural before you arrive…",
"Asking the AI to please stop rewriting history…",
"Letting the AI stretch before its coding sprint…",
"Persuading the AI to give you keyboard control…",
]
interface CliStatus {
state?: string
url?: string | null
error?: string | null
}
interface TauriBridge {
invoke?: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T>
event?: {
listen: (event: string, handler: (payload: { payload: unknown }) => void) => Promise<() => void>
}
}
function pickPhrase(previous?: string) {
const filtered = phrases.filter((phrase) => phrase !== previous)
const source = filtered.length > 0 ? filtered : phrases
const index = Math.floor(Math.random() * source.length)
return source[index]
}
function navigateTo(url?: string | null) {
if (!url) return
window.location.replace(url)
}
function getTauriBridge(): TauriBridge | null {
if (typeof window === "undefined") {
return null
}
const bridge = (window as { __TAURI__?: TauriBridge }).__TAURI__
if (!bridge || !bridge.event || !bridge.invoke) {
return null
}
return bridge
}
function annotateDocument() {
if (typeof document === "undefined") {
return
}
document.documentElement.dataset.runtimeHost = runtimeEnv.host
document.documentElement.dataset.runtimePlatform = runtimeEnv.platform
}
function LoadingApp() {
const [phrase, setPhrase] = createSignal(pickPhrase())
const [error, setError] = createSignal<string | null>(null)
const [status, setStatus] = createSignal<string | null>(null)
const changePhrase = () => setPhrase(pickPhrase(phrase()))
onMount(() => {
annotateDocument()
setPhrase(pickPhrase())
const unsubscribers: Array<() => void> = []
async function bootstrapTauri(tauriBridge: TauriBridge | null) {
if (!tauriBridge || !tauriBridge.event || !tauriBridge.invoke) {
return
}
try {
const readyUnlisten = await tauriBridge.event.listen("cli:ready", (event) => {
const payload = (event?.payload as CliStatus) || {}
setError(null)
setStatus(null)
navigateTo(payload.url)
})
const errorUnlisten = await tauriBridge.event.listen("cli:error", (event) => {
const payload = (event?.payload as CliStatus) || {}
if (payload.error) {
setError(payload.error)
setStatus("Encountered an issue")
}
})
const statusUnlisten = await tauriBridge.event.listen("cli:status", (event) => {
const payload = (event?.payload as CliStatus) || {}
if (payload.state === "error" && payload.error) {
setError(payload.error)
setStatus("Encountered an issue")
return
}
if (payload.state && payload.state !== "ready") {
setError(null)
setStatus(null)
}
})
unsubscribers.push(readyUnlisten, errorUnlisten, statusUnlisten)
const result = await tauriBridge.invoke<CliStatus>("cli_get_status")
if (result?.state === "ready" && result.url) {
navigateTo(result.url)
} else if (result?.state === "error" && result.error) {
setError(result.error)
setStatus("Encountered an issue")
}
} catch (err) {
setError(String(err))
setStatus("Encountered an issue")
}
}
if (isTauriHost()) {
void bootstrapTauri(getTauriBridge())
}
onCleanup(() => {
unsubscribers.forEach((unsubscribe) => {
try {
unsubscribe()
} catch {
/* noop */
}
})
})
})
return (
<div class="loading-wrapper" role="status" aria-live="polite">
<img src={iconUrl} alt="CodeNomad" class="loading-logo" width="180" height="180" />
<div class="loading-heading">
<h1 class="loading-title">CodeNomad</h1>
<Show when={status()}>{(statusText) => <p class="loading-status">{statusText()}</p>}</Show>
</div>
<div class="loading-card">
<div class="loading-row">
<div class="spinner" aria-hidden="true" />
<span>{phrase()}</span>
</div>
<div class="phrase-controls">
<button type="button" onClick={changePhrase}>
Show another
</button>
</div>
{error() && <div class="loading-error">{error()}</div>}
</div>
</div>
)
}
const root = document.getElementById("loading-root")
if (!root) {
throw new Error("Loading root element not found")
}
render(() => <LoadingApp />, root)

View File

@@ -0,0 +1 @@
import "../main.tsx"