Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const stagedObsidianRules = {
'obsidianmd/platform': obsidianRuleSeverity,
'obsidianmd/prefer-abstract-input-suggest': obsidianRuleSeverity,
'obsidianmd/prefer-active-doc': obsidianRuleSeverity,
'obsidianmd/prefer-create-el': obsidianRuleSeverity,
'obsidianmd/prefer-file-manager-trash-file': obsidianRuleSeverity,
'obsidianmd/prefer-get-language': obsidianRuleSeverity,
'obsidianmd/prefer-instanceof': obsidianRuleSeverity,
Expand Down
33 changes: 27 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"esbuild": "0.28.1",
"eslint": "^10.2.0",
"eslint-plugin-jest": "^29.15.1",
"eslint-plugin-obsidianmd": "^0.3.0",
"eslint-plugin-obsidianmd": "^0.4.1",
"eslint-plugin-simple-import-sort": "^12.1.1",
"jest": "^30.3.0",
"jest-environment-jsdom": "^30.3.0",
Expand Down
16 changes: 3 additions & 13 deletions src/features/chat/chat-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,9 @@ export class QoderianView extends ItemView {
* The wrapper is moved to the active tab's nav row on tab switches.
*/
private buildNavRowContent(): HTMLElement {
const activeDocument = this.containerEl.ownerDocument;

const fragment = activeDocument.createDocumentFragment();
const wrapper = createDiv({ cls: 'qoderian-input-nav-content' });

this.tabBarContainerEl = activeDocument.createElement('div');
this.tabBarContainerEl.className = 'qoderian-tab-bar-container';
this.tabBarContainerEl = wrapper.createDiv({ cls: 'qoderian-tab-bar-container' });
this.tabBar = new TabBar(this.tabBarContainerEl, {
onTabClick: (tabId) => this.handleTabClick(tabId),
onTabClose: (tabId) => {
Expand All @@ -234,10 +231,8 @@ export class QoderianView extends ItemView {
void this.createNewTab().catch(() => new Notice('Failed to create tab'));
},
});
fragment.appendChild(this.tabBarContainerEl);

const navActionsEl = activeDocument.createElement('div');
navActionsEl.className = 'qoderian-input-nav-actions';
const navActionsEl = wrapper.createDiv({ cls: 'qoderian-input-nav-actions' });

this.newTabButtonEl = navActionsEl.createDiv({ cls: 'qoderian-input-nav-btn qoderian-new-tab-btn' });
setIcon(this.newTabButtonEl, 'square-plus');
Expand Down Expand Up @@ -269,11 +264,6 @@ export class QoderianView extends ItemView {
this.toggleHistoryDropdown();
});

fragment.appendChild(navActionsEl);

const wrapper = activeDocument.createElement('div');
wrapper.className = 'qoderian-input-nav-content';
wrapper.appendChild(fragment);
return wrapper;
}

Expand Down
6 changes: 2 additions & 4 deletions src/features/chat/controllers/conversation-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ export class ConversationController {

// Show regenerate button if title generation failed, or loading indicator if pending
if (conv.titleGenerationStatus === 'pending') {
const loadingEl = actions.createEl('span', { cls: 'qoderian-action-btn qoderian-action-loading' });
const loadingEl = actions.createSpan({ cls: 'qoderian-action-btn qoderian-action-loading' });
setIcon(loadingEl, 'loader-2');
loadingEl.setAttribute('aria-label', 'Generating title...');
} else if (conv.titleGenerationStatus === 'failed') {
Expand Down Expand Up @@ -874,9 +874,7 @@ export class ConversationController {
const titleEl = item.querySelector('.qoderian-history-item-title') as HTMLElement;
if (!titleEl) return;

const input = (item.ownerDocument ?? window.document).createElement('input');
input.type = 'text';
input.className = 'qoderian-rename-input';
const input = item.createEl('input', { cls: 'qoderian-rename-input', type: 'text' });
input.value = currentTitle;

titleEl.replaceWith(input);
Expand Down
4 changes: 2 additions & 2 deletions src/features/chat/rendering/message-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ export class MessageRenderer {
if (pre.parentElement?.classList.contains('qoderian-code-wrapper')) return;

// Create wrapper
const wrapper = createEl('div', { cls: 'qoderian-code-wrapper' });
const wrapper = createDiv({ cls: 'qoderian-code-wrapper' });
pre.parentElement?.insertBefore(wrapper, pre);
wrapper.appendChild(pre);

Expand All @@ -670,7 +670,7 @@ export class MessageRenderer {
const match = code.className.match(/language-(\w+)/);
if (match) {
wrapper.classList.add('has-language');
const label = createEl('span', {
const label = createSpan({
cls: 'qoderian-code-lang-label',
text: match[1],
});
Expand Down
34 changes: 13 additions & 21 deletions src/features/chat/ui/image-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,27 +86,19 @@ export class ImageContextManager {

this.dropOverlay = inputWrapper.createDiv({ cls: 'qoderian-drop-overlay' });
const dropContent = this.dropOverlay.createDiv({ cls: 'qoderian-drop-content' });
const ownerDocument = inputWrapper.ownerDocument ?? window.document;
const svg = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('width', '32');
svg.setAttribute('height', '32');
svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor');
svg.setAttribute('stroke-width', '2');
const pathEl = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'path');
pathEl.setAttribute('d', 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4');
const polyline = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'polyline');
polyline.setAttribute('points', '17 8 12 3 7 8');
const line = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', '12');
line.setAttribute('y1', '3');
line.setAttribute('x2', '12');
line.setAttribute('y2', '15');
svg.appendChild(pathEl);
svg.appendChild(polyline);
svg.appendChild(line);
dropContent.appendChild(svg);
const svg = dropContent.createSvg('svg', {
attr: {
viewBox: '0 0 24 24',
width: '32',
height: '32',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
},
});
svg.createSvg('path', { attr: { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' } });
svg.createSvg('polyline', { attr: { points: '17 8 12 3 7 8' } });
svg.createSvg('line', { attr: { x1: '12', y1: '3', x2: '12', y2: '15' } });
dropContent.createSpan({ text: 'Drop image here' });

const dropZone = inputWrapper;
Expand Down
51 changes: 28 additions & 23 deletions src/features/chat/ui/input-toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,31 +649,36 @@ export class ContextUsageMeter {
const y2 = cy + radius * Math.sin(endRad);

const gaugeEl = this.container.createDiv({ cls: 'qoderian-context-meter-gauge' });
const svg = gaugeEl.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', String(size));
svg.setAttribute('height', String(size));
svg.setAttribute('viewBox', `0 0 ${size} ${size}`);
const svg = gaugeEl.createSvg('svg', {
attr: {
width: String(size),
height: String(size),
viewBox: `0 0 ${size} ${size}`,
},
});

const pathData = `M ${x1} ${y1} A ${radius} ${radius} 0 1 1 ${x2} ${y2}`;
const backgroundPath = gaugeEl.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'path');
backgroundPath.classList.add('qoderian-meter-bg');
backgroundPath.setAttribute('d', pathData);
backgroundPath.setAttribute('fill', 'none');
backgroundPath.setAttribute('stroke-width', String(strokeWidth));
backgroundPath.setAttribute('stroke-linecap', 'round');

const fillPath = gaugeEl.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'path');
fillPath.classList.add('qoderian-meter-fill');
fillPath.setAttribute('d', pathData);
fillPath.setAttribute('fill', 'none');
fillPath.setAttribute('stroke-width', String(strokeWidth));
fillPath.setAttribute('stroke-linecap', 'round');
fillPath.setAttribute('stroke-dasharray', String(this.circumference));
fillPath.setAttribute('stroke-dashoffset', String(this.circumference));

svg.appendChild(backgroundPath);
svg.appendChild(fillPath);
gaugeEl.appendChild(svg);
svg.createSvg('path', {
cls: 'qoderian-meter-bg',
attr: {
d: pathData,
fill: 'none',
'stroke-width': String(strokeWidth),
'stroke-linecap': 'round',
},
});

const fillPath = svg.createSvg('path', {
cls: 'qoderian-meter-fill',
attr: {
d: pathData,
fill: 'none',
'stroke-width': String(strokeWidth),
'stroke-linecap': 'round',
'stroke-dasharray': String(this.circumference),
'stroke-dashoffset': String(this.circumference),
},
});
this.fillPath = fillPath;

this.percentEl = this.container.createSpan({ cls: 'qoderian-context-meter-percent' });
Expand Down
Loading
Loading