feat(fusion): add Global → General settings pane and wire fn --version
Move the Star-on-GitHub toggle, CLI Binary panel, and update-check controls out of project General into a new Global → General pane (with an inline Updates subsection), matching their actual scope. Drop the CLI Binary panel's outlined card background and apply the standard horizontal indent so it doesn't bleed to the pane edges. Wire --version / -v in the fn/fusion bin so the dashboard's CLI Binary probe gets a real version string instead of booting the dashboard and reporting "unknown". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
9
.changeset/global-general-settings-pane.md
Normal file
9
.changeset/global-general-settings-pane.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Split the Star-on-GitHub toggle, CLI Binary panel, and update-check controls into a new **Global → General** settings pane (with an inline **Updates** subsection), separate from the project-scoped Project → General pane. All three are global by nature, and grouping them under Global avoids the impression that they apply only to the active project. The standalone Global → Updates entry has been folded into this pane.
|
||||
|
||||
The CLI Binary panel also drops its own outlined card background and adopts the standard `padding: 0 var(--space-xl)` indent every other top-level child of `.settings-content` uses, so it sits flush with adjacent form groups instead of bleeding to the pane edges.
|
||||
|
||||
Wire `--version` / `-v` in the `fn` / `fusion` bin so it prints the package version and exits before falling through to the default `dashboard` command. Without this, the dashboard's CLI Binary panel reported the installed version as "unknown" because its `<bin> --version` probe was booting the full server instead of getting a version string.
|
||||
@@ -13,9 +13,10 @@
|
||||
*/
|
||||
import { existsSync, mkdtempSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { join, dirname } from "node:path";
|
||||
import { join, dirname, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// @ts-expect-error -- Bun-only global; undefined in Node
|
||||
const isBunBinary = typeof Bun !== "undefined" && !!Bun.embeddedFiles;
|
||||
@@ -412,9 +413,53 @@ function getFlagValueNumber(args: string[], flag: string): number | undefined {
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate `@runfusion/fusion`'s own version by walking up from the running
|
||||
* `bin.js`. Mirrors `packages/dashboard/src/cli-package-version.ts` but is
|
||||
* inlined here to avoid pulling the dashboard barrel into the bin's static
|
||||
* import graph (bin keeps app imports dynamic until env bootstrap is done).
|
||||
*/
|
||||
function readOwnCliVersion(): string | undefined {
|
||||
let currentDir: string;
|
||||
try {
|
||||
currentDir = dirname(fileURLToPath(import.meta.url));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
for (let i = 0; i < 8; i += 1) {
|
||||
const pkgPath = resolve(currentDir, "package.json");
|
||||
if (existsSync(pkgPath)) {
|
||||
try {
|
||||
const parsed = JSON.parse(readFileSync(pkgPath, "utf-8")) as {
|
||||
name?: string;
|
||||
version?: string;
|
||||
};
|
||||
if (parsed.name === "@runfusion/fusion" && typeof parsed.version === "string") {
|
||||
return parsed.version;
|
||||
}
|
||||
} catch {
|
||||
// Ignore malformed manifest and keep walking.
|
||||
}
|
||||
}
|
||||
const parentDir = resolve(currentDir, "..");
|
||||
if (parentDir === currentDir) break;
|
||||
currentDir = parentDir;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { cleanedArgs: args, projectName } = extractGlobalProjectFlag(process.argv.slice(2));
|
||||
|
||||
// Print version and exit before any application imports. This is what the
|
||||
// dashboard's CLI Binary panel probes via `<bin> --version`; without an
|
||||
// early exit, the flag falls through to the default `dashboard` command and
|
||||
// boots the full server.
|
||||
if (args.includes("--version") || args.includes("-v")) {
|
||||
console.log(readOwnCliVersion() ?? "unknown");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (args.includes("--help") || args.includes("-h")) {
|
||||
console.log(HELP);
|
||||
process.exit(0);
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
|
||||
border-radius: 8px;
|
||||
background: var(--surface-2, rgba(255, 255, 255, 0.02));
|
||||
/* Match the horizontal indent every other top-level child of
|
||||
.settings-content uses (.form-group, .settings-section-heading, etc.).
|
||||
Top margin matches .form-group so consecutive sections breathe the same. */
|
||||
padding: 0 var(--space-xl);
|
||||
margin-top: var(--space-lg);
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.cli-binary-header {
|
||||
|
||||
@@ -179,12 +179,12 @@ const SETTINGS_SECTIONS: SettingsSection[] = [
|
||||
|
||||
// Global group (shared across all Fusion projects)
|
||||
{ id: "__global_header", label: "Global", scope: undefined, isGroupHeader: true },
|
||||
{ id: "global-general", label: "General", scope: "global" },
|
||||
{ id: "appearance", label: "Appearance", scope: "global" },
|
||||
{ id: "notifications", label: "Notifications", scope: "global" },
|
||||
{ id: "node-sync", label: "Node Sync", scope: "global" },
|
||||
{ id: "global-models", label: "Models", scope: "global" },
|
||||
{ id: "research-global", label: "Research Defaults", scope: "global" },
|
||||
{ id: "updates", label: "Updates", scope: "global" },
|
||||
{ id: "experimental", label: "Experimental Features", scope: "global" },
|
||||
{ id: "remote", label: "Remote Access", scope: "global" },
|
||||
|
||||
@@ -1818,6 +1818,13 @@ export function SettingsModal({
|
||||
</label>
|
||||
<small>Show the floating chat button in the dashboard. Chat is still accessible from the Chat tab in the mobile navigation.</small>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case "global-general":
|
||||
return (
|
||||
<>
|
||||
{renderScopeBanner()}
|
||||
<h4 className="settings-section-heading">General</h4>
|
||||
<div className="form-group">
|
||||
<label htmlFor="showGitHubStarButton" className="checkbox-label">
|
||||
<input
|
||||
@@ -1836,6 +1843,53 @@ export function SettingsModal({
|
||||
</small>
|
||||
</div>
|
||||
<CliBinaryPanel />
|
||||
<h4 className="settings-section-heading settings-section-heading--spaced">Updates</h4>
|
||||
<div className="form-group">
|
||||
<label htmlFor="updateCheckEnabled" className="checkbox-label">
|
||||
<input
|
||||
id="updateCheckEnabled"
|
||||
type="checkbox"
|
||||
checked={form.updateCheckEnabled !== false}
|
||||
onChange={(e) =>
|
||||
setForm((f) => ({ ...f, updateCheckEnabled: e.target.checked }))
|
||||
}
|
||||
/>
|
||||
Check for updates automatically
|
||||
</label>
|
||||
<small>
|
||||
When enabled, Fusion checks npm for new versions of{" "}
|
||||
<code>@runfusion/fusion</code> and shows update notices in the CLI and dashboard.
|
||||
Cadence is governed by the frequency below.
|
||||
</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="updateCheckFrequency">Frequency</label>
|
||||
<select
|
||||
id="updateCheckFrequency"
|
||||
value={form.updateCheckFrequency ?? "daily"}
|
||||
onChange={(e) =>
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
updateCheckFrequency: e.target.value as
|
||||
| "manual"
|
||||
| "on-startup"
|
||||
| "daily"
|
||||
| "weekly",
|
||||
}))
|
||||
}
|
||||
disabled={form.updateCheckEnabled === false}
|
||||
>
|
||||
<option value="manual">Manual only — never auto-check</option>
|
||||
<option value="on-startup">On startup — once per server launch</option>
|
||||
<option value="daily">Daily (recommended)</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
</select>
|
||||
<small>
|
||||
Controls how often the dashboard re-fetches the npm registry.
|
||||
Use the version + refresh control in the header to trigger an
|
||||
immediate check at any time.
|
||||
</small>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case "global-models": {
|
||||
@@ -2017,61 +2071,6 @@ export function SettingsModal({
|
||||
);
|
||||
}
|
||||
|
||||
case "updates": {
|
||||
return (
|
||||
<>
|
||||
{renderScopeBanner()}
|
||||
<h4 className="settings-section-heading">Updates</h4>
|
||||
<div className="form-group">
|
||||
<label htmlFor="updateCheckEnabled" className="checkbox-label">
|
||||
<input
|
||||
id="updateCheckEnabled"
|
||||
type="checkbox"
|
||||
checked={form.updateCheckEnabled !== false}
|
||||
onChange={(e) =>
|
||||
setForm((f) => ({ ...f, updateCheckEnabled: e.target.checked }))
|
||||
}
|
||||
/>
|
||||
Check for updates automatically
|
||||
</label>
|
||||
<small>
|
||||
When enabled, Fusion checks npm for new versions of{" "}
|
||||
<code>@runfusion/fusion</code> and shows update notices in the CLI and dashboard.
|
||||
Cadence is governed by the frequency below.
|
||||
</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="updateCheckFrequency">Frequency</label>
|
||||
<select
|
||||
id="updateCheckFrequency"
|
||||
value={form.updateCheckFrequency ?? "daily"}
|
||||
onChange={(e) =>
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
updateCheckFrequency: e.target.value as
|
||||
| "manual"
|
||||
| "on-startup"
|
||||
| "daily"
|
||||
| "weekly",
|
||||
}))
|
||||
}
|
||||
disabled={form.updateCheckEnabled === false}
|
||||
>
|
||||
<option value="manual">Manual only — never auto-check</option>
|
||||
<option value="on-startup">On startup — once per server launch</option>
|
||||
<option value="daily">Daily (recommended)</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
</select>
|
||||
<small>
|
||||
Controls how often the dashboard re-fetches the npm registry.
|
||||
Use the version + refresh control in the header to trigger an
|
||||
immediate check at any time.
|
||||
</small>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
case "project-models": {
|
||||
const presets = form.modelPresets || [];
|
||||
const presetOptions = presets.map((preset) => ({ id: preset.id, name: preset.name }));
|
||||
|
||||
Reference in New Issue
Block a user