myrlin-workbook 0.8.10 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/web/public/app.js +162 -58
- package/src/web/public/index.html +8 -3
- package/src/web/public/styles.css +161 -41
- package/src/web/server.js +230 -0
- package/cron-full.json +0 -813
- package/cron-runs.json +0 -813
- package/logo-cropped.png +0 -0
- package/src/web/public/logo-cropped.png +0 -0
package/package.json
CHANGED
package/src/web/public/app.js
CHANGED
|
@@ -3571,6 +3571,7 @@ class CWMApp {
|
|
|
3571
3571
|
{ key: 'maxConcurrentTasks', label: 'Max Concurrent Tasks', description: 'Maximum number of worktree tasks that can run simultaneously (1-8)', category: 'Advanced', type: 'number', min: 1, max: 8 },
|
|
3572
3572
|
{ key: 'defaultModelPlanning', label: 'Default Model (Planning)', description: 'Auto-assign when tasks enter Planning. Haiku is fast/cheap for exploration. Only applies to tasks without a model set.', category: 'Advanced', type: 'select', options: [{ value: '', label: 'None' }, { value: 'claude-haiku-4-5-20251001', label: 'Haiku (fast, cheap)' }, { value: 'claude-sonnet-4-6', label: 'Sonnet (balanced)' }, { value: 'claude-opus-4-6', label: 'Opus (thorough)' }] },
|
|
3573
3573
|
{ key: 'defaultModelRunning', label: 'Default Model (Running)', description: 'Auto-assign when tasks enter Running. Sonnet balances speed and quality for implementation. Only applies to tasks without a model set.', category: 'Advanced', type: 'select', options: [{ value: '', label: 'None' }, { value: 'claude-haiku-4-5-20251001', label: 'Haiku (fast, cheap)' }, { value: 'claude-sonnet-4-6', label: 'Sonnet (balanced)' }, { value: 'claude-opus-4-6', label: 'Opus (thorough)' }] },
|
|
3574
|
+
{ key: 'anthropicApiKey', label: 'Anthropic API Key', description: 'Required for AI-powered session finder. Uses Claude Haiku for fast, low-cost semantic search across your projects and sessions. Get a key at console.anthropic.com.', category: 'AI', type: 'server-text', placeholder: 'sk-ant-...', apiEndpoint: '/api/keys/anthropic', apiField: 'key' },
|
|
3574
3575
|
{ key: 'cfNamedTunnel', label: 'Cloudflare Named Tunnel', description: 'Expose Myrlin on the internet via your own domain. Go to one.dash.cloudflare.com → Networks → Tunnels → Create a tunnel, then copy the token from the install command (the long eyJ… string).', category: 'Remote Access', type: 'tunnel' },
|
|
3575
3576
|
];
|
|
3576
3577
|
}
|
|
@@ -8489,92 +8490,170 @@ class CWMApp {
|
|
|
8489
8490
|
|
|
8490
8491
|
|
|
8491
8492
|
/* ═══════════════════════════════════════════════════════════
|
|
8492
|
-
FIND A
|
|
8493
|
+
FIND A SESSION (AI-POWERED)
|
|
8493
8494
|
═══════════════════════════════════════════════════════════ */
|
|
8494
8495
|
|
|
8496
|
+
/**
|
|
8497
|
+
* Open the AI-powered session finder overlay.
|
|
8498
|
+
* Uses Claude Haiku to semantically match a natural language description
|
|
8499
|
+
* against all known projects and sessions. Falls back to keyword matching
|
|
8500
|
+
* when no Anthropic API key is configured.
|
|
8501
|
+
*/
|
|
8495
8502
|
openFindConversation() {
|
|
8496
8503
|
const overlay = document.getElementById('find-convo-overlay');
|
|
8497
8504
|
const input = document.getElementById('find-convo-input');
|
|
8498
8505
|
const results = document.getElementById('find-convo-results');
|
|
8499
8506
|
const closeBtn = document.getElementById('find-convo-close');
|
|
8507
|
+
const searchBtn = document.getElementById('find-convo-search-btn');
|
|
8508
|
+
const modeIndicator = document.getElementById('find-convo-mode');
|
|
8500
8509
|
|
|
8501
8510
|
if (!overlay || !input || !results) return;
|
|
8502
8511
|
|
|
8503
8512
|
overlay.hidden = false;
|
|
8504
8513
|
input.value = '';
|
|
8505
|
-
results.innerHTML = '<div class="find-convo-empty">
|
|
8514
|
+
results.innerHTML = '<div class="find-convo-empty">Describe the session or project you\'re looking for</div>';
|
|
8506
8515
|
setTimeout(() => input.focus(), 50);
|
|
8507
8516
|
|
|
8508
|
-
//
|
|
8509
|
-
|
|
8517
|
+
// Check if AI mode is available (API key configured)
|
|
8518
|
+
this.api('GET', '/api/keys/anthropic')
|
|
8519
|
+
.then(data => {
|
|
8520
|
+
if (modeIndicator) {
|
|
8521
|
+
if (data.configured) {
|
|
8522
|
+
modeIndicator.innerHTML = '<span class="find-convo-mode-ai">✨ AI search</span>';
|
|
8523
|
+
} else {
|
|
8524
|
+
modeIndicator.innerHTML = '<span class="find-convo-mode-keyword">Keyword search <a href="#" class="find-convo-setup-link">(add API key for AI)</a></span>';
|
|
8525
|
+
}
|
|
8526
|
+
// Wire up the "add API key" link to open settings
|
|
8527
|
+
const setupLink = modeIndicator.querySelector('.find-convo-setup-link');
|
|
8528
|
+
if (setupLink) {
|
|
8529
|
+
setupLink.addEventListener('click', (e) => {
|
|
8530
|
+
e.preventDefault();
|
|
8531
|
+
this.closeFindConversation();
|
|
8532
|
+
this.openSettings();
|
|
8533
|
+
});
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
})
|
|
8537
|
+
.catch(() => {});
|
|
8538
|
+
|
|
8539
|
+
/** Execute the AI find search */
|
|
8510
8540
|
const doSearch = () => {
|
|
8511
8541
|
const query = input.value.trim();
|
|
8512
|
-
if (query.length <
|
|
8513
|
-
results.innerHTML = '<div class="find-convo-empty">Enter at least
|
|
8542
|
+
if (query.length < 3) {
|
|
8543
|
+
results.innerHTML = '<div class="find-convo-empty">Enter at least 3 characters to search</div>';
|
|
8514
8544
|
return;
|
|
8515
8545
|
}
|
|
8516
|
-
|
|
8517
|
-
|
|
8546
|
+
|
|
8547
|
+
// Show skeleton loading cards
|
|
8548
|
+
results.innerHTML = Array.from({ length: 3 }, () => `
|
|
8549
|
+
<div class="ai-find-card ai-find-card-skeleton">
|
|
8550
|
+
<div class="ai-find-card-header">
|
|
8551
|
+
<span class="skeleton-line" style="width: 60%"></span>
|
|
8552
|
+
<span class="skeleton-line" style="width: 30px"></span>
|
|
8553
|
+
</div>
|
|
8554
|
+
<div class="ai-find-card-summary">
|
|
8555
|
+
<span class="skeleton-line" style="width: 90%"></span>
|
|
8556
|
+
<span class="skeleton-line" style="width: 70%"></span>
|
|
8557
|
+
</div>
|
|
8558
|
+
<div class="ai-find-card-meta">
|
|
8559
|
+
<span class="skeleton-line" style="width: 50%"></span>
|
|
8560
|
+
</div>
|
|
8561
|
+
</div>
|
|
8562
|
+
`).join('');
|
|
8563
|
+
|
|
8564
|
+
if (searchBtn) {
|
|
8565
|
+
searchBtn.disabled = true;
|
|
8566
|
+
searchBtn.textContent = 'Searching...';
|
|
8567
|
+
}
|
|
8568
|
+
|
|
8569
|
+
this.api('POST', '/api/ai/find-session', { query })
|
|
8518
8570
|
.then(data => {
|
|
8519
8571
|
const items = data.results || [];
|
|
8572
|
+
|
|
8520
8573
|
if (items.length === 0) {
|
|
8521
|
-
results.innerHTML = '<div class="find-convo-empty">No
|
|
8574
|
+
results.innerHTML = '<div class="find-convo-empty">No matching sessions or projects found</div>';
|
|
8522
8575
|
return;
|
|
8523
8576
|
}
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8577
|
+
|
|
8578
|
+
// Show fallback hint when AI is not configured
|
|
8579
|
+
const fallbackHint = data.fallback
|
|
8580
|
+
? '<div class="find-convo-fallback-hint">Showing keyword matches. Add an Anthropic API key in Settings for smarter AI-powered search.</div>'
|
|
8581
|
+
: '';
|
|
8582
|
+
|
|
8583
|
+
results.innerHTML = fallbackHint + items.map(r => {
|
|
8584
|
+
const confidence = Math.round((r.confidence || 0) * 100);
|
|
8585
|
+
const lastActive = r.lastActive ? this.relativeTime(r.lastActive) : 'unknown';
|
|
8586
|
+
const statusBadge = r.status === 'running'
|
|
8587
|
+
? '<span class="ai-find-card-status ai-find-card-status-running">running</span>'
|
|
8588
|
+
: '';
|
|
8589
|
+
const sessionCount = r.sessionCount != null ? `${r.sessionCount} sessions` : '';
|
|
8590
|
+
const typeBadge = r.type === 'workspace' ? 'project' : r.type === 'project' ? 'discovered' : 'session';
|
|
8591
|
+
|
|
8592
|
+
return `
|
|
8593
|
+
<div class="ai-find-card" data-type="${this.escapeHtml(r.type)}" data-id="${this.escapeHtml(r.id)}" data-path="${this.escapeHtml(r.path || '')}" data-workspace-id="${this.escapeHtml(r.workspaceId || '')}">
|
|
8594
|
+
<div class="ai-find-card-header">
|
|
8595
|
+
<span class="ai-find-card-name">${this.escapeHtml(r.name || r.id)}</span>
|
|
8596
|
+
<span class="ai-find-card-badge">${typeBadge}</span>
|
|
8597
|
+
<span class="ai-find-card-confidence">${confidence}%</span>
|
|
8598
|
+
</div>
|
|
8599
|
+
<div class="ai-find-card-summary">${this.escapeHtml(r.summary || '')}</div>
|
|
8600
|
+
<div class="ai-find-card-meta">
|
|
8601
|
+
${r.path ? `<span class="ai-find-card-path">${this.escapeHtml(r.path)}</span>` : ''}
|
|
8602
|
+
<span class="ai-find-card-time">${lastActive}</span>
|
|
8603
|
+
${sessionCount ? `<span class="ai-find-card-sessions">${sessionCount}</span>` : ''}
|
|
8604
|
+
${statusBadge}
|
|
8605
|
+
</div>
|
|
8606
|
+
<button class="ai-find-card-open" title="Open in terminal pane">Open →</button>
|
|
8529
8607
|
</div>
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
const projectPath = el.dataset.projectPath;
|
|
8541
|
-
this.openConversationResult(sessionId, projectPath);
|
|
8542
|
-
this.closeFindConversation();
|
|
8608
|
+
`;
|
|
8609
|
+
}).join('');
|
|
8610
|
+
|
|
8611
|
+
// Bind click handlers on cards (open button only, not the whole card)
|
|
8612
|
+
results.querySelectorAll('.ai-find-card-open').forEach(btn => {
|
|
8613
|
+
btn.addEventListener('click', (e) => {
|
|
8614
|
+
e.stopPropagation();
|
|
8615
|
+
const card = btn.closest('.ai-find-card');
|
|
8616
|
+
if (!card) return;
|
|
8617
|
+
this._openFindResult(card);
|
|
8543
8618
|
});
|
|
8544
8619
|
});
|
|
8620
|
+
|
|
8621
|
+
// Also allow clicking the whole card
|
|
8622
|
+
results.querySelectorAll('.ai-find-card').forEach(card => {
|
|
8623
|
+
card.addEventListener('click', () => this._openFindResult(card));
|
|
8624
|
+
});
|
|
8545
8625
|
})
|
|
8546
8626
|
.catch(err => {
|
|
8547
8627
|
results.innerHTML = `<div class="find-convo-empty" style="color: var(--red);">Search failed: ${this.escapeHtml(err.message || 'Unknown error')}</div>`;
|
|
8628
|
+
})
|
|
8629
|
+
.finally(() => {
|
|
8630
|
+
if (searchBtn) {
|
|
8631
|
+
searchBtn.disabled = false;
|
|
8632
|
+
searchBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><line x1="16.5" y1="16.5" x2="21" y2="21"/></svg>`;
|
|
8633
|
+
}
|
|
8548
8634
|
});
|
|
8549
8635
|
};
|
|
8550
8636
|
|
|
8551
|
-
// Remove old
|
|
8552
|
-
if (this._findConvoInputHandler) {
|
|
8553
|
-
input.removeEventListener('input', this._findConvoInputHandler);
|
|
8554
|
-
}
|
|
8555
|
-
this._findConvoInputHandler = () => {
|
|
8556
|
-
clearTimeout(searchTimer);
|
|
8557
|
-
searchTimer = setTimeout(doSearch, 400);
|
|
8558
|
-
};
|
|
8559
|
-
input.addEventListener('input', this._findConvoInputHandler);
|
|
8560
|
-
|
|
8561
|
-
// Enter key triggers immediate search
|
|
8637
|
+
// Remove old listeners
|
|
8562
8638
|
if (this._findConvoKeyHandler) {
|
|
8563
8639
|
input.removeEventListener('keydown', this._findConvoKeyHandler);
|
|
8564
8640
|
}
|
|
8565
8641
|
this._findConvoKeyHandler = (e) => {
|
|
8566
|
-
if (e.key === 'Enter')
|
|
8567
|
-
|
|
8568
|
-
doSearch();
|
|
8569
|
-
} else if (e.key === 'Escape') {
|
|
8570
|
-
this.closeFindConversation();
|
|
8571
|
-
}
|
|
8642
|
+
if (e.key === 'Enter') doSearch();
|
|
8643
|
+
else if (e.key === 'Escape') this.closeFindConversation();
|
|
8572
8644
|
};
|
|
8573
8645
|
input.addEventListener('keydown', this._findConvoKeyHandler);
|
|
8574
8646
|
|
|
8647
|
+
// Search button click
|
|
8648
|
+
if (this._findConvoSearchHandler && searchBtn) {
|
|
8649
|
+
searchBtn.removeEventListener('click', this._findConvoSearchHandler);
|
|
8650
|
+
}
|
|
8651
|
+
this._findConvoSearchHandler = doSearch;
|
|
8652
|
+
if (searchBtn) searchBtn.addEventListener('click', this._findConvoSearchHandler);
|
|
8653
|
+
|
|
8575
8654
|
// Close handlers
|
|
8576
8655
|
if (this._findConvoCloseHandler) {
|
|
8577
|
-
closeBtn.removeEventListener('click', this._findConvoCloseHandler);
|
|
8656
|
+
if (closeBtn) closeBtn.removeEventListener('click', this._findConvoCloseHandler);
|
|
8578
8657
|
overlay.removeEventListener('click', this._findConvoCloseHandler);
|
|
8579
8658
|
}
|
|
8580
8659
|
this._findConvoCloseHandler = (e) => {
|
|
@@ -8582,29 +8661,54 @@ class CWMApp {
|
|
|
8582
8661
|
this.closeFindConversation();
|
|
8583
8662
|
}
|
|
8584
8663
|
};
|
|
8585
|
-
closeBtn.addEventListener('click', this._findConvoCloseHandler);
|
|
8664
|
+
if (closeBtn) closeBtn.addEventListener('click', this._findConvoCloseHandler);
|
|
8586
8665
|
overlay.addEventListener('click', this._findConvoCloseHandler);
|
|
8587
8666
|
}
|
|
8588
8667
|
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8668
|
+
/**
|
|
8669
|
+
* Open a find result in a terminal pane.
|
|
8670
|
+
* Does NOT close the overlay so the user can open multiple results.
|
|
8671
|
+
*/
|
|
8672
|
+
_openFindResult(card) {
|
|
8673
|
+
const type = card.dataset.type;
|
|
8674
|
+
const id = card.dataset.id;
|
|
8675
|
+
const cardPath = card.dataset.path;
|
|
8676
|
+
const wsId = card.dataset.workspaceId;
|
|
8677
|
+
|
|
8678
|
+
// Mark this card as opened
|
|
8679
|
+
card.classList.add('ai-find-card-opened');
|
|
8593
8680
|
|
|
8594
|
-
openConversationResult(sessionId, projectPath) {
|
|
8595
|
-
// Open the session in a terminal pane - not added to any workspace
|
|
8596
8681
|
const emptySlot = this.terminalPanes.findIndex(p => p === null);
|
|
8597
8682
|
if (emptySlot === -1) {
|
|
8598
8683
|
this.showToast('All terminal panes full. Close one first.', 'warning');
|
|
8599
8684
|
return;
|
|
8600
8685
|
}
|
|
8686
|
+
|
|
8601
8687
|
this.setViewMode('terminal');
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8688
|
+
|
|
8689
|
+
if (type === 'session') {
|
|
8690
|
+
// Resume existing session
|
|
8691
|
+
this.openTerminalInPane(emptySlot, id, id, {
|
|
8692
|
+
cwd: cardPath,
|
|
8693
|
+
resumeSessionId: id,
|
|
8694
|
+
command: 'claude',
|
|
8695
|
+
});
|
|
8696
|
+
this.showToast('Opening session in terminal', 'info');
|
|
8697
|
+
} else {
|
|
8698
|
+
// Workspace or discovered project: open a new session in the project directory
|
|
8699
|
+
const name = card.querySelector('.ai-find-card-name')?.textContent || 'new-session';
|
|
8700
|
+
this.openTerminalInPane(emptySlot, null, name, {
|
|
8701
|
+
cwd: cardPath,
|
|
8702
|
+
command: 'claude',
|
|
8703
|
+
});
|
|
8704
|
+
this.showToast(`Opening ${name} in terminal`, 'info');
|
|
8705
|
+
}
|
|
8706
|
+
}
|
|
8707
|
+
|
|
8708
|
+
/** Close the find conversation overlay and clean up listeners. */
|
|
8709
|
+
closeFindConversation() {
|
|
8710
|
+
const overlay = document.getElementById('find-convo-overlay');
|
|
8711
|
+
if (overlay) overlay.hidden = true;
|
|
8608
8712
|
}
|
|
8609
8713
|
|
|
8610
8714
|
|
|
@@ -1192,7 +1192,7 @@
|
|
|
1192
1192
|
<div class="modal-overlay" id="find-convo-overlay" hidden>
|
|
1193
1193
|
<div class="modal find-convo-modal" role="dialog" aria-modal="true">
|
|
1194
1194
|
<div class="modal-header">
|
|
1195
|
-
<h3 class="modal-title">Find a
|
|
1195
|
+
<h3 class="modal-title">Find a Session</h3>
|
|
1196
1196
|
<button class="btn btn-ghost btn-icon btn-sm" id="find-convo-close" aria-label="Close">
|
|
1197
1197
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
1198
1198
|
<path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
@@ -1200,8 +1200,13 @@
|
|
|
1200
1200
|
</button>
|
|
1201
1201
|
</div>
|
|
1202
1202
|
<div class="modal-body find-convo-body">
|
|
1203
|
-
<
|
|
1204
|
-
|
|
1203
|
+
<div class="find-convo-search-row">
|
|
1204
|
+
<input type="text" id="find-convo-input" class="find-convo-input" placeholder="Describe the session you're looking for..." spellcheck="false" autocomplete="off" />
|
|
1205
|
+
<button class="btn btn-primary btn-icon find-convo-search-btn" id="find-convo-search-btn" title="Search">
|
|
1206
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><line x1="16.5" y1="16.5" x2="21" y2="21"/></svg>
|
|
1207
|
+
</button>
|
|
1208
|
+
</div>
|
|
1209
|
+
<div class="find-convo-hint" id="find-convo-mode">Press Enter to search</div>
|
|
1205
1210
|
<div class="find-convo-results" id="find-convo-results"></div>
|
|
1206
1211
|
</div>
|
|
1207
1212
|
</div>
|
|
@@ -3604,9 +3604,9 @@ textarea.input {
|
|
|
3604
3604
|
background: rgba(203, 166, 247, 0.05);
|
|
3605
3605
|
}
|
|
3606
3606
|
|
|
3607
|
-
/* Find
|
|
3607
|
+
/* Find Session Modal (AI-powered) */
|
|
3608
3608
|
.find-convo-modal {
|
|
3609
|
-
width: min(
|
|
3609
|
+
width: min(640px, 90vw);
|
|
3610
3610
|
max-height: 80vh;
|
|
3611
3611
|
display: flex;
|
|
3612
3612
|
flex-direction: column;
|
|
@@ -3617,8 +3617,13 @@ textarea.input {
|
|
|
3617
3617
|
gap: 8px;
|
|
3618
3618
|
overflow: hidden;
|
|
3619
3619
|
}
|
|
3620
|
+
.find-convo-search-row {
|
|
3621
|
+
display: flex;
|
|
3622
|
+
gap: 6px;
|
|
3623
|
+
align-items: stretch;
|
|
3624
|
+
}
|
|
3620
3625
|
.find-convo-input {
|
|
3621
|
-
|
|
3626
|
+
flex: 1;
|
|
3622
3627
|
padding: 10px 12px;
|
|
3623
3628
|
font-size: 14px;
|
|
3624
3629
|
font-family: var(--font-mono);
|
|
@@ -3635,83 +3640,198 @@ textarea.input {
|
|
|
3635
3640
|
.find-convo-input::placeholder {
|
|
3636
3641
|
color: var(--overlay0);
|
|
3637
3642
|
}
|
|
3643
|
+
.find-convo-search-btn {
|
|
3644
|
+
width: 38px;
|
|
3645
|
+
min-width: 38px;
|
|
3646
|
+
display: flex;
|
|
3647
|
+
align-items: center;
|
|
3648
|
+
justify-content: center;
|
|
3649
|
+
border-radius: var(--radius-md);
|
|
3650
|
+
font-size: 12px;
|
|
3651
|
+
}
|
|
3638
3652
|
.find-convo-hint {
|
|
3639
3653
|
font-size: 11px;
|
|
3640
3654
|
color: var(--overlay0);
|
|
3641
3655
|
padding: 0 2px;
|
|
3656
|
+
display: flex;
|
|
3657
|
+
align-items: center;
|
|
3658
|
+
gap: 4px;
|
|
3659
|
+
}
|
|
3660
|
+
.find-convo-mode-ai {
|
|
3661
|
+
color: var(--mauve);
|
|
3662
|
+
font-weight: 500;
|
|
3663
|
+
}
|
|
3664
|
+
.find-convo-mode-keyword {
|
|
3665
|
+
color: var(--overlay0);
|
|
3666
|
+
}
|
|
3667
|
+
.find-convo-setup-link {
|
|
3668
|
+
color: var(--accent);
|
|
3669
|
+
text-decoration: none;
|
|
3670
|
+
font-size: 11px;
|
|
3671
|
+
}
|
|
3672
|
+
.find-convo-setup-link:hover {
|
|
3673
|
+
text-decoration: underline;
|
|
3674
|
+
}
|
|
3675
|
+
.find-convo-fallback-hint {
|
|
3676
|
+
font-size: 11px;
|
|
3677
|
+
color: var(--overlay0);
|
|
3678
|
+
background: var(--surface0);
|
|
3679
|
+
padding: 8px 12px;
|
|
3680
|
+
border-radius: var(--radius-md);
|
|
3681
|
+
margin-bottom: 4px;
|
|
3642
3682
|
}
|
|
3643
3683
|
.find-convo-results {
|
|
3644
3684
|
overflow-y: auto;
|
|
3645
|
-
max-height: calc(80vh -
|
|
3685
|
+
max-height: calc(80vh - 180px);
|
|
3646
3686
|
display: flex;
|
|
3647
3687
|
flex-direction: column;
|
|
3648
|
-
gap:
|
|
3688
|
+
gap: 6px;
|
|
3689
|
+
}
|
|
3690
|
+
.find-convo-loading {
|
|
3691
|
+
text-align: center;
|
|
3692
|
+
padding: 24px;
|
|
3693
|
+
color: var(--overlay0);
|
|
3694
|
+
font-size: 12px;
|
|
3695
|
+
}
|
|
3696
|
+
.find-convo-empty {
|
|
3697
|
+
text-align: center;
|
|
3698
|
+
padding: 24px;
|
|
3699
|
+
color: var(--overlay0);
|
|
3700
|
+
font-size: 12px;
|
|
3649
3701
|
}
|
|
3650
|
-
|
|
3702
|
+
|
|
3703
|
+
/* AI Find Result Cards */
|
|
3704
|
+
.ai-find-card {
|
|
3705
|
+
position: relative;
|
|
3651
3706
|
display: flex;
|
|
3652
3707
|
flex-direction: column;
|
|
3653
|
-
gap:
|
|
3654
|
-
padding:
|
|
3708
|
+
gap: 6px;
|
|
3709
|
+
padding: 12px 14px;
|
|
3655
3710
|
background: var(--surface0);
|
|
3656
|
-
border: 1px solid
|
|
3711
|
+
border: 1px solid var(--surface1);
|
|
3657
3712
|
border-radius: var(--radius-md);
|
|
3658
3713
|
cursor: pointer;
|
|
3659
|
-
transition: all
|
|
3714
|
+
transition: all 150ms ease;
|
|
3660
3715
|
}
|
|
3661
|
-
.find-
|
|
3716
|
+
.ai-find-card:hover {
|
|
3662
3717
|
background: var(--surface1);
|
|
3663
3718
|
border-color: var(--accent);
|
|
3664
3719
|
}
|
|
3665
|
-
.find-
|
|
3720
|
+
.ai-find-card.ai-find-card-opened {
|
|
3721
|
+
border-color: var(--green);
|
|
3722
|
+
opacity: 0.7;
|
|
3723
|
+
}
|
|
3724
|
+
.ai-find-card.ai-find-card-opened::after {
|
|
3725
|
+
content: 'opened';
|
|
3726
|
+
position: absolute;
|
|
3727
|
+
top: 8px;
|
|
3728
|
+
right: 8px;
|
|
3729
|
+
font-size: 10px;
|
|
3730
|
+
color: var(--green);
|
|
3731
|
+
font-weight: 500;
|
|
3732
|
+
text-transform: uppercase;
|
|
3733
|
+
letter-spacing: 0.05em;
|
|
3734
|
+
}
|
|
3735
|
+
.ai-find-card-header {
|
|
3666
3736
|
display: flex;
|
|
3667
3737
|
align-items: center;
|
|
3668
|
-
justify-content: space-between;
|
|
3669
3738
|
gap: 8px;
|
|
3670
3739
|
}
|
|
3671
|
-
.find-
|
|
3672
|
-
font-size:
|
|
3740
|
+
.ai-find-card-name {
|
|
3741
|
+
font-size: 14px;
|
|
3673
3742
|
font-weight: 600;
|
|
3674
|
-
color: var(--
|
|
3743
|
+
color: var(--text-primary);
|
|
3744
|
+
flex: 1;
|
|
3675
3745
|
white-space: nowrap;
|
|
3676
3746
|
overflow: hidden;
|
|
3677
3747
|
text-overflow: ellipsis;
|
|
3678
3748
|
}
|
|
3679
|
-
.find-
|
|
3749
|
+
.ai-find-card-badge {
|
|
3680
3750
|
font-size: 10px;
|
|
3681
|
-
|
|
3682
|
-
|
|
3751
|
+
font-weight: 500;
|
|
3752
|
+
text-transform: uppercase;
|
|
3753
|
+
letter-spacing: 0.04em;
|
|
3754
|
+
padding: 1px 6px;
|
|
3755
|
+
border-radius: 3px;
|
|
3756
|
+
background: var(--surface1);
|
|
3757
|
+
color: var(--subtext0);
|
|
3758
|
+
}
|
|
3759
|
+
.ai-find-card-confidence {
|
|
3760
|
+
font-size: 12px;
|
|
3761
|
+
font-weight: 600;
|
|
3762
|
+
color: var(--green);
|
|
3683
3763
|
font-family: var(--font-mono);
|
|
3764
|
+
white-space: nowrap;
|
|
3684
3765
|
}
|
|
3685
|
-
.find-
|
|
3766
|
+
.ai-find-card-summary {
|
|
3686
3767
|
font-size: 12px;
|
|
3687
|
-
color: var(--
|
|
3688
|
-
line-height: 1.
|
|
3768
|
+
color: var(--subtext0);
|
|
3769
|
+
line-height: 1.45;
|
|
3689
3770
|
}
|
|
3690
|
-
.find-
|
|
3771
|
+
.ai-find-card-meta {
|
|
3772
|
+
display: flex;
|
|
3773
|
+
flex-wrap: wrap;
|
|
3774
|
+
gap: 6px 12px;
|
|
3691
3775
|
font-size: 11px;
|
|
3692
|
-
color: var(--
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
-
|
|
3776
|
+
color: var(--overlay0);
|
|
3777
|
+
font-family: var(--font-mono);
|
|
3778
|
+
}
|
|
3779
|
+
.ai-find-card-path {
|
|
3780
|
+
max-width: 100%;
|
|
3697
3781
|
overflow: hidden;
|
|
3782
|
+
text-overflow: ellipsis;
|
|
3783
|
+
white-space: nowrap;
|
|
3698
3784
|
}
|
|
3699
|
-
.find-
|
|
3785
|
+
.ai-find-card-status {
|
|
3700
3786
|
font-size: 10px;
|
|
3701
|
-
|
|
3702
|
-
|
|
3787
|
+
font-weight: 500;
|
|
3788
|
+
padding: 0 5px;
|
|
3789
|
+
border-radius: 3px;
|
|
3703
3790
|
}
|
|
3704
|
-
.find-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
color: var(--overlay0);
|
|
3708
|
-
font-size: 12px;
|
|
3791
|
+
.ai-find-card-status-running {
|
|
3792
|
+
color: var(--green);
|
|
3793
|
+
background: rgba(166, 218, 149, 0.1);
|
|
3709
3794
|
}
|
|
3710
|
-
.find-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3795
|
+
.ai-find-card-open {
|
|
3796
|
+
position: absolute;
|
|
3797
|
+
bottom: 10px;
|
|
3798
|
+
right: 10px;
|
|
3799
|
+
background: var(--accent);
|
|
3800
|
+
color: var(--base);
|
|
3801
|
+
border: none;
|
|
3802
|
+
padding: 4px 12px;
|
|
3803
|
+
border-radius: var(--radius-sm);
|
|
3804
|
+
font-size: 11px;
|
|
3805
|
+
font-weight: 600;
|
|
3806
|
+
cursor: pointer;
|
|
3807
|
+
opacity: 0;
|
|
3808
|
+
transition: opacity 150ms ease;
|
|
3809
|
+
}
|
|
3810
|
+
.ai-find-card:hover .ai-find-card-open {
|
|
3811
|
+
opacity: 1;
|
|
3812
|
+
}
|
|
3813
|
+
.ai-find-card-open:hover {
|
|
3814
|
+
filter: brightness(1.15);
|
|
3815
|
+
}
|
|
3816
|
+
.ai-find-card.ai-find-card-opened .ai-find-card-open {
|
|
3817
|
+
display: none;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
/* Skeleton loading animation for AI find cards */
|
|
3821
|
+
.ai-find-card-skeleton {
|
|
3822
|
+
pointer-events: none;
|
|
3823
|
+
}
|
|
3824
|
+
.skeleton-line {
|
|
3825
|
+
display: inline-block;
|
|
3826
|
+
height: 12px;
|
|
3827
|
+
background: linear-gradient(90deg, var(--surface1) 25%, var(--surface2) 50%, var(--surface1) 75%);
|
|
3828
|
+
background-size: 200% 100%;
|
|
3829
|
+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
|
3830
|
+
border-radius: 3px;
|
|
3831
|
+
}
|
|
3832
|
+
@keyframes skeleton-shimmer {
|
|
3833
|
+
0% { background-position: 200% 0; }
|
|
3834
|
+
100% { background-position: -200% 0; }
|
|
3715
3835
|
}
|
|
3716
3836
|
|
|
3717
3837
|
/* Project accordion */
|