chore: prepare automation loop for Plan 2 (M8–M20)
- Add PLAN2.md with 116 tasks across M8–M20 with testing gates - Populate loop/plan.md with all new tasks (30-attempt retry policy) - Update loop/prompt.md to enforce glass design system and PLAN2.md - Fix PassphraseDialog to use glass-card/gradient-button design system - Fix crypto.randomUUID fallback for non-secure contexts - Fix dev.sh to skip proxy startup if port 3141 already in use - Fix PWA manifest for Android A2HS (purpose:any, display_override, id) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
69c32dce1c
commit
9f6158ddd2
@@ -18,10 +18,13 @@ trap cleanup EXIT INT TERM
|
||||
# Use local node_modules binaries
|
||||
BIN="$APP_DIR/node_modules/.bin"
|
||||
|
||||
# Start Claude proxy in background
|
||||
"$BIN/tsx" server/claude-proxy.ts &
|
||||
|
||||
sleep 0.3
|
||||
# Start Claude proxy in background (skip gracefully if already running on :3141)
|
||||
if lsof -ti:3141 > /dev/null 2>&1; then
|
||||
echo " Claude proxy already running on :3141 — skipping"
|
||||
else
|
||||
"$BIN/tsx" server/claude-proxy.ts &
|
||||
sleep 0.3
|
||||
fi
|
||||
|
||||
# Start Vite dev server in background
|
||||
"$BIN/vite" --host &
|
||||
|
||||
@@ -6,44 +6,40 @@
|
||||
>
|
||||
<!-- Backdrop -->
|
||||
<div
|
||||
class="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
||||
class="absolute inset-0 bg-black/70 backdrop-blur-sm"
|
||||
@click.self="handleSkip"
|
||||
/>
|
||||
|
||||
<!-- Dialog -->
|
||||
<div
|
||||
class="relative w-full max-w-sm rounded-2xl p-6 space-y-4 animate-scale-in"
|
||||
:class="isDark
|
||||
? 'bg-gray-900 border border-white/10'
|
||||
: 'bg-white border border-gray-200 shadow-xl'"
|
||||
>
|
||||
<h2
|
||||
class="text-lg font-bold"
|
||||
:class="isDark ? 'text-white/90' : 'text-gray-900'"
|
||||
>
|
||||
{{ isCreating ? 'Create Passphrase' : 'Enter Passphrase' }}
|
||||
</h2>
|
||||
<p
|
||||
class="text-xs"
|
||||
:class="isDark ? 'text-white/50' : 'text-gray-500'"
|
||||
>
|
||||
{{ isCreating
|
||||
? 'Set a passphrase to encrypt your data. You\'ll need it to access your conversations.'
|
||||
: 'Enter your passphrase to decrypt your data.'
|
||||
}}
|
||||
</p>
|
||||
<!-- Dialog — uses glass-card from design system -->
|
||||
<div class="glass-card relative w-full max-w-sm p-6 space-y-5 animate-scale-in">
|
||||
|
||||
<div class="space-y-3">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-xl bg-accent/15 border border-accent/30 shrink-0">
|
||||
<svg class="w-5 h-5 text-accent" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-base font-semibold text-white/96 leading-tight">
|
||||
{{ isCreating ? 'Create Passphrase' : 'Unlock AIUI' }}
|
||||
</h2>
|
||||
<p class="text-xs text-white/40 mt-0.5">
|
||||
{{ isCreating ? 'Encrypts all your data locally' : 'Enter your passphrase to decrypt' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inputs -->
|
||||
<div class="space-y-2.5">
|
||||
<input
|
||||
ref="inputRef"
|
||||
v-model="passphrase"
|
||||
type="password"
|
||||
:placeholder="isCreating ? 'Create a passphrase...' : 'Enter your passphrase...'"
|
||||
class="w-full px-4 py-3 rounded-xl text-sm outline-none transition-colors"
|
||||
:class="isDark
|
||||
? 'bg-white/5 text-white/90 placeholder:text-white/25 focus:bg-white/10 border border-white/10'
|
||||
: 'bg-gray-50 text-gray-800 placeholder:text-gray-400 focus:bg-gray-100 border border-gray-200'"
|
||||
:placeholder="isCreating ? 'Create a passphrase…' : 'Enter passphrase…'"
|
||||
class="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white/90 placeholder:text-white/25 outline-none focus:border-white/30 focus:bg-white/8 transition-colors"
|
||||
style="font-size: 16px"
|
||||
autocomplete="current-password"
|
||||
@keydown.enter="handleSubmit"
|
||||
/>
|
||||
|
||||
@@ -51,42 +47,39 @@
|
||||
v-if="isCreating"
|
||||
v-model="confirmPassphrase"
|
||||
type="password"
|
||||
placeholder="Confirm passphrase..."
|
||||
class="w-full px-4 py-3 rounded-xl text-sm outline-none transition-colors"
|
||||
:class="isDark
|
||||
? 'bg-white/5 text-white/90 placeholder:text-white/25 focus:bg-white/10 border border-white/10'
|
||||
: 'bg-gray-50 text-gray-800 placeholder:text-gray-400 focus:bg-gray-100 border border-gray-200'"
|
||||
placeholder="Confirm passphrase…"
|
||||
class="w-full px-4 py-3 rounded-xl bg-white/5 border border-white/10 text-white/90 placeholder:text-white/25 outline-none focus:border-white/30 focus:bg-white/8 transition-colors"
|
||||
style="font-size: 16px"
|
||||
autocomplete="new-password"
|
||||
@keydown.enter="handleSubmit"
|
||||
/>
|
||||
|
||||
<p
|
||||
v-if="errorMessage"
|
||||
class="text-xs text-red-400"
|
||||
>
|
||||
<p v-if="errorMessage" class="text-xs text-red-400 px-1">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="flex-1 py-2.5 rounded-xl text-sm font-medium transition-colors"
|
||||
:class="isDark
|
||||
? 'bg-white/5 text-white/50 hover:bg-white/10'
|
||||
: 'bg-gray-100 text-gray-500 hover:bg-gray-200'"
|
||||
class="glass-button glass-button-sm flex-1 rounded-xl text-sm text-white/50 hover:text-white/80 transition-colors"
|
||||
@click="handleSkip"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 py-2.5 rounded-xl text-sm font-medium transition-colors bg-accent text-white hover:bg-accent/90"
|
||||
class="gradient-button flex-1 h-10 rounded-xl text-sm font-medium text-white transition-opacity"
|
||||
:disabled="!canSubmit"
|
||||
:class="{ 'opacity-50 cursor-not-allowed': !canSubmit }"
|
||||
:class="{ 'opacity-40 cursor-not-allowed': !canSubmit }"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ isCreating ? 'Create' : 'Unlock' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-white/25 text-center">
|
||||
Data never leaves your device · AES-256-GCM
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
@@ -94,7 +87,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
@@ -106,7 +98,6 @@ const emit = defineEmits<{
|
||||
skip: []
|
||||
}>()
|
||||
|
||||
const { isDark } = useTheme()
|
||||
const passphrase = ref('')
|
||||
const confirmPassphrase = ref('')
|
||||
const errorMessage = ref('')
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import type { Message, Conversation, WebSearchResult } from '@aiui/core/types/message'
|
||||
|
||||
function generateId(): string {
|
||||
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
// Fallback for non-secure contexts (HTTP over network IP)
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0
|
||||
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
|
||||
})
|
||||
}
|
||||
import {
|
||||
saveConversation as idbSave,
|
||||
loadAllConversations as idbLoadAll,
|
||||
@@ -158,7 +169,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
)
|
||||
|
||||
function createConversation(title = 'New Chat'): string {
|
||||
const id = crypto.randomUUID()
|
||||
const id = generateId()
|
||||
const conversation: Conversation = {
|
||||
id,
|
||||
title,
|
||||
@@ -178,7 +189,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
|
||||
const msg: Message = {
|
||||
...message,
|
||||
id: crypto.randomUUID(),
|
||||
id: generateId(),
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
conv.messages.push(msg)
|
||||
|
||||
@@ -24,24 +24,29 @@ export default defineConfig({
|
||||
registerType: 'autoUpdate',
|
||||
includeAssets: ['favicon.svg', 'icon.svg', 'apple-touch-icon-180x180.png'],
|
||||
manifest: {
|
||||
id: '/',
|
||||
name: 'AIUI',
|
||||
short_name: 'AIUI',
|
||||
description: 'AI chat interface with rich content surfaces',
|
||||
theme_color: '#0a0a0a',
|
||||
background_color: '#0a0a0a',
|
||||
display: 'standalone',
|
||||
display_override: ['standalone', 'minimal-ui'],
|
||||
orientation: 'any',
|
||||
start_url: '/',
|
||||
scope: '/',
|
||||
icons: [
|
||||
{
|
||||
src: 'pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
|
||||
Reference in New Issue
Block a user