Add mode READMEs and move Bigscreen app
Add comprehensive READMEs for Bigscreen and Desktop modes, and relocate the Tauri frontend prototype into a new Bigscreen/ subdirectory. This moves package.json/package-lock, src, src-tauri, assets, views, styles and related files into Bigscreen/ to separate the controller-first shell from top-level docs. Update the root README.md to reframe the project as "NebulaOS", describe Bigscreen/Desktop modes, the vision, tech stack and development notes. This reorganizes the repo layout for clearer mode separation and documentation.
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
const SETTINGS_TEMPLATE = `
|
||||
<section class="view settings-view" data-view="settings">
|
||||
<header class="shell-topbar">
|
||||
<div class="shell-topbar-content">
|
||||
<p class="shell-brand">Nebula OS</p>
|
||||
<div class="shell-status">
|
||||
<span class="shell-avatar" aria-hidden="true"></span>
|
||||
<p class="shell-time" data-clock>--:--</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shell-accent-line"></div>
|
||||
</header>
|
||||
|
||||
<section class="settings-header-copy">
|
||||
<p class="muted">System</p>
|
||||
<h1 class="view-title">Settings</h1>
|
||||
</section>
|
||||
|
||||
<section class="settings-body" data-focus-root>
|
||||
<section class="settings-category-bar">
|
||||
<button class="focusable settings-category" data-focusable="true" data-row="0" data-col="0" data-cat="network" data-focus-key="network">Network</button>
|
||||
<button class="focusable settings-category" data-focusable="true" data-row="0" data-col="1" data-cat="audio" data-focus-key="audio">Audio</button>
|
||||
<button class="focusable settings-category" data-focusable="true" data-row="0" data-col="2" data-cat="display" data-focus-key="display">Display</button>
|
||||
<button class="focusable settings-category" data-focusable="true" data-row="0" data-col="3" data-cat="storage" data-focus-key="storage">Storage</button>
|
||||
<button class="focusable settings-category" data-focusable="true" data-row="0" data-col="4" data-cat="system" data-focus-key="system">System</button>
|
||||
</section>
|
||||
|
||||
<article class="panel settings-panel">
|
||||
<div class="settings-panel-head">
|
||||
<h2 class="settings-panel-title" data-panel-title>Network</h2>
|
||||
<p class="muted" data-panel-copy>Optimize connection and online routing.</p>
|
||||
</div>
|
||||
<div class="settings-card-grid">
|
||||
<button class="focusable settings-card" data-focusable="true" data-row="1" data-col="0" data-toggle="passkey-enabled" data-focus-key="passkey-enabled">
|
||||
<p class="settings-card-title">Passkey Lock</p>
|
||||
<p class="muted" data-passkey-enabled>Enabled</p>
|
||||
</button>
|
||||
<button class="focusable settings-card" data-focusable="true" data-row="1" data-col="1" data-toggle="passkey-change" data-focus-key="passkey-change">
|
||||
<p class="settings-card-title">Reset Passkey</p>
|
||||
<p class="muted" data-passkey-change>Clear and set a new passkey</p>
|
||||
</button>
|
||||
<button class="focusable settings-card" data-focusable="true" data-row="1" data-col="2" data-toggle="passkey-length" data-focus-key="passkey-length">
|
||||
<p class="settings-card-title">Required Length</p>
|
||||
<p class="muted" data-passkey-length>6 digits</p>
|
||||
<p class="muted">Fixed for testing</p>
|
||||
</button>
|
||||
<button class="focusable settings-card" data-focusable="true" data-row="1" data-col="3" data-toggle="passkey-confirm" data-focus-key="passkey-confirm">
|
||||
<p class="settings-card-title">Require Confirm</p>
|
||||
<p class="muted" data-passkey-confirm>Disabled</p>
|
||||
</button>
|
||||
<button class="focusable settings-card" data-focusable="true" data-row="1" data-col="4" data-toggle="passkey-keyboard" data-focus-key="passkey-keyboard">
|
||||
<p class="settings-card-title">Keyboard Support</p>
|
||||
<p class="muted" data-passkey-keyboard>Enabled</p>
|
||||
</button>
|
||||
<div class="settings-card settings-card-info">
|
||||
<p class="settings-card-title">Status</p>
|
||||
<p class="muted" data-status-note>Applying Nebula profile</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</section>
|
||||
`;
|
||||
|
||||
const CATEGORIES = {
|
||||
network: "Network",
|
||||
audio: "Audio",
|
||||
display: "Display",
|
||||
storage: "Storage",
|
||||
system: "System",
|
||||
};
|
||||
|
||||
export const createSettingsView = ({ state, renderView }) => {
|
||||
const updatePasskey = (partial) => {
|
||||
state.passkey.updateConfig(partial);
|
||||
refreshPanel();
|
||||
};
|
||||
|
||||
const refreshPanel = () => {
|
||||
const title = document.querySelector("[data-panel-title]");
|
||||
const copy = document.querySelector("[data-panel-copy]");
|
||||
const passkeyEnabled = document.querySelector("[data-passkey-enabled]");
|
||||
const passkeyLength = document.querySelector("[data-passkey-length]");
|
||||
const passkeyConfirm = document.querySelector("[data-passkey-confirm]");
|
||||
const passkeyKeyboard = document.querySelector("[data-passkey-keyboard]");
|
||||
const status = document.querySelector("[data-status-note]");
|
||||
const passkeyConfig = state.passkey.getConfig();
|
||||
|
||||
document.querySelectorAll(".settings-category").forEach((button) => {
|
||||
button.classList.toggle("is-active", button.dataset.cat === state.settingsCategory);
|
||||
});
|
||||
|
||||
if (title) {
|
||||
title.textContent = CATEGORIES[state.settingsCategory] ?? "Settings";
|
||||
}
|
||||
if (copy) {
|
||||
if (state.settingsCategory === "system") {
|
||||
copy.textContent = "Configure passkey security and controller login behavior.";
|
||||
} else {
|
||||
copy.textContent = `Tune ${CATEGORIES[state.settingsCategory] ?? "system"} options with controller-first cards.`;
|
||||
}
|
||||
}
|
||||
if (passkeyEnabled) {
|
||||
passkeyEnabled.textContent = passkeyConfig.enabled ? "Enabled" : "Disabled";
|
||||
}
|
||||
if (passkeyLength) {
|
||||
passkeyLength.textContent = `${passkeyConfig.length} digits`;
|
||||
}
|
||||
if (passkeyConfirm) {
|
||||
passkeyConfirm.textContent = passkeyConfig.requireConfirm ? "Enabled" : "Disabled";
|
||||
}
|
||||
if (passkeyKeyboard) {
|
||||
passkeyKeyboard.textContent = passkeyConfig.keyboardSupport ? "Enabled" : "Disabled";
|
||||
}
|
||||
if (status) {
|
||||
status.textContent = state.settingsCategory === "system"
|
||||
? `Attempts: ${passkeyConfig.maxAttempts}, cooldown: ${passkeyConfig.cooldownSeconds}s`
|
||||
: `${CATEGORIES[state.settingsCategory] ?? "System"} profile synced`;
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-toggle^='passkey']").forEach((button) => {
|
||||
button.classList.toggle("is-disabled", state.settingsCategory !== "system");
|
||||
button.setAttribute("aria-disabled", state.settingsCategory === "system" ? "false" : "true");
|
||||
});
|
||||
};
|
||||
|
||||
const toggleCurrent = (suffix = "") => {
|
||||
const key = suffix ? `${state.settingsCategory}_${suffix}` : state.settingsCategory;
|
||||
state.settingsValues[key] = !Boolean(state.settingsValues[key]);
|
||||
refreshPanel();
|
||||
};
|
||||
|
||||
return {
|
||||
id: "settings",
|
||||
render: () => SETTINGS_TEMPLATE,
|
||||
mount: () => {
|
||||
const root = document.querySelector("[data-focus-root]");
|
||||
root?.addEventListener("focusin", (event) => {
|
||||
const focused = event.target.closest("[data-focusable='true']");
|
||||
const col = Number(focused?.dataset.col ?? 0);
|
||||
document.documentElement.style.setProperty("--nebula-accent-line-x", `${24 + col * 110}px`);
|
||||
});
|
||||
refreshPanel();
|
||||
document.documentElement.style.setProperty("--nebula-focus-strength", "1");
|
||||
},
|
||||
getNavigationContract: () => {
|
||||
const root = document.querySelector("[data-focus-root]");
|
||||
return {
|
||||
focusRoot: root,
|
||||
defaultFocus: root?.querySelector("[data-cat='network']") ?? null,
|
||||
layout: { type: "grid", cols: 5, rows: 2 },
|
||||
hintsTemplate: "#global-hints-template",
|
||||
nebulaNavigation: state.nebula.navigation,
|
||||
onAccept: (element) => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const category = element.dataset.cat;
|
||||
const toggle = element.dataset.toggle;
|
||||
|
||||
if (category) {
|
||||
state.settingsCategory = category;
|
||||
refreshPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "primary") {
|
||||
toggleCurrent();
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "secondary") {
|
||||
toggleCurrent("secondary");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.settingsCategory !== "system") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "passkey-enabled") {
|
||||
updatePasskey({ enabled: !state.passkey.getConfig().enabled });
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "passkey-change") {
|
||||
state.passkey.resetSequence();
|
||||
state.passkeySetupRequired = true;
|
||||
state.locked = true;
|
||||
state.activeView = "lock";
|
||||
renderView("lock");
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "passkey-length") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "passkey-confirm") {
|
||||
updatePasskey({ requireConfirm: !state.passkey.getConfig().requireConfirm });
|
||||
return;
|
||||
}
|
||||
|
||||
if (toggle === "passkey-keyboard") {
|
||||
updatePasskey({ keyboardSupport: !state.passkey.getConfig().keyboardSupport });
|
||||
}
|
||||
},
|
||||
onBack: () => {
|
||||
state.activeView = "home";
|
||||
renderView("home");
|
||||
},
|
||||
onMenu: () => {},
|
||||
onAction: (action, element) => {
|
||||
if (state.settingsCategory !== "system") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (element?.dataset.toggle !== "passkey-length") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user