Files
clawtap/src/lib/storage-keys.ts
T
kuannnn 35b4519b94 chore: release 0.3.1 — iOS PWA fixes, notification improvements, docs update
- 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>
2026-03-30 05:58:56 +08:00

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');
}
}