35b4519b94
- fix(pwa): iOS keyboard gap caused by WebKit viewport-fit=cover bug. After keyboard open/close, 100dvh permanently shrinks. Track max innerHeight in --app-height CSS variable as stable replacement. - feat(pwa): auto-prompt notification permission on first login in standalone mode (once only, skips if denied). - refactor: remove duplicate notification toggle from header menu (already in Settings). - feat(dev): expose Vite dev server on network (host: true) for mobile testing via Tailscale. - docs: update README — add Task Progress FAB, fix notification flow description, document OPENAI_API_KEY / VAPID_EMAIL env vars, clarify voice input backends, add CLI --version/--help, update .env.example. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
49 lines
2.2 KiB
TypeScript
49 lines
2.2 KiB
TypeScript
/** Centralized localStorage key constants. All keys use the `clawtap:` prefix. */
|
|
export const STORAGE = {
|
|
TOKEN: 'clawtap:token',
|
|
ADAPTER: 'clawtap:adapter',
|
|
PROJECT_DIR: 'clawtap:projectDir',
|
|
DRAFT: 'clawtap:draft',
|
|
INSTALL_DISMISSED: 'clawtap:install-dismissed',
|
|
SESSIONS_TAB: 'clawtap:sessionsTab',
|
|
PUSH_PROMPTED: 'clawtap:push-prompted',
|
|
adapterPrefs: (id: string) => `clawtap:adapterPrefs:${id}` as const,
|
|
} as const;
|
|
|
|
/** One-time migration from old key names. Runs once before app mount. */
|
|
export function migrateStorageKeys(): void {
|
|
// Rename simple keys
|
|
for (const [oldKey, newKey] of [
|
|
['token', STORAGE.TOKEN],
|
|
['selectedProjectDir', STORAGE.PROJECT_DIR],
|
|
] as const) {
|
|
const val = localStorage.getItem(oldKey);
|
|
if (val !== null && localStorage.getItem(newKey) === null) {
|
|
localStorage.setItem(newKey, val);
|
|
localStorage.removeItem(oldKey);
|
|
}
|
|
}
|
|
|
|
// Migrate old global model/permissionMode/effort into per-adapter prefs
|
|
// Check both old bare keys AND clawtap:-prefixed keys (from intermediate migration)
|
|
const oldModel = localStorage.getItem('selectedModel') || localStorage.getItem('clawtap:model');
|
|
const oldMode = localStorage.getItem('permissionMode') || localStorage.getItem('clawtap:permissionMode');
|
|
const oldEffort = localStorage.getItem('effort') || localStorage.getItem('clawtap:effort');
|
|
if (oldModel || oldMode || oldEffort) {
|
|
const adapter = localStorage.getItem(STORAGE.ADAPTER) || 'claude';
|
|
const prefsKey = STORAGE.adapterPrefs(adapter);
|
|
let prefs: Record<string, string> = {};
|
|
try { prefs = JSON.parse(localStorage.getItem(prefsKey) || '{}'); } catch {}
|
|
if (oldModel && !prefs.model) prefs.model = oldModel;
|
|
if (oldMode && !prefs.permissionMode) prefs.permissionMode = oldMode;
|
|
if (oldEffort && !prefs.effort) prefs.effort = oldEffort;
|
|
localStorage.setItem(prefsKey, JSON.stringify(prefs));
|
|
localStorage.removeItem('selectedModel');
|
|
localStorage.removeItem('permissionMode');
|
|
localStorage.removeItem('effort');
|
|
localStorage.removeItem('clawtap:model');
|
|
localStorage.removeItem('clawtap:permissionMode');
|
|
localStorage.removeItem('clawtap:effort');
|
|
}
|
|
}
|