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
|
- reporting
|
||||||
- data-processing
|
- data-processing
|
||||||
- scripting
|
- scripting
|
||||||
- name: "Android Logical Imaging"
|
- name: Android Logical Imaging
|
||||||
icon: "📋"
|
icon: 📋
|
||||||
type: "method"
|
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.
|
description: >-
|
||||||
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.
|
Es gibt immer wieder auch Fälle, wo man nicht allermodernste Mobilgeräte
|
||||||
Die Erfolgsaussichten sinken jedoch massiv bei neuren Geräten."
|
knacken muss - der Großvater, der im Nachlass ein altes Samsung mit
|
||||||
domains: ["mobile-forensics"]
|
wichtigen Daten hinterlassen hat. Es muss in diesn Fällen nicht der teure
|
||||||
phases: ["data-collection"]
|
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: []
|
platforms: []
|
||||||
skillLevel: "advanced"
|
skillLevel: advanced
|
||||||
accessType: null
|
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
|
projectUrl: null
|
||||||
license: null
|
license: null
|
||||||
knowledgebase: true
|
knowledgebase: true
|
||||||
related_concepts: null
|
related_concepts: null
|
||||||
tags: ["imaging", "filesystem", "hardware-interface"]
|
tags:
|
||||||
|
- imaging
|
||||||
|
- filesystem
|
||||||
|
- hardware-interface
|
||||||
- name: Microsoft Office 365
|
- name: Microsoft Office 365
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
@ -2081,6 +2091,29 @@ tools:
|
|||||||
- evidence-preservation
|
- evidence-preservation
|
||||||
- malware-identification
|
- malware-identification
|
||||||
- chain-of-custody
|
- 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:
|
domains:
|
||||||
- id: incident-response
|
- id: incident-response
|
||||||
name: Incident Response & Breach-Untersuchung
|
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
|
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
|
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:
|
SOFTWARE/METHODEN-AUSWAHL NACH PHASE:
|
||||||
${phaseDescriptions}
|
${phaseDescriptions}
|
||||||
|
|
||||||
@ -163,16 +168,18 @@ ${domainAgnosticDescriptions}
|
|||||||
|
|
||||||
ANTWORT-FORMAT (strict JSON):
|
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": [
|
"recommended_tools": [
|
||||||
{
|
{
|
||||||
"name": "EXAKTER Name aus der Tools-Database",
|
"name": "EXAKTER Name aus der Tools-Database",
|
||||||
"priority": "high|medium|low",
|
"priority": "high|medium|low",
|
||||||
"phase": "${validPhases}",
|
"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": [
|
"background_knowledge": [
|
||||||
{
|
{
|
||||||
"concept_name": "EXAKTER Name aus der Konzepte-Database",
|
"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
|
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
|
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):
|
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": [
|
"recommended_tools": [
|
||||||
{
|
{
|
||||||
"name": "EXAKTER Name aus der Tools-Database",
|
"name": "EXAKTER Name aus der Tools-Database",
|
||||||
"rank": 1,
|
"rank": 1,
|
||||||
"suitability_score": "high|medium|low",
|
"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",
|
"implementation_approach": "Konkrete Schritte/Ansatz zur Anwendung für dieses spezifische Problem",
|
||||||
"pros": ["Spezifische Vorteile für diesen Anwendungsfall", "Weitere Vorteile"],
|
"pros": ["Spezifische Vorteile für diesen Anwendungsfall", "Weitere Vorteile"],
|
||||||
"cons": ["Potentielle Nachteile oder Limitationen", "Weitere Einschränkungen"],
|
"cons": ["Potentielle Nachteile oder Limitationen", "Weitere Einschränkungen"],
|
||||||
@ -348,6 +362,10 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
if (mode === 'workflow') {
|
if (mode === 'workflow') {
|
||||||
validatedRecommendation = {
|
validatedRecommendation = {
|
||||||
...recommendation,
|
...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) => {
|
recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
|
||||||
if (!validToolNames.has(tool.name)) {
|
if (!validToolNames.has(tool.name)) {
|
||||||
console.warn(`AI recommended unknown tool: ${tool.name}`);
|
console.warn(`AI recommended unknown tool: ${tool.name}`);
|
||||||
@ -366,6 +384,10 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
} else {
|
} else {
|
||||||
validatedRecommendation = {
|
validatedRecommendation = {
|
||||||
...recommendation,
|
...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) => {
|
recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
|
||||||
if (!validToolNames.has(tool.name)) {
|
if (!validToolNames.has(tool.name)) {
|
||||||
console.warn(`AI recommended unknown tool: ${tool.name}`);
|
console.warn(`AI recommended unknown tool: ${tool.name}`);
|
||||||
|
@ -967,6 +967,23 @@ input[type="checkbox"] {
|
|||||||
transition: var(--transition-fast);
|
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 {
|
.ai-input-container textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
@ -1789,6 +1806,33 @@ footer {
|
|||||||
.form-grid.two-columns {
|
.form-grid.two-columns {
|
||||||
grid-template-columns: 1fr;
|
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) {
|
@media (width <= 640px) {
|
||||||
@ -1922,4 +1966,444 @@ footer {
|
|||||||
.kb-entry .flex.gap-2 {
|
.kb-entry .flex.gap-2 {
|
||||||
gap: 0.5rem;
|
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