Skip to content

Commit 24c45c3

Browse files
committed
Work on adding model support
1 parent 157309a commit 24c45c3

File tree

7 files changed

+254
-57
lines changed

7 files changed

+254
-57
lines changed
Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,134 @@
1-
console.log("Hello world");
1+
// AI DOM Editor Content Script
2+
// Handles DOM collection and element selection
3+
4+
class AIDOMContent {
5+
constructor() {
6+
this.setupMessageListener();
7+
}
8+
9+
setupMessageListener() {
10+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
11+
if (message.action === 'collectDOMSummary') {
12+
const summary = this.collectDOMSummary();
13+
sendResponse({ success: true, summary });
14+
return true;
15+
}
16+
17+
if (message.action === 'startSelection') {
18+
this.startElementSelection();
19+
sendResponse({ success: true });
20+
return true;
21+
}
22+
23+
if (message.action === 'stopSelection') {
24+
this.stopElementSelection();
25+
sendResponse({ success: true });
26+
return true;
27+
}
28+
29+
return false;
30+
});
31+
}
32+
33+
collectDOMSummary() {
34+
try {
35+
const summary = {
36+
title: document.title,
37+
url: window.location.href,
38+
tags: this.getTagCounts(),
39+
classes: this.getTopClasses(),
40+
ids: this.getTopIds(),
41+
structure: this.getStructureSummary()
42+
};
43+
44+
return JSON.stringify(summary, null, 2);
45+
} catch (error) {
46+
console.error('Error collecting DOM summary:', error);
47+
return JSON.stringify({ error: error.message });
48+
}
49+
}
50+
51+
getTagCounts() {
52+
const tags = {};
53+
const elements = document.querySelectorAll('*');
54+
55+
elements.forEach(el => {
56+
const tag = el.tagName.toLowerCase();
57+
tags[tag] = (tags[tag] || 0) + 1;
58+
});
59+
60+
// Return top 20 most common tags
61+
return Object.entries(tags)
62+
.sort((a, b) => b[1] - a[1])
63+
.slice(0, 20)
64+
.reduce((obj, [key, val]) => {
65+
obj[key] = val;
66+
return obj;
67+
}, {});
68+
}
69+
70+
getTopClasses() {
71+
const classes = {};
72+
const elements = document.querySelectorAll('[class]');
73+
74+
elements.forEach(el => {
75+
el.className.split(/\s+/).forEach(cls => {
76+
if (cls.trim()) {
77+
classes[cls] = (classes[cls] || 0) + 1;
78+
}
79+
});
80+
});
81+
82+
// Return top 30 most common classes
83+
return Object.entries(classes)
84+
.sort((a, b) => b[1] - a[1])
85+
.slice(0, 30)
86+
.map(([cls]) => cls);
87+
}
88+
89+
getTopIds() {
90+
const ids = [];
91+
const elements = document.querySelectorAll('[id]');
92+
93+
elements.forEach(el => {
94+
if (el.id.trim()) {
95+
ids.push(el.id);
96+
}
97+
});
98+
99+
return ids.slice(0, 50);
100+
}
101+
102+
getStructureSummary() {
103+
const structure = {
104+
hasHeader: !!document.querySelector('header, [role="banner"]'),
105+
hasNav: !!document.querySelector('nav, [role="navigation"]'),
106+
hasMain: !!document.querySelector('main, [role="main"]'),
107+
hasFooter: !!document.querySelector('footer, [role="contentinfo"]'),
108+
hasSidebar: !!document.querySelector('aside, [role="complementary"]'),
109+
hasArticle: !!document.querySelector('article'),
110+
hasForm: !!document.querySelector('form'),
111+
hasTable: !!document.querySelector('table'),
112+
buttonCount: document.querySelectorAll('button, [role="button"]').length,
113+
linkCount: document.querySelectorAll('a[href]').length,
114+
imageCount: document.querySelectorAll('img').length
115+
};
116+
117+
return structure;
118+
}
119+
120+
startElementSelection() {
121+
// Element selection is handled by elementSelector.js
122+
// This is just a placeholder for future functionality
123+
}
124+
125+
stopElementSelection() {
126+
// Element selection is handled by elementSelector.js
127+
// This is just a placeholder for future functionality
128+
}
129+
}
130+
131+
// Initialize
132+
if (!window.__ctwkAIDOMContent) {
133+
window.__ctwkAIDOMContent = new AIDOMContent();
134+
}

