feat: plan-approval overlay, auth-expiry handling, cert download, login reset
- Add a dedicated plan-approval bottom sheet (interactive-prompt) driven by ExitPlanMode's now-4-option layout (Claude Code v2.1.177), with approve/YOLO/reject/feedback wired through respondPlan(requestId). - PermissionManager: stop auto-expiring AskUserQuestion/plan prompts and stop releasing pending prompts on client disconnect — they reflect real CLI state and must survive reconnects. - Detect server-rejected tokens (401/403 or failed WS handshake) and bounce back to the login screen via a new AUTH_EXPIRED_EVENT. - Add `/cert` endpoint + Settings entry so phones can download/trust the self-signed HTTPS cert directly; fix cert IP detection on non-macOS hosts. - Add `clawtap reset-login` command and `/api/auth/reset-attempts` endpoint to clear login rate-limit lockouts. - Codex adapter: only forward recognized keystrokes/option indices to tmux, never the synthetic "deny" dismissal signal. - PWA: actively poll for service-worker updates (iOS standalone apps don't reliably check on navigation). - update-service.sh: install tmux if missing, prompt to create the env file instead of failing, add --skip-firewall, and improve logging.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ChevronLeft, ChevronRight, ClipboardList, Bell, Info } from 'lucide-react';
|
||||
import { ChevronLeft, ChevronRight, ClipboardList, Bell, Info, ShieldCheck } from 'lucide-react';
|
||||
import { api } from '@/lib/api';
|
||||
import { AdapterIcon } from './AdapterIcon';
|
||||
import { SavedInstructionsView } from './SavedInstructionsView';
|
||||
@@ -16,6 +16,7 @@ export function SettingsView({ onBack }: { onBack: () => void }) {
|
||||
const [subView, setSubView] = useState<'main' | 'instructions' | string>('main');
|
||||
const [adapters, setAdapters] = useState<Adapter[]>([]);
|
||||
const [version, setVersion] = useState<string>('');
|
||||
const [certAvailable, setCertAvailable] = useState(false);
|
||||
const { supported, subscribed, subscribe, unsubscribe } = usePushNotifications();
|
||||
const [toggling, setToggling] = useState(false);
|
||||
|
||||
@@ -25,6 +26,9 @@ export function SettingsView({ onBack }: { onBack: () => void }) {
|
||||
.then(r => r.json())
|
||||
.then((data: { version: string }) => setVersion(data.version))
|
||||
.catch(() => {});
|
||||
fetch('/cert', { method: 'HEAD' })
|
||||
.then(r => setCertAvailable(r.ok))
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
if (subView === 'instructions') {
|
||||
@@ -87,6 +91,29 @@ export function SettingsView({ onBack }: { onBack: () => void }) {
|
||||
</button>
|
||||
))}
|
||||
|
||||
{/* Install HTTPS certificate (only relevant when serving over a self-signed cert) */}
|
||||
{window.location.protocol === 'https:' && (
|
||||
certAvailable ? (
|
||||
<a
|
||||
href="/cert"
|
||||
download
|
||||
className="w-full flex items-center px-4 min-h-[48px] border-b border-border hover:bg-surface-hover transition-colors"
|
||||
>
|
||||
<span className="mr-3"><ShieldCheck className="w-5 h-5 text-text-dim" /></span>
|
||||
<span className="flex-1 text-left text-text text-sm">Install HTTPS Certificate</span>
|
||||
<ChevronRight className="w-4 h-4 text-text-dim" />
|
||||
</a>
|
||||
) : (
|
||||
<div className="w-full flex items-center px-4 min-h-[48px] border-b border-border">
|
||||
<span className="mr-3"><ShieldCheck className="w-5 h-5 text-text-dim" /></span>
|
||||
<div className="flex-1 text-left">
|
||||
<div className="text-text text-sm">Install HTTPS Certificate</div>
|
||||
<div className="text-text-dim text-xs font-mono">No cert found — run `clawtap cert` on the server</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Notifications */}
|
||||
{supported && (
|
||||
<div className="w-full flex items-center px-4 min-h-[48px] border-b border-border">
|
||||
|
||||
Reference in New Issue
Block a user