feat(content): comprehensive extraction hardening, code mode UI, overnight plan
- Relax isBookLikeResponse threshold (>=2 to >=1) - Widen book patterns: inline prose, verb-preceded, numbered bold, em-dash - Remove overly aggressive film/song gating on book extraction - Allow songs to coexist with film/book tags (explicit tags always returned) - Strengthen TV patterns: seasons, created by, standalone "tv" query match - Fix TV_EXT_RE to handle both Title|Year|Creator and Title|Creator|Year - Widen place patterns: type word search in descriptions, bold fallback - Add "pizza" to isPlaceQuery and preferredFirstTab - Add Code tab: isCodeQuery, isCodeLikeResponse, extractCodeBlocks, wiring - Fix image threshold: single image with meaningful alt text shown - Refactor filterTabsByContext: specialized paths now append remaining content - Add code mode UI: orange input styling, design system selection, file selection - DesignSystemGrid: selection toggle only on checkmark, card click opens detail - Add 64-test extraction quality test suite - Update overnight plan.md and prompt.md for hardening run Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
75e9274582
commit
f0eb4383bd
@@ -77,8 +77,13 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="path-glass-bubble rounded-2xl px-4 py-3 flex items-end gap-2 transition-all duration-300"
|
||||
:class="focused ? 'border-white/30' : ''"
|
||||
class="rounded-2xl px-4 py-3 flex items-end gap-2 transition-all duration-300"
|
||||
:class="[
|
||||
isCodeMode
|
||||
? 'bg-accent/15 border border-accent/25 backdrop-blur-xl'
|
||||
: 'path-glass-bubble',
|
||||
focused ? (isCodeMode ? 'border-accent/40' : 'border-white/30') : '',
|
||||
]"
|
||||
>
|
||||
<!-- Image attach button -->
|
||||
<button
|
||||
@@ -131,10 +136,14 @@
|
||||
<!-- Send button -->
|
||||
<button
|
||||
:disabled="!canSend"
|
||||
class="shrink-0 path-glass-button path-glass-button-sm rounded-xl px-3 transition-all duration-200"
|
||||
:class="canSend
|
||||
? 'hover:opacity-80 active:scale-95'
|
||||
: 'opacity-30 cursor-not-allowed'"
|
||||
class="shrink-0 rounded-xl px-3 transition-all duration-200"
|
||||
:class="[
|
||||
isCodeMode ? 'bg-accent/80 text-white' : 'path-glass-button path-glass-button-sm',
|
||||
canSend
|
||||
? 'hover:opacity-80 active:scale-95'
|
||||
: 'opacity-30 cursor-not-allowed',
|
||||
]"
|
||||
:style="isCodeMode ? 'height: 32px' : ''"
|
||||
aria-label="Send message"
|
||||
@click="send"
|
||||
>
|
||||
@@ -172,16 +181,20 @@ const props = withDefaults(
|
||||
disabled?: boolean
|
||||
streaming?: boolean
|
||||
placeholder?: string
|
||||
activeTab?: string
|
||||
replyTo?: { messageId: string; excerpt: string } | null
|
||||
}>(),
|
||||
{
|
||||
disabled: false,
|
||||
streaming: false,
|
||||
placeholder: 'Message AIUI...',
|
||||
activeTab: '',
|
||||
replyTo: null,
|
||||
}
|
||||
)
|
||||
|
||||
const isCodeMode = computed(() => props.activeTab === 'code')
|
||||
|
||||
const emit = defineEmits<{
|
||||
send: [text: string, images: ImageAttachment[]]
|
||||
extract: [text: string]
|
||||
|
||||
@@ -106,7 +106,8 @@
|
||||
<ChatInput
|
||||
:disabled="isStreaming || comparison.isAnyStreaming.value"
|
||||
:streaming="isStreaming || comparison.isAnyStreaming.value"
|
||||
:placeholder="isStreaming ? 'Waiting for response...' : 'Message AIUI...'"
|
||||
:placeholder="activeTab === 'code' ? 'Code...' : isStreaming ? 'Waiting for response...' : 'Message AIUI...'"
|
||||
:active-tab="activeTab"
|
||||
:reply-to="replyTo"
|
||||
@send="handleSend"
|
||||
@extract="handleExtract"
|
||||
|
||||
@@ -87,6 +87,7 @@ const promptPairs = computed<PromptPair[]>(() => {
|
||||
if (content.magazineSections.length > 0) badges.push('Magazine')
|
||||
if ((content.newsLinks?.length ?? 0) > 0) badges.push('News')
|
||||
if ((content.websitesLinks?.length ?? 0) > 0) badges.push('Web')
|
||||
if ((content.codeBlocks?.length ?? 0) > 0) badges.push('Code')
|
||||
if ((content.apps?.length ?? 0) > 0) badges.push('Apps')
|
||||
if (content.hasNostr) badges.push('Nostr')
|
||||
}
|
||||
|
||||
@@ -37,12 +37,29 @@
|
||||
<button
|
||||
v-for="item in filteredItems"
|
||||
:key="item.id"
|
||||
class="text-left p-3 rounded-xl transition-colors group cursor-pointer"
|
||||
:class="isDark
|
||||
? 'bg-white/[0.03] hover:bg-white/[0.07]'
|
||||
: 'bg-black/[0.02] hover:bg-black/[0.05]'"
|
||||
class="text-left p-3 rounded-xl transition-all duration-150 group relative"
|
||||
:class="[
|
||||
codeMode && isDesignTokenSelected(item.id)
|
||||
? 'ring-2 ring-accent/50 bg-accent/10 cursor-pointer'
|
||||
: isDark
|
||||
? 'bg-white/[0.03] hover:bg-white/[0.07] cursor-pointer'
|
||||
: 'bg-black/[0.02] hover:bg-black/[0.05] cursor-pointer',
|
||||
]"
|
||||
@click="selectItem(item)"
|
||||
>
|
||||
<!-- Selection toggle (top-right) — only this area toggles context selection -->
|
||||
<div
|
||||
v-if="codeMode"
|
||||
class="absolute top-2 right-2 w-5 h-5 rounded-full flex items-center justify-center z-10 cursor-pointer transition-colors"
|
||||
:class="isDesignTokenSelected(item.id)
|
||||
? 'bg-accent'
|
||||
: isDark ? 'bg-white/10 hover:bg-white/20' : 'bg-black/10 hover:bg-black/20'"
|
||||
@click.stop="toggleDesignToken(item.id)"
|
||||
>
|
||||
<svg v-if="isDesignTokenSelected(item.id)" class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- Preview swatch for colors -->
|
||||
<div v-if="item.category === 'colors' && item.preview === 'inline'"
|
||||
class="h-8 rounded-md mb-2 border"
|
||||
@@ -92,9 +109,11 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { useContentPanel, type DesignSystemItem } from '@/composables/useContentPanel'
|
||||
import { useCodeContext } from '@/composables/useCodeContext'
|
||||
|
||||
const { isDark } = useTheme()
|
||||
const { openDesignSystemItem } = useContentPanel()
|
||||
const { codeMode, toggleDesignToken, isDesignTokenSelected } = useCodeContext()
|
||||
|
||||
const activeCategory = ref<string>('all')
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
<button
|
||||
class="w-full text-left flex items-center gap-1.5 py-1 px-2 rounded-lg text-xs transition-colors"
|
||||
:class="[
|
||||
isActive
|
||||
? isDark ? 'bg-white/10 text-white/90' : 'bg-black/8 text-gray-900'
|
||||
: isDark ? 'text-white/60 hover:bg-white/[0.04] hover:text-white/80' : 'text-gray-600 hover:bg-black/[0.03] hover:text-gray-800',
|
||||
isSelected
|
||||
? 'bg-accent/15 text-accent ring-1 ring-accent/30'
|
||||
: isActive
|
||||
? isDark ? 'bg-white/10 text-white/90' : 'bg-black/8 text-gray-900'
|
||||
: isDark ? 'text-white/60 hover:bg-white/[0.04] hover:text-white/80' : 'text-gray-600 hover:bg-black/[0.03] hover:text-gray-800',
|
||||
]"
|
||||
:style="{ paddingLeft: `${depth * 12 + 8}px` }"
|
||||
@click="handleClick"
|
||||
@@ -33,6 +35,9 @@
|
||||
</svg>
|
||||
|
||||
<span class="truncate">{{ entry.name }}</span>
|
||||
<svg v-if="isSelected" class="w-3 h-3 shrink-0 text-accent ml-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Children (when expanded) -->
|
||||
@@ -52,7 +57,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import type { FileEntry } from '@/composables/useCodeContext'
|
||||
import { useCodeContext, type FileEntry } from '@/composables/useCodeContext'
|
||||
|
||||
const props = defineProps<{
|
||||
entry: FileEntry
|
||||
@@ -60,6 +65,8 @@ const props = defineProps<{
|
||||
depth: number
|
||||
}>()
|
||||
|
||||
const { isFileSelected } = useCodeContext()
|
||||
|
||||
const emit = defineEmits<{
|
||||
select: [path: string]
|
||||
}>()
|
||||
@@ -68,6 +75,7 @@ const { isDark } = useTheme()
|
||||
const expanded = ref(props.depth < 1) // Auto-expand first level
|
||||
|
||||
const isActive = computed(() => !props.entry.isDirectory && props.activeFile === props.entry.path)
|
||||
const isSelected = computed(() => !props.entry.isDirectory && isFileSelected(props.entry.path))
|
||||
|
||||
function handleClick() {
|
||||
if (props.entry.isDirectory) {
|
||||
|
||||
@@ -172,10 +172,14 @@ const {
|
||||
activeProject,
|
||||
fileTree,
|
||||
activeFile,
|
||||
codeMode,
|
||||
selectedFiles,
|
||||
selectProject: doSelectProject,
|
||||
openFile,
|
||||
createProject,
|
||||
clearActiveFile,
|
||||
toggleFileSelection,
|
||||
isFileSelected,
|
||||
} = useCodeContext()
|
||||
|
||||
const search = ref('')
|
||||
@@ -215,6 +219,7 @@ function backToProjects() {
|
||||
}
|
||||
|
||||
function handleFileSelect(filePath: string) {
|
||||
toggleFileSelection(filePath)
|
||||
openFile(filePath)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user