src/ai_dom_editor/editor/ai_dom_editor.html

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@
1010
<div class="ai-editor-container">
1111
<!-- Header -->
1212
<div class="ai-editor-header">
13+
<h2 class="header-title">AI DOM Editor</h2>
1314
<div class="header-actions">
14-
<select id="modelSelector" class="model-selector" title="Select AI Model">
15-
<option value="">Loading models...</option>
16-
</select>
15+
<button class="tool-btn" id="clearChatBtn" title="Clear conversation">
16+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17+
<polyline points="3 6 5 6 21 6"></polyline>
18+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
19+
</svg>
20+
</button>
1721
<button class="tool-btn" id="headerSettingsBtn" title="Settings">
1822
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1923
<circle cx="12" cy="12" r="3"></circle>
@@ -73,20 +77,17 @@ <h2>Ai Editor</h2>
7377

7478
<!-- Input Area -->
7579
<div class="input-area">
76-
<div class="input-toolbar">
80+
<div class="model-selector-row">
81+
<select id="modelSelector" class="model-selector" title="Select AI Model">
82+
<option value="">Loading models...</option>
83+
</select>
84+
</div>
85+
<div class="input-wrapper">
7786
<button class="tool-btn" id="elementSelectorBtn" title="Select element on page">
7887
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
7988
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
8089
</svg>
8190
</button>
82-
<button class="tool-btn" id="clearChatBtn" title="Clear conversation">
83-
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
84-
<polyline points="3 6 5 6 21 6"></polyline>
85-
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
86-
</svg>
87-
</button>
88-
</div>
89-
<div class="input-wrapper">
9091
<textarea
9192
id="userInput"
9293
placeholder="Describe what you want to change on this page..."

src/ai_dom_editor/editor/ai_dom_editor.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,14 @@ class AIDOMEditor {
6767

6868
async loadAPIConfig() {
6969
try {
70-
const { aiDomEditorConfig } = await chrome.storage.local.get('aiDomEditorConfig');
71-
this.apiConfig = aiDomEditorConfig || null;
70+
const { aiDomEditorConfigs } = await chrome.storage.local.get('aiDomEditorConfigs');
71+
72+
// Use first config from multi-config system
73+
if (aiDomEditorConfigs && aiDomEditorConfigs.length > 0) {
74+
this.apiConfig = aiDomEditorConfigs[0];
75+
} else {
76+
this.apiConfig = null;
77+
}
7278

7379
if (!this.apiConfig || !this.apiConfig.apiKey || !this.apiConfig.endpoint) {
7480
this.showConfigBanner();
@@ -83,9 +89,21 @@ class AIDOMEditor {
8389

8490
async loadAvailableModels() {
8591
try {
86-
const { availableModels, selectedModel } = await chrome.storage.local.get(['availableModels', 'selectedModel']);
92+
const { availableModels, selectedModel, aiDomEditorConfigs } =
93+
await chrome.storage.local.get(['availableModels', 'selectedModel', 'aiDomEditorConfigs']);
94+
8795
this.availableModels = availableModels || [];
8896

97+
// If no models but we have configs, create models from configs as fallback
98+
if (this.availableModels.length === 0 && aiDomEditorConfigs && aiDomEditorConfigs.length > 0) {
99+
this.availableModels = aiDomEditorConfigs.map(config => ({
100+
id: config.model || 'default',
101+
provider: config.provider || 'custom',
102+
apiKey: config.apiKey,
103+
endpoint: config.endpoint
104+
}));
105+
}
106+
89107
// Populate model selector
90108
if (this.elements.modelSelector) {
91109
this.elements.modelSelector.innerHTML = '';
@@ -728,7 +746,7 @@ ${scriptCode.split('\n').map(line => ' ' + line).join('\n')}
728746
if (confirm('Clear conversation history for this site?')) {
729747
this.messages = [];
730748
this.elements.messages.innerHTML = '';
731-
this.elements.welcomeMessage.style.display = 'flex';
749+
this.elements.welcomeMessage.style.display = 'block';
732750

733751
// Clear from storage
734752
if (this.currentSiteUrl) {

src/ai_dom_editor/settings/ai_settings.js

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,9 @@ class AISettings {
6161

6262
async loadSettings() {
6363
try {
64-
const { aiDomEditorConfigs, aiDomEditorConfig } = await chrome.storage.local.get(['aiDomEditorConfigs', 'aiDomEditorConfig']);
64+
const { aiDomEditorConfigs } = await chrome.storage.local.get('aiDomEditorConfigs');
6565

66-
// Migrate old single config to new multi-config format
67-
if (aiDomEditorConfig && !aiDomEditorConfigs) {
68-
this.apiConfigs = [aiDomEditorConfig];
69-
await chrome.storage.local.set({ aiDomEditorConfigs: this.apiConfigs });
70-
await chrome.storage.local.remove('aiDomEditorConfig');
71-
} else if (aiDomEditorConfigs && Array.isArray(aiDomEditorConfigs)) {
66+
if (aiDomEditorConfigs && Array.isArray(aiDomEditorConfigs)) {
7267
this.apiConfigs = aiDomEditorConfigs;
7368
}
7469

@@ -339,16 +334,30 @@ class AISettings {
339334

340335
await chrome.storage.local.set({ aiDomEditorConfigs: this.apiConfigs });
341336

342-
// Also save the first config as default for backward compatibility
343-
if (this.apiConfigs.length > 0) {
344-
await chrome.storage.local.set({ aiDomEditorConfig: this.apiConfigs[0] });
345-
}
346-
347337
this.renderAPIKeysList();
348338
this.showToast('Settings saved successfully! ✓', 'success');
349339

350340
// Fetch and save all available models
351-
await this.fetchAllModels();
341+
const fetchedModels = await this.fetchAllModels();
342+
343+
// If no models were fetched, create entries from provider fallback models
344+
if (!fetchedModels || fetchedModels.length === 0) {
345+
const basicModels = [];
346+
this.apiConfigs.forEach(cfg => {
347+
const modelsForProvider = this.providerModels[cfg.provider] || [];
348+
modelsForProvider.forEach(modelId => {
349+
if (!basicModels.find(m => m.id === modelId && m.provider === cfg.provider)) {
350+
basicModels.push({
351+
id: modelId,
352+
provider: cfg.provider,
353+
apiKey: cfg.apiKey,
354+
endpoint: cfg.endpoint
355+
});
356+
}
357+
});
358+
});
359+
await chrome.storage.local.set({ availableModels: basicModels });
360+
}
352361

353362
// Notify any open AI editor windows
354363
chrome.runtime.sendMessage({ action: 'aiSettingsUpdated' });
@@ -384,13 +393,6 @@ class AISettings {
384393
this.apiConfigs.splice(index, 1);
385394
await chrome.storage.local.set({ aiDomEditorConfigs: this.apiConfigs });
386395

387-
// Update default config
388-
if (this.apiConfigs.length > 0) {
389-
await chrome.storage.local.set({ aiDomEditorConfig: this.apiConfigs[0] });
390-
} else {
391-
await chrome.storage.local.remove('aiDomEditorConfig');
392-
}
393-
394396
// Load first config or reset form
395397
if (this.apiConfigs.length > 0) {
396398
this.loadConfig(0);
@@ -401,7 +403,30 @@ class AISettings {
401403
}
402404

403405
this.renderAPIKeysList();
404-
await this.fetchAllModels();
406+
407+
// Fetch all models or create entries from provider fallback models
408+
const fetchedModels = await this.fetchAllModels();
409+
if ((!fetchedModels || fetchedModels.length === 0) && this.apiConfigs.length > 0) {
410+
const basicModels = [];
411+
this.apiConfigs.forEach(cfg => {
412+
const modelsForProvider = this.providerModels[cfg.provider] || [];
413+
modelsForProvider.forEach(modelId => {
414+
if (!basicModels.find(m => m.id === modelId && m.provider === cfg.provider)) {
415+
basicModels.push({
416+
id: modelId,
417+
provider: cfg.provider,
418+
apiKey: cfg.apiKey,
419+
endpoint: cfg.endpoint
420+
});
421+
}
422+
});
423+
});
424+
await chrome.storage.local.set({ availableModels: basicModels });
425+
} else if (this.apiConfigs.length === 0) {
426+
// Clear models if no configs left
427+
await chrome.storage.local.set({ availableModels: [] });
428+
}
429+
405430
this.showToast('API key configuration deleted', 'success');
406431

407432
// Notify any open AI editor windows
@@ -416,7 +441,8 @@ class AISettings {
416441
this.toggleEndpointField();
417442
this.updateTemperatureValue();
418443

419-
chrome.storage.local.remove('aiDomEditorConfig');
444+
chrome.storage.local.remove('aiDomEditorConfigs');
445+
chrome.storage.local.remove('availableModels');
420446
this.showToast('Settings reset', 'success');
421447
}
422448
}

src/ai_dom_editor/sidebar/ai_sidebar_window.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>AI DOM Sidebar</title>
77
<link rel="stylesheet" href="/assets/styles/popup.css" />
8-
<link rel="stylesheet" href="/assets/styles/ai_sidebar_window.css" />
8+
<link rel="stylesheet" href="/assets/styles/ai/ai_dom_editor.css" />
99
<script type="module" src="ai_sidebar_window.js"></script>
1010
</head>
1111
<body>

0 commit comments

Comments
 (0)