mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-16 13:44:44 +00:00
fix: tfcode web fails after npm install — copy app dist in postinstall
The web command crashed because postinstall scripts never copied the app/dist directory from the platform package. Added copyAppDir() to both postinstall scripts, multi-path resolution in findAppDir() and server static serving, and updated branding to use local favicon. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2
bun.lock
2
bun.lock
@@ -381,7 +381,7 @@
|
||||
},
|
||||
"packages/tfcode": {
|
||||
"name": "@toothfairyai/tfcode",
|
||||
"version": "1.0.33",
|
||||
"version": "1.0.36",
|
||||
"bin": {
|
||||
"tfcode": "./bin/tfcode",
|
||||
},
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>TF Code</title>
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96-v3.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon-v3.svg" />
|
||||
<link rel="shortcut icon" href="/favicon-v3.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-v3.png" />
|
||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<meta name="theme-color" content="#F8F7F7" />
|
||||
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
|
||||
|
||||
BIN
packages/app/public/favicon.png
Executable file
BIN
packages/app/public/favicon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@@ -67,7 +67,7 @@ const notify: Platform["notify"] = async (title, description, href) => {
|
||||
|
||||
const notification = new Notification(title, {
|
||||
body: description ?? "",
|
||||
icon: "https://opencode.ai/favicon-96x96-v3.png",
|
||||
icon: "/favicon.png",
|
||||
})
|
||||
|
||||
notification.onclick = () => {
|
||||
|
||||
@@ -43,9 +43,7 @@ export const ProjectIcon = (props: { project: LocalProject; class?: string; noti
|
||||
<div class="size-full rounded overflow-clip">
|
||||
<Avatar
|
||||
fallback={name()}
|
||||
src={
|
||||
props.project.id === OPENCODE_PROJECT_ID ? "https://opencode.ai/favicon.svg" : props.project.icon?.override
|
||||
}
|
||||
src={props.project.id === OPENCODE_PROJECT_ID ? "/favicon.png" : props.project.icon?.override}
|
||||
{...getAvatarColors(props.project.icon?.color)}
|
||||
class="size-full rounded"
|
||||
classList={{ "badge-mask": notify() }}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.33",
|
||||
"version": "1.0.36",
|
||||
"name": "@toothfairyai/tfcode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"bin",
|
||||
"app",
|
||||
"python",
|
||||
"postinstall.mjs",
|
||||
"LICENSE"
|
||||
|
||||
@@ -164,6 +164,10 @@ for (const item of targets) {
|
||||
}
|
||||
|
||||
await $`rm -rf ./dist/${name}/bin/tui`
|
||||
const appDistDir = path.resolve(dir, "../app/dist")
|
||||
if (fs.existsSync(appDistDir)) {
|
||||
fs.cpSync(appDistDir, path.join(dir, "dist", name, "bin", "app", "dist"), { recursive: true })
|
||||
}
|
||||
await Bun.file(`dist/${name}/package.json`).write(
|
||||
JSON.stringify(
|
||||
{
|
||||
|
||||
@@ -181,6 +181,15 @@ async function downloadBinary() {
|
||||
console.log(`Installed tfcode to ${targetBinary}`)
|
||||
}
|
||||
|
||||
// Move app directory
|
||||
const extractedAppDir = path.join(tmpDir, "app")
|
||||
const targetAppDir = path.join(binDir, "app")
|
||||
if (fs.existsSync(path.join(extractedAppDir, "dist", "index.html"))) {
|
||||
if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
|
||||
fs.cpSync(extractedAppDir, targetAppDir, { recursive: true })
|
||||
console.log("Installed web app")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
}
|
||||
@@ -202,6 +211,16 @@ function setupBinary(sourcePath, platform) {
|
||||
|
||||
fs.chmodSync(targetBinary, 0o755)
|
||||
console.log(`tfcode installed to ${targetBinary}`)
|
||||
|
||||
// Also copy app dir from platform package
|
||||
const sourceDir = path.dirname(sourcePath)
|
||||
const srcAppDir = path.join(sourceDir, "app")
|
||||
const targetAppDir = path.join(binDir, "app")
|
||||
if (fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
|
||||
if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
|
||||
fs.cpSync(srcAppDir, targetAppDir, { recursive: true })
|
||||
console.log("Web app copied from platform package")
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,171 +1,225 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn, execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawn, execSync } = require("child_process")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
|
||||
const RESET = '\x1b[0m';
|
||||
const BOLD = '\x1b[1m';
|
||||
const GREEN = '\x1b[32m';
|
||||
const YELLOW = '\x1b[33m';
|
||||
const RED = '\x1b[31m';
|
||||
const CYAN = '\x1b[36m';
|
||||
const DIM = '\x1b[90m';
|
||||
const RESET = "\x1b[0m"
|
||||
const BOLD = "\x1b[1m"
|
||||
const GREEN = "\x1b[32m"
|
||||
const YELLOW = "\x1b[33m"
|
||||
const RED = "\x1b[31m"
|
||||
const CYAN = "\x1b[36m"
|
||||
const DIM = "\x1b[90m"
|
||||
|
||||
function log(msg) {
|
||||
console.log(msg);
|
||||
console.log(msg)
|
||||
}
|
||||
|
||||
function logSuccess(msg) {
|
||||
console.log(`${GREEN}✓${RESET} ${msg}`);
|
||||
console.log(`${GREEN}✓${RESET} ${msg}`)
|
||||
}
|
||||
|
||||
function logError(msg) {
|
||||
console.error(`${RED}✗${RESET} ${msg}`);
|
||||
console.error(`${RED}✗${RESET} ${msg}`)
|
||||
}
|
||||
|
||||
function logInfo(msg) {
|
||||
console.log(`${CYAN}ℹ${RESET} ${msg}`);
|
||||
console.log(`${CYAN}ℹ${RESET} ${msg}`)
|
||||
}
|
||||
|
||||
function logWarning(msg) {
|
||||
console.log(`${YELLOW}!${RESET} ${msg}`);
|
||||
console.log(`${YELLOW}!${RESET} ${msg}`)
|
||||
}
|
||||
|
||||
function checkPython() {
|
||||
const commands = ['python3', 'python'];
|
||||
|
||||
const commands = ["python3", "python"]
|
||||
|
||||
for (const cmd of commands) {
|
||||
try {
|
||||
const result = execSync(`${cmd} --version`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
||||
const match = result.match(/Python (\d+)\.(\d+)/);
|
||||
const result = execSync(`${cmd} --version`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
|
||||
const match = result.match(/Python (\d+)\.(\d+)/)
|
||||
if (match) {
|
||||
const major = parseInt(match[1]);
|
||||
const minor = parseInt(match[2]);
|
||||
const major = parseInt(match[1])
|
||||
const minor = parseInt(match[2])
|
||||
if (major >= 3 && minor >= 10) {
|
||||
return { cmd, version: result.trim() };
|
||||
return { cmd, version: result.trim() }
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function installPythonDeps(pythonCmd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const packages = ['toothfairyai', 'pydantic', 'httpx', 'rich'];
|
||||
|
||||
log(`${DIM}Installing Python packages: ${packages.join(', ')}...${RESET}`);
|
||||
|
||||
const packages = ["toothfairyai", "pydantic", "httpx", "rich"]
|
||||
|
||||
log(`${DIM}Installing Python packages: ${packages.join(", ")}...${RESET}`)
|
||||
|
||||
// Try with --user first, then --break-system-packages if needed
|
||||
const args = ['-m', 'pip', 'install', '--user', ...packages];
|
||||
|
||||
const args = ["-m", "pip", "install", "--user", ...packages]
|
||||
|
||||
const proc = spawn(pythonCmd, args, {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
})
|
||||
|
||||
proc.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
resolve()
|
||||
} else {
|
||||
// Try with --break-system-packages flag
|
||||
log(`${DIM}Retrying with --break-system-packages...${RESET}`);
|
||||
const retryArgs = ['-m', 'pip', 'install', '--user', '--break-system-packages', ...packages];
|
||||
log(`${DIM}Retrying with --break-system-packages...${RESET}`)
|
||||
const retryArgs = ["-m", "pip", "install", "--user", "--break-system-packages", ...packages]
|
||||
const retry = spawn(pythonCmd, retryArgs, {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
retry.on('close', (retryCode) => {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
})
|
||||
|
||||
retry.on("close", (retryCode) => {
|
||||
if (retryCode === 0) {
|
||||
resolve();
|
||||
resolve()
|
||||
} else {
|
||||
reject(new Error(`pip install exited with code ${retryCode}`));
|
||||
reject(new Error(`pip install exited with code ${retryCode}`))
|
||||
}
|
||||
});
|
||||
|
||||
retry.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
|
||||
retry.on("error", (err) => {
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
proc.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
proc.on("error", (err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function copyAppDir() {
|
||||
let platform
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
platform = "darwin"
|
||||
break
|
||||
case "linux":
|
||||
platform = "linux"
|
||||
break
|
||||
case "win32":
|
||||
platform = "windows"
|
||||
break
|
||||
default:
|
||||
platform = process.platform
|
||||
}
|
||||
|
||||
let arch
|
||||
switch (process.arch) {
|
||||
case "x64":
|
||||
arch = "x64"
|
||||
break
|
||||
case "arm64":
|
||||
arch = "arm64"
|
||||
break
|
||||
default:
|
||||
arch = process.arch
|
||||
}
|
||||
|
||||
const pkgName = `@toothfairyai/tfcode-${platform}-${arch}`
|
||||
const appDir = path.join(__dirname, "app")
|
||||
|
||||
if (fs.existsSync(path.join(appDir, "dist", "index.html"))) {
|
||||
logSuccess("Web app already present")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const pkgDir = path.dirname(require.resolve(`${pkgName}/package.json`))
|
||||
const srcAppDir = path.join(pkgDir, "bin", "app")
|
||||
|
||||
if (!fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
|
||||
logWarning("Web app dist not found in platform package")
|
||||
return
|
||||
}
|
||||
|
||||
fs.cpSync(srcAppDir, appDir, { recursive: true })
|
||||
logSuccess("Web app copied from platform package")
|
||||
} catch (e) {
|
||||
logWarning(`Could not copy web app: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
log('');
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
||||
log(`${BOLD} tfcode - ToothFairyAI's official coding agent${RESET}`);
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
||||
log('');
|
||||
|
||||
log("")
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`)
|
||||
log(`${BOLD} tfcode - ToothFairyAI's official coding agent${RESET}`)
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`)
|
||||
log("")
|
||||
|
||||
copyAppDir()
|
||||
|
||||
// Check for Python
|
||||
logInfo('Checking Python installation...');
|
||||
const python = checkPython();
|
||||
|
||||
logInfo("Checking Python installation...")
|
||||
const python = checkPython()
|
||||
|
||||
if (!python) {
|
||||
log('');
|
||||
logError('Python 3.10+ is required but not found.');
|
||||
log('');
|
||||
log(`${BOLD}Please install Python 3.10 or later:${RESET}`);
|
||||
log('');
|
||||
log(` ${CYAN}macOS:${RESET} brew install python@3.12`);
|
||||
log(` ${CYAN}Ubuntu:${RESET} sudo apt-get install python3.12`);
|
||||
log(` ${CYAN}Windows:${RESET} Download from https://python.org/downloads`);
|
||||
log('');
|
||||
log(`${DIM}After installing Python, run: npm rebuild tfcode${RESET}`);
|
||||
log('');
|
||||
process.exit(1);
|
||||
log("")
|
||||
logError("Python 3.10+ is required but not found.")
|
||||
log("")
|
||||
log(`${BOLD}Please install Python 3.10 or later:${RESET}`)
|
||||
log("")
|
||||
log(` ${CYAN}macOS:${RESET} brew install python@3.12`)
|
||||
log(` ${CYAN}Ubuntu:${RESET} sudo apt-get install python3.12`)
|
||||
log(` ${CYAN}Windows:${RESET} Download from https://python.org/downloads`)
|
||||
log("")
|
||||
log(`${DIM}After installing Python, run: npm rebuild tfcode${RESET}`)
|
||||
log("")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
logSuccess(`Found ${python.version} (${python.cmd})`);
|
||||
log('');
|
||||
|
||||
|
||||
logSuccess(`Found ${python.version} (${python.cmd})`)
|
||||
log("")
|
||||
|
||||
// Install Python dependencies
|
||||
logInfo('Installing ToothFairyAI Python SDK...');
|
||||
logInfo("Installing ToothFairyAI Python SDK...")
|
||||
try {
|
||||
await installPythonDeps(python.cmd);
|
||||
logSuccess('Python dependencies installed');
|
||||
await installPythonDeps(python.cmd)
|
||||
logSuccess("Python dependencies installed")
|
||||
} catch (err) {
|
||||
logWarning(`Failed to install Python dependencies: ${err.message}`);
|
||||
log('');
|
||||
log(`${DIM}You can install them manually with:${RESET}`);
|
||||
log(` ${CYAN}${python.cmd} -m pip install toothfairyai pydantic httpx rich${RESET}`);
|
||||
log('');
|
||||
logWarning(`Failed to install Python dependencies: ${err.message}`)
|
||||
log("")
|
||||
log(`${DIM}You can install them manually with:${RESET}`)
|
||||
log(` ${CYAN}${python.cmd} -m pip install toothfairyai pydantic httpx rich${RESET}`)
|
||||
log("")
|
||||
// Don't exit - user might install manually later
|
||||
}
|
||||
|
||||
log('');
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
||||
log(`${GREEN}✓ tfcode installed successfully!${RESET}`);
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
||||
log('');
|
||||
log(`${BOLD}Quick Start:${RESET}`);
|
||||
log('');
|
||||
log(` ${CYAN}1.${RESET} Set your ToothFairyAI credentials:`);
|
||||
log(` ${DIM}export TF_WORKSPACE_ID="your-workspace-id"${RESET}`);
|
||||
log(` ${DIM}export TF_API_KEY="your-api-key"${RESET}`);
|
||||
log('');
|
||||
log(` ${CYAN}2.${RESET} Validate your credentials:`);
|
||||
log(` ${DIM}tfcode validate${RESET}`);
|
||||
log('');
|
||||
log(` ${CYAN}3.${RESET} Sync tools from your workspace:`);
|
||||
log(` ${DIM}tfcode sync${RESET}`);
|
||||
log('');
|
||||
log(` ${CYAN}4.${RESET} Start coding:`);
|
||||
log(` ${DIM}tfcode${RESET}`);
|
||||
log('');
|
||||
log(`${DIM}Documentation: https://toothfairyai.com/developers/tfcode${RESET}`);
|
||||
log('');
|
||||
|
||||
log("")
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`)
|
||||
log(`${GREEN}✓ tfcode installed successfully!${RESET}`)
|
||||
log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`)
|
||||
log("")
|
||||
log(`${BOLD}Quick Start:${RESET}`)
|
||||
log("")
|
||||
log(` ${CYAN}1.${RESET} Set your ToothFairyAI credentials:`)
|
||||
log(` ${DIM}export TF_WORKSPACE_ID="your-workspace-id"${RESET}`)
|
||||
log(` ${DIM}export TF_API_KEY="your-api-key"${RESET}`)
|
||||
log("")
|
||||
log(` ${CYAN}2.${RESET} Validate your credentials:`)
|
||||
log(` ${DIM}tfcode validate${RESET}`)
|
||||
log("")
|
||||
log(` ${CYAN}3.${RESET} Sync tools from your workspace:`)
|
||||
log(` ${DIM}tfcode sync${RESET}`)
|
||||
log("")
|
||||
log(` ${CYAN}4.${RESET} Start coding:`)
|
||||
log(` ${DIM}tfcode${RESET}`)
|
||||
log("")
|
||||
log(`${DIM}Documentation: https://toothfairyai.com/developers/tfcode${RESET}`)
|
||||
log("")
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
logError(`Installation failed: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
logError(`Installation failed: ${err.message}`)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@@ -11,9 +11,6 @@ export const ServeCommand = cmd({
|
||||
builder: (yargs) => withNetworkOptions(yargs),
|
||||
describe: "starts a headless tfcode server",
|
||||
handler: async (args) => {
|
||||
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
||||
console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
||||
}
|
||||
const opts = await resolveNetworkOptions(args)
|
||||
const server = Server.listen(opts)
|
||||
console.log(`tfcode server listening on http://${server.hostname}:${server.port}`)
|
||||
|
||||
@@ -27,8 +27,22 @@ function getNetworkIPs() {
|
||||
return results
|
||||
}
|
||||
|
||||
function findAppDir(): string {
|
||||
const binDir = path.dirname(process.execPath)
|
||||
const candidates = [
|
||||
path.join(binDir, "app"),
|
||||
path.join(binDir, "..", "app"),
|
||||
path.join(import.meta.dirname, "..", "app"),
|
||||
path.join(import.meta.dirname, "..", "..", "..", "..", "app"),
|
||||
]
|
||||
for (const dir of candidates) {
|
||||
if (existsSync(path.join(dir, "dist", "index.html"))) return dir
|
||||
}
|
||||
return candidates[0]
|
||||
}
|
||||
|
||||
async function ensureAppBuilt() {
|
||||
const appDir = path.join(import.meta.dirname, "..", "..", "..", "..", "app")
|
||||
const appDir = findAppDir()
|
||||
const distDir = path.join(appDir, "dist")
|
||||
|
||||
if (!existsSync(distDir) || !existsSync(path.join(distDir, "index.html"))) {
|
||||
@@ -44,10 +58,6 @@ export const WebCommand = cmd({
|
||||
builder: (yargs) => withNetworkOptions(yargs),
|
||||
describe: "start tfcode server and open web interface",
|
||||
handler: async (args) => {
|
||||
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
||||
UI.println(UI.Style.TEXT_WARNING_BOLD + "! " + "OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
||||
}
|
||||
|
||||
await ensureAppBuilt()
|
||||
|
||||
const opts = await resolveNetworkOptions(args)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { cors } from "hono/cors"
|
||||
import { basicAuth } from "hono/basic-auth"
|
||||
import z from "zod"
|
||||
import path from "path"
|
||||
import { existsSync } from "fs"
|
||||
import { Provider } from "../provider/provider"
|
||||
import { NamedError } from "@opencode-ai/util/error"
|
||||
import { LSP } from "../lsp"
|
||||
@@ -530,8 +531,15 @@ export namespace Server {
|
||||
)
|
||||
.all("/*", async (c) => {
|
||||
const reqPath = c.req.path === "/" ? "/index.html" : c.req.path
|
||||
const appDist = path.join(import.meta.dirname, "..", "..", "..", "app", "dist")
|
||||
const publicDir = path.join(import.meta.dirname, "..", "..", "..", "app", "public")
|
||||
const binDir = path.dirname(process.execPath)
|
||||
const appDistCandidates = [
|
||||
path.join(binDir, "app", "dist"),
|
||||
path.join(binDir, "..", "app", "dist"),
|
||||
path.join(import.meta.dirname, "..", "app", "dist"),
|
||||
path.join(import.meta.dirname, "..", "..", "..", "app", "dist"),
|
||||
]
|
||||
const appDist = appDistCandidates.find((d) => existsSync(path.join(d, "index.html"))) ?? appDistCandidates[0]
|
||||
const publicDir = path.join(appDist, "..", "public")
|
||||
|
||||
let filePath = path.join(appDist, reqPath)
|
||||
let file = Bun.file(filePath)
|
||||
|
||||
BIN
packages/ui/src/assets/favicon.png
Executable file
BIN
packages/ui/src/assets/favicon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@@ -1,4 +1,13 @@
|
||||
[data-component="logo-mark"] {
|
||||
width: 16px;
|
||||
aspect-ratio: 4/5;
|
||||
aspect-ratio: 137/120;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
[data-component="logo-splash"] {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
[data-component="logo-full"] {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@@ -1,60 +1,39 @@
|
||||
import { ComponentProps } from "solid-js"
|
||||
import tfIcon from "../assets/favicon.png"
|
||||
|
||||
export const Mark = (props: { class?: string }) => {
|
||||
return (
|
||||
<svg
|
||||
<img
|
||||
data-component="logo-mark"
|
||||
classList={{ [props.class ?? ""]: !!props.class }}
|
||||
viewBox="0 0 16 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path data-slot="logo-logo-mark-shadow" d="M12 16H4V8H12V16Z" fill="var(--icon-weak-base)" />
|
||||
<path data-slot="logo-logo-mark-o" d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="var(--icon-strong-base)" />
|
||||
</svg>
|
||||
src={tfIcon}
|
||||
alt="ToothFairyAI"
|
||||
draggable={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
|
||||
export const Splash = (props: Pick<ComponentProps<"img">, "ref" | "class">) => {
|
||||
return (
|
||||
<svg
|
||||
<img
|
||||
ref={props.ref}
|
||||
data-component="logo-splash"
|
||||
classList={{ [props.class ?? ""]: !!props.class }}
|
||||
viewBox="0 0 80 100"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M60 80H20V40H60V80Z" fill="var(--icon-base)" />
|
||||
<path d="M60 20H20V80H60V20ZM80 100H0V0H80V100Z" fill="var(--icon-strong-base)" />
|
||||
</svg>
|
||||
src={tfIcon}
|
||||
alt="ToothFairyAI"
|
||||
draggable={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const Logo = (props: { class?: string }) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 180 42"
|
||||
fill="none"
|
||||
<img
|
||||
data-component="logo-full"
|
||||
classList={{ [props.class ?? ""]: !!props.class }}
|
||||
>
|
||||
<g>
|
||||
<path d="M18 30H6V18H18V30Z" fill="var(--icon-weak-base)" />
|
||||
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="var(--icon-base)" />
|
||||
<path d="M48 30H36V18H48V30Z" fill="var(--icon-weak-base)" />
|
||||
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="var(--icon-base)" />
|
||||
<text
|
||||
x="66"
|
||||
y="32"
|
||||
font-size="28"
|
||||
font-weight="600"
|
||||
fill="var(--icon-strong-base)"
|
||||
font-family="var(--font-family-sans)"
|
||||
>
|
||||
Code
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
src={tfIcon}
|
||||
alt="ToothFairyAI"
|
||||
draggable={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user