Merge pull request 'ai-enhancement' (#23) from ai-enhancement into main
Reviewed-on: mstoeck3/cc24-hub#23
This commit is contained in:
commit
0327f9cea8
File diff suppressed because it is too large
Load Diff
@ -878,23 +878,33 @@ tools:
|
||||
- reporting
|
||||
- data-processing
|
||||
- scripting
|
||||
- name: "Android Logical Imaging"
|
||||
icon: "📋"
|
||||
type: "method"
|
||||
description: "Es gibt immer wieder auch Fälle, wo man nicht allermodernste Mobilgeräte knacken muss - der Großvater, der im Nachlass ein altes Samsung mit wichtigen Daten hinterlassen hat.
|
||||
Es muss in diesn Fällen nicht der teure Hersteller aus Israel sein. Die Androis-ADB-Shell bietet genug Möglichkeiten, auch ohne viel Geld auszugeben an das logische Dateisystem zu gelangen.
|
||||
Die Erfolgsaussichten sinken jedoch massiv bei neuren Geräten."
|
||||
domains: ["mobile-forensics"]
|
||||
phases: ["data-collection"]
|
||||
platforms: []
|
||||
skillLevel: "advanced"
|
||||
- name: Android Logical Imaging
|
||||
icon: 📋
|
||||
type: method
|
||||
description: >-
|
||||
Es gibt immer wieder auch Fälle, wo man nicht allermodernste Mobilgeräte
|
||||
knacken muss - der Großvater, der im Nachlass ein altes Samsung mit
|
||||
wichtigen Daten hinterlassen hat. Es muss in diesn Fällen nicht der teure
|
||||
Hersteller aus Israel sein. Die Androis-ADB-Shell bietet genug
|
||||
Möglichkeiten, auch ohne viel Geld auszugeben an das logische Dateisystem
|
||||
zu gelangen. Die Erfolgsaussichten sinken jedoch massiv bei neuren
|
||||
Geräten.
|
||||
domains:
|
||||
- mobile-forensics
|
||||
phases:
|
||||
- data-collection
|
||||
platforms: []
|
||||
skillLevel: advanced
|
||||
accessType: null
|
||||
url: "https://claude.ai/public/artifacts/66785e1f-62bb-4eb9-9269-b08648161742"
|
||||
url: https://claude.ai/public/artifacts/66785e1f-62bb-4eb9-9269-b08648161742
|
||||
projectUrl: null
|
||||
license: null
|
||||
knowledgebase: true
|
||||
related_concepts: null
|
||||
tags: ["imaging", "filesystem", "hardware-interface"]
|
||||
tags:
|
||||
- imaging
|
||||
- filesystem
|
||||
- hardware-interface
|
||||
- name: Microsoft Office 365
|
||||
type: software
|
||||
description: >-
|
||||
@ -2081,6 +2091,29 @@ tools:
|
||||
- evidence-preservation
|
||||
- malware-identification
|
||||
- chain-of-custody
|
||||
- name: MSAB XRY
|
||||
type: software
|
||||
description: >-
|
||||
Die Smartphone-Extraktionssoftware der Firma MSAB positioniert sich als
|
||||
Konkurrenz zum Marktführer Cellebrite. MSAB wirbt mit dem Imaging selbst
|
||||
neuester Android- und IOS-Geräte.
|
||||
skillLevel: beginner
|
||||
url: https://www.msab.com/product/xry-extract/
|
||||
icon: 📦
|
||||
domains:
|
||||
- mobile-forensics
|
||||
phases:
|
||||
- data-collection
|
||||
tags:
|
||||
- law-enforcement
|
||||
- fast
|
||||
- SIM
|
||||
- image-recognition
|
||||
platforms:
|
||||
- Windows
|
||||
accessType: download
|
||||
license: Proprietary
|
||||
knowledgebase: false
|
||||
domains:
|
||||
- id: incident-response
|
||||
name: Incident Response & Breach-Untersuchung
|
||||
|
186
src/pages/api/ai/enhance-input.ts
Normal file
186
src/pages/api/ai/enhance-input.ts
Normal file
@ -0,0 +1,186 @@
|
||||
// src/pages/api/ai/enhance-input.ts
|
||||
import type { APIRoute } from 'astro';
|
||||
import { withAPIAuth } from '../../../utils/auth.js';
|
||||
import { apiError, apiServerError, createAuthErrorResponse } from '../../../utils/api.js';
|
||||
import { enqueueApiCall } from '../../../utils/rateLimitedQueue.js';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
function getEnv(key: string): string {
|
||||
const value = process.env[key];
|
||||
if (!value) {
|
||||
throw new Error(`Missing environment variable: ${key}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const AI_MODEL = getEnv('AI_MODEL');
|
||||
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
|
||||
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
|
||||
const RATE_LIMIT_MAX = 5; // 5 enhancement requests per minute per user
|
||||
|
||||
function sanitizeInput(input: string): string {
|
||||
return input
|
||||
.replace(/```[\s\S]*?```/g, '[CODE_BLOCK_REMOVED]')
|
||||
.replace(/\<\/?[^>]+(>|$)/g, '')
|
||||
.replace(/\b(system|assistant|user)\s*[:]/gi, '[ROLE_REMOVED]')
|
||||
.replace(/\b(ignore|forget|disregard)\s+(previous|all|your)\s+(instructions?|context|rules?)/gi, '[INSTRUCTION_REMOVED]')
|
||||
.trim()
|
||||
.slice(0, 1000); // Shorter limit for enhancement
|
||||
}
|
||||
|
||||
function checkRateLimit(userId: string): boolean {
|
||||
const now = Date.now();
|
||||
const userLimit = rateLimitStore.get(userId);
|
||||
|
||||
if (!userLimit || now > userLimit.resetTime) {
|
||||
rateLimitStore.set(userId, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userLimit.count >= RATE_LIMIT_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
userLimit.count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
function cleanupExpiredRateLimits() {
|
||||
const now = Date.now();
|
||||
for (const [userId, limit] of rateLimitStore.entries()) {
|
||||
if (now > limit.resetTime) {
|
||||
rateLimitStore.delete(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up expired limits every 5 minutes
|
||||
setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000);
|
||||
|
||||
function createEnhancementPrompt(input: string): string {
|
||||
return `
|
||||
Du bist eine KI für digitale Forensik. Der Nutzer beschreibt ein forensisches Szenario. Analysiere die Eingabe.
|
||||
|
||||
Wenn die Beschreibung unvollständig oder vage ist, stelle bis zu drei präzise Rückfragen im JSON-Array-Format, um wichtige Details zu klären (z. B. Vorfalltyp, System, Ziel, Datenquellen, Zeit, Beteiligte, rechtlicher Rahmen).
|
||||
|
||||
Wenn die Eingabe bereits klar, spezifisch und vollständig ist, gib stattdessen nur eine leere Liste [] zurück.
|
||||
|
||||
Antwortformat strikt:
|
||||
|
||||
\`\`\`json
|
||||
[
|
||||
"Frage 1?",
|
||||
"Frage 2?",
|
||||
"Frage 3?"
|
||||
]
|
||||
\`\`\`
|
||||
|
||||
Nutzer-Eingabe:
|
||||
${input}
|
||||
`.trim();
|
||||
}
|
||||
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
const authResult = await withAPIAuth(request, 'ai');
|
||||
if (!authResult.authenticated) {
|
||||
return createAuthErrorResponse();
|
||||
}
|
||||
|
||||
const userId = authResult.userId;
|
||||
|
||||
if (!checkRateLimit(userId)) {
|
||||
return apiError.rateLimit('Enhancement rate limit exceeded');
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { input } = body;
|
||||
|
||||
if (!input || typeof input !== 'string' || input.length < 20) {
|
||||
return apiError.badRequest('Input too short for enhancement');
|
||||
}
|
||||
|
||||
const sanitizedInput = sanitizeInput(input);
|
||||
if (sanitizedInput.length < 20) {
|
||||
return apiError.badRequest('Input too short after sanitization');
|
||||
}
|
||||
|
||||
const systemPrompt = createEnhancementPrompt(sanitizedInput);
|
||||
const taskId = `enhance_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 4)}`;
|
||||
|
||||
const aiResponse = await enqueueApiCall(() =>
|
||||
fetch(process.env.AI_API_ENDPOINT + '/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${process.env.AI_API_KEY}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: AI_MODEL,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: systemPrompt
|
||||
}
|
||||
],
|
||||
max_tokens: 200,
|
||||
temperature: 0.7
|
||||
})
|
||||
}), taskId);
|
||||
|
||||
if (!aiResponse.ok) {
|
||||
console.error('AI enhancement error:', await aiResponse.text());
|
||||
return apiServerError.unavailable('Enhancement service unavailable');
|
||||
}
|
||||
|
||||
const aiData = await aiResponse.json();
|
||||
const aiContent = aiData.choices?.[0]?.message?.content;
|
||||
|
||||
if (!aiContent) {
|
||||
return apiServerError.unavailable('No enhancement response');
|
||||
}
|
||||
|
||||
let questions;
|
||||
try {
|
||||
const cleanedContent = aiContent
|
||||
.replace(/^```json\s*/i, '')
|
||||
.replace(/\s*```\s*$/, '')
|
||||
.trim();
|
||||
questions = JSON.parse(cleanedContent);
|
||||
|
||||
if (!Array.isArray(questions) || questions.length === 0) {
|
||||
throw new Error('Invalid questions format');
|
||||
}
|
||||
|
||||
// Validate and clean questions
|
||||
questions = questions
|
||||
.filter(q => typeof q === 'string' && q.length > 5 && q.length < 120)
|
||||
.slice(0, 3);
|
||||
|
||||
if (questions.length === 0) {
|
||||
throw new Error('No valid questions found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to parse enhancement response:', aiContent);
|
||||
return apiServerError.unavailable('Invalid enhancement response format');
|
||||
}
|
||||
|
||||
console.log(`[AI Enhancement] User: ${userId}, Questions: ${questions.length}, Input length: ${sanitizedInput.length}`);
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
questions,
|
||||
taskId
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Enhancement error:', error);
|
||||
return apiServerError.internal('Enhancement processing failed');
|
||||
}
|
||||
};
|
@ -155,6 +155,11 @@ WICHTIGE REGELN:
|
||||
8. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben
|
||||
9. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis
|
||||
|
||||
ENHANCED CONTEXTUAL ANALYSIS:
|
||||
10. Analysiere das Szenario detailliert und identifiziere Schlüsselelemente, Bedrohungen und forensische Herausforderungen
|
||||
11. Entwickle einen strategischen Untersuchungsansatz basierend auf dem spezifischen Szenario
|
||||
12. Identifiziere zeitkritische oder besonders wichtige Faktoren für diesen Fall
|
||||
|
||||
SOFTWARE/METHODEN-AUSWAHL NACH PHASE:
|
||||
${phaseDescriptions}
|
||||
|
||||
@ -163,16 +168,18 @@ ${domainAgnosticDescriptions}
|
||||
|
||||
ANTWORT-FORMAT (strict JSON):
|
||||
{
|
||||
"scenario_analysis": "Detaillierte Analyse des Szenarios auf Deutsch/English",
|
||||
"scenario_analysis": "Detaillierte Analyse des Szenarios: Erkannte Schlüsselelemente, Art des Vorfalls, betroffene Systeme, potentielle Bedrohungen und forensische Herausforderungen",
|
||||
"investigation_approach": "Strategischer Untersuchungsansatz für dieses spezifische Szenario: Prioritäten, Reihenfolge der Phasen, besondere Überlegungen",
|
||||
"critical_considerations": "Zeitkritische Faktoren, wichtige Sicherheitsaspekte oder besondere Vorsichtsmaßnahmen für diesen Fall",
|
||||
"recommended_tools": [
|
||||
{
|
||||
"name": "EXAKTER Name aus der Tools-Database",
|
||||
"priority": "high|medium|low",
|
||||
"phase": "${validPhases}",
|
||||
"justification": "Warum diese Methode für diese Phase und Szenario geeignet ist"
|
||||
"justification": "Warum diese Methode für diese Phase und dieses spezifische Szenario geeignet ist - mit Bezug zu den erkannten Schlüsselelementen"
|
||||
}
|
||||
],
|
||||
"workflow_suggestion": "Vorgeschlagener Untersuchungsablauf",
|
||||
"workflow_suggestion": "Vorgeschlagener Untersuchungsablauf mit konkreten Schritten für dieses Szenario",
|
||||
"background_knowledge": [
|
||||
{
|
||||
"concept_name": "EXAKTER Name aus der Konzepte-Database",
|
||||
@ -230,15 +237,22 @@ WICHTIGE REGELN:
|
||||
10. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben
|
||||
11. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis
|
||||
|
||||
ENHANCED CONTEXTUAL ANALYSIS:
|
||||
12. Analysiere das Problem detailliert und identifiziere technische Anforderungen, Herausforderungen und Erfolgsfaktoren
|
||||
13. Entwickle einen strategischen Lösungsansatz basierend auf dem spezifischen Problem
|
||||
14. Identifiziere wichtige Voraussetzungen oder Warnungen für die Anwendung
|
||||
|
||||
ANTWORT-FORMAT (strict JSON):
|
||||
{
|
||||
"problem_analysis": "Detaillierte Analyse des Problems/der Anforderung",
|
||||
"problem_analysis": "Detaillierte Analyse des Problems: Erkannte technische Anforderungen, Herausforderungen, benötigte Fähigkeiten und Erfolgsfaktoren",
|
||||
"investigation_approach": "Strategischer Lösungsansatz für dieses spezifische Problem: Herangehensweise, Prioritäten, optimale Anwendungsreihenfolge",
|
||||
"critical_considerations": "Wichtige Voraussetzungen, potentielle Fallstricke oder Warnungen für die Anwendung der empfohlenen Lösungen",
|
||||
"recommended_tools": [
|
||||
{
|
||||
"name": "EXAKTER Name aus der Tools-Database",
|
||||
"rank": 1,
|
||||
"suitability_score": "high|medium|low",
|
||||
"detailed_explanation": "Detaillierte Erklärung, warum dieses Tool/diese Methode das Problem löst",
|
||||
"detailed_explanation": "Detaillierte Erklärung, warum dieses Tool/diese Methode das spezifische Problem löst - mit Bezug zu den erkannten Anforderungen",
|
||||
"implementation_approach": "Konkrete Schritte/Ansatz zur Anwendung für dieses spezifische Problem",
|
||||
"pros": ["Spezifische Vorteile für diesen Anwendungsfall", "Weitere Vorteile"],
|
||||
"cons": ["Potentielle Nachteile oder Limitationen", "Weitere Einschränkungen"],
|
||||
@ -348,6 +362,10 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
if (mode === 'workflow') {
|
||||
validatedRecommendation = {
|
||||
...recommendation,
|
||||
// Ensure all new fields are included with fallbacks
|
||||
scenario_analysis: recommendation.scenario_analysis || recommendation.problem_analysis || '',
|
||||
investigation_approach: recommendation.investigation_approach || '',
|
||||
critical_considerations: recommendation.critical_considerations || '',
|
||||
recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
|
||||
if (!validToolNames.has(tool.name)) {
|
||||
console.warn(`AI recommended unknown tool: ${tool.name}`);
|
||||
@ -366,6 +384,10 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
} else {
|
||||
validatedRecommendation = {
|
||||
...recommendation,
|
||||
// Ensure all new fields are included with fallbacks
|
||||
problem_analysis: recommendation.problem_analysis || recommendation.scenario_analysis || '',
|
||||
investigation_approach: recommendation.investigation_approach || '',
|
||||
critical_considerations: recommendation.critical_considerations || '',
|
||||
recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
|
||||
if (!validToolNames.has(tool.name)) {
|
||||
console.warn(`AI recommended unknown tool: ${tool.name}`);
|
||||
|
@ -967,6 +967,23 @@ input[type="checkbox"] {
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.ai-input-layout {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ai-textarea-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ai-suggestions-section {
|
||||
flex: 0 0 320px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.ai-input-container textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
@ -1789,6 +1806,33 @@ footer {
|
||||
.form-grid.two-columns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.hint-card {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.hint-description {
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.hint-trigger {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.ai-input-layout {
|
||||
gap: 0.75rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
.ai-suggestions-section {
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
.ai-textarea-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 640px) {
|
||||
@ -1922,4 +1966,444 @@ footer {
|
||||
.kb-entry .flex.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.prompting-card {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.suggestion-item {
|
||||
padding: 0.375rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.position-badge {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.queue-status-card {
|
||||
padding: 1rem;
|
||||
}
|
||||
.hint-card {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.hint-title {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.hint-description {
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Smart Prompting Styles - Simplified */
|
||||
.smart-prompting-container {
|
||||
height: 100%;
|
||||
animation: smartPromptSlideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.prompting-card {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
height: 100%;
|
||||
min-height: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
opacity: 0.85;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.prompting-card:hover {
|
||||
opacity: 1;
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.prompting-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
font-size: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.prompting-spinner {
|
||||
flex-shrink: 0;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* Smart Prompting Hint */
|
||||
.smart-prompting-hint {
|
||||
height: 100%;
|
||||
min-height: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
animation: hintFadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.hint-card {
|
||||
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, var(--color-bg-tertiary) 100%);
|
||||
border: 1px dashed var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.25rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
opacity: 0.7;
|
||||
transition: var(--transition-medium);
|
||||
}
|
||||
|
||||
.hint-card:hover {
|
||||
opacity: 0.9;
|
||||
border-color: var(--color-accent);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.hint-icon {
|
||||
margin-bottom: 0.75rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.hint-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.hint-title {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.hint-description {
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.hint-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.hint-label {
|
||||
font-size: 0.6875rem;
|
||||
color: var(--color-text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hint-value {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent);
|
||||
background-color: var(--color-bg);
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--color-accent);
|
||||
}
|
||||
|
||||
/* Hide hint when smart prompting is active */
|
||||
.smart-prompting-container[style*="block"] ~ .smart-prompting-hint,
|
||||
.smart-prompting-container:not([style*="none"]) ~ .smart-prompting-hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes hintFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 0.7;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.suggested-questions {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.suggestions-header {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.suggestions-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.questions-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.suggestion-item {
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.4;
|
||||
color: var(--color-text-secondary);
|
||||
border-left: 2px solid var(--color-accent);
|
||||
transition: var(--transition-fast);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.suggestion-item::before {
|
||||
content: counter(suggestion-counter);
|
||||
counter-increment: suggestion-counter;
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: -6px;
|
||||
background-color: var(--color-accent);
|
||||
color: white;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.questions-list {
|
||||
counter-reset: suggestion-counter;
|
||||
}
|
||||
|
||||
.suggestion-number {
|
||||
color: var(--color-accent);
|
||||
font-weight: 600;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.dismiss-button {
|
||||
align-self: flex-end;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: var(--transition-fast);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.dismiss-button:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-text);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Queue Status - Improved Design */
|
||||
.queue-status-card {
|
||||
max-width: 400px;
|
||||
margin: 1.5rem auto 0;
|
||||
background: linear-gradient(135deg, var(--color-bg-secondary) 0%, var(--color-bg-tertiary) 100%);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.25rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.queue-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.queue-position-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.position-badge {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.position-label {
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.queue-details {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.queue-stat {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background-color: var(--color-bg);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: 0.25rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.stat-unit {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.queue-progress-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.queue-progress-track {
|
||||
background-color: var(--color-bg);
|
||||
border-radius: 8px;
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-border);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.queue-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--color-primary) 0%, var(--color-accent) 100%);
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.task-id-display {
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--color-bg);
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.task-label {
|
||||
font-size: 0.6875rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-right: 0.5rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.task-id {
|
||||
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 0.6875rem;
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg-secondary);
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.25rem;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
@keyframes smartPromptSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
max-height: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0.85;
|
||||
transform: translateX(0);
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Enhanced contextual analysis cards */
|
||||
.contextual-analysis-card {
|
||||
margin-bottom: 2rem;
|
||||
border-left: 4px solid;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.contextual-analysis-card:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.contextual-analysis-card.scenario {
|
||||
border-left-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.contextual-analysis-card.approach {
|
||||
border-left-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.contextual-analysis-card.critical {
|
||||
border-left-color: var(--color-warning);
|
||||
}
|
||||
|
||||
.analysis-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.analysis-header.scenario { color: var(--color-primary); }
|
||||
.analysis-header.approach { color: var(--color-accent); }
|
||||
.analysis-header.critical { color: var(--color-warning); }
|
Loading…
x
Reference in New Issue
Block a user