feat: restructure generator modal into two-zone layout
Split the single-column generator modal into a Selection zone (what to include) and Configuration zone (how to output) using CSS grid, with a full-width footer for action buttons. Adds note count indicator and token estimate from existing context files. Responsive: side-by-side on wide viewports, stacked on narrow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a6cc173293
commit
08b8a180ca
2 changed files with 261 additions and 149 deletions
339
src/generator.ts
339
src/generator.ts
|
|
@ -1,6 +1,7 @@
|
|||
import { App, Modal, Notice, Setting, TFolder } from 'obsidian';
|
||||
import { App, Modal, Notice, Setting, TFile, TFolder } from 'obsidian';
|
||||
import ClaudeContextPlugin from './main';
|
||||
import { createFreetextSource, SourcePosition } from './sources';
|
||||
import { estimateTokens } from './history';
|
||||
import { OutputTarget, getTargetIcon, formatTokenCount } from './targets';
|
||||
|
||||
interface FolderConfig {
|
||||
|
|
@ -103,77 +104,66 @@ export class ContextGeneratorModal extends Modal {
|
|||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
contentEl.addClass('claude-context-generator');
|
||||
contentEl.style.maxHeight = '80vh';
|
||||
contentEl.style.overflow = 'auto';
|
||||
this.modalEl.addClass('cc-gen-modal');
|
||||
|
||||
contentEl.createEl('h2', { text: 'Context Generator' });
|
||||
|
||||
// === BASIC SECTION ===
|
||||
contentEl.createEl('h3', { text: 'General' });
|
||||
// === TWO-ZONE GRID LAYOUT ===
|
||||
const layout = contentEl.createDiv({ cls: 'cc-gen-layout' });
|
||||
const selectionZone = layout.createDiv({ cls: 'cc-gen-zone cc-gen-selection' });
|
||||
const configZone = layout.createDiv({ cls: 'cc-gen-zone cc-gen-configuration' });
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Vault description')
|
||||
.setDesc('What is this vault used for?')
|
||||
.addTextArea(text => {
|
||||
text.setPlaceholder('e.g. Personal Zettelkasten for development and knowledge management')
|
||||
.setValue(this.config.vaultDescription)
|
||||
.onChange(v => this.config.vaultDescription = v);
|
||||
text.inputEl.rows = 2;
|
||||
text.inputEl.style.width = '100%';
|
||||
});
|
||||
// =============================================
|
||||
// SELECTION ZONE (left) – "What to include"
|
||||
// =============================================
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Language')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('english', 'English')
|
||||
.addOption('german', 'Deutsch')
|
||||
.setValue(this.config.language)
|
||||
.onChange(v => this.config.language = v));
|
||||
// === FILES TO GENERATE ===
|
||||
selectionZone.createEl('h3', { text: 'Files to generate' });
|
||||
|
||||
// === FORMATTING SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Formatting' });
|
||||
new Setting(selectionZone)
|
||||
.setName('conventions.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.conventions)
|
||||
.onChange(v => this.config.generateFiles.conventions = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('File naming')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('kebab-case', 'kebab-case')
|
||||
.addOption('snake_case', 'snake_case')
|
||||
.addOption('camelCase', 'camelCase')
|
||||
.addOption('free', 'Free / no convention')
|
||||
.setValue(this.config.fileNaming)
|
||||
.onChange(v => this.config.fileNaming = v));
|
||||
new Setting(selectionZone)
|
||||
.setName('structure.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.structure)
|
||||
.onChange(v => this.config.generateFiles.structure = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Link style')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('wikilinks', '[[Wikilinks]]')
|
||||
.addOption('markdown', '[Markdown](links)')
|
||||
.setValue(this.config.linkStyle)
|
||||
.onChange(v => this.config.linkStyle = v));
|
||||
new Setting(selectionZone)
|
||||
.setName('workflows.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.workflows)
|
||||
.onChange(v => this.config.generateFiles.workflows = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Heading depth')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('h2', 'H1 - H2')
|
||||
.addOption('h3', 'H1 - H3')
|
||||
.addOption('h4', 'H1 - H4')
|
||||
.addOption('h6', 'Unlimited')
|
||||
.setValue(this.config.headingDepth)
|
||||
.onChange(v => this.config.headingDepth = v));
|
||||
new Setting(selectionZone)
|
||||
.setName('templates.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.templates)
|
||||
.onChange(v => this.config.generateFiles.templates = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Date format')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('YYYY-MM-DD', 'YYYY-MM-DD (ISO)')
|
||||
.addOption('DD.MM.YYYY', 'DD.MM.YYYY')
|
||||
.addOption('MM/DD/YYYY', 'MM/DD/YYYY')
|
||||
.setValue(this.config.dateFormat)
|
||||
.onChange(v => this.config.dateFormat = v));
|
||||
new Setting(selectionZone)
|
||||
.setName('examples.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.examples)
|
||||
.onChange(v => this.config.generateFiles.examples = v));
|
||||
|
||||
// === FOLDER STRUCTURE ===
|
||||
selectionZone.createEl('h3', { text: 'Folder structure' });
|
||||
selectionZone.createEl('p', {
|
||||
text: 'Describe the purpose of your folders:',
|
||||
cls: 'setting-item-description'
|
||||
});
|
||||
|
||||
const foldersContainer = selectionZone.createDiv({ cls: 'folders-container' });
|
||||
this.renderFolders(foldersContainer);
|
||||
|
||||
// === TAGS SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Tags' });
|
||||
selectionZone.createEl('h3', { text: 'Tags' });
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(selectionZone)
|
||||
.setName('Tag style')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('hierarchical', 'Hierarchical (#area/tag)')
|
||||
|
|
@ -182,7 +172,7 @@ export class ContextGeneratorModal extends Modal {
|
|||
.setValue(this.config.tagsStyle)
|
||||
.onChange(v => this.config.tagsStyle = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(selectionZone)
|
||||
.setName('Predefined tags')
|
||||
.setDesc('Comma-separated (e.g. status/active, status/done, project)')
|
||||
.addText(text => text
|
||||
|
|
@ -193,9 +183,9 @@ export class ContextGeneratorModal extends Modal {
|
|||
}));
|
||||
|
||||
// === FRONTMATTER SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Frontmatter' });
|
||||
selectionZone.createEl('h3', { text: 'Frontmatter' });
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(selectionZone)
|
||||
.setName('Frontmatter fields')
|
||||
.setDesc('Comma-separated (e.g. date, tags, aliases, status)')
|
||||
.addText(text => text
|
||||
|
|
@ -204,10 +194,98 @@ export class ContextGeneratorModal extends Modal {
|
|||
this.config.frontmatterFields = v.split(',').map(s => s.trim()).filter(s => s);
|
||||
}));
|
||||
|
||||
// === RULES SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Rules' });
|
||||
// === NOTE TEMPLATES ===
|
||||
selectionZone.createEl('h3', { text: 'Note templates' });
|
||||
|
||||
new Setting(contentEl)
|
||||
const templatesContainer = selectionZone.createDiv({ cls: 'templates-container' });
|
||||
this.renderTemplates(templatesContainer);
|
||||
|
||||
new Setting(selectionZone)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('+ Add template')
|
||||
.onClick(() => {
|
||||
this.config.templates.push({ name: '', folder: '', tag: '' });
|
||||
this.renderTemplates(templatesContainer);
|
||||
}));
|
||||
|
||||
// === NOTE COUNT INDICATOR ===
|
||||
const fileCount = this.app.vault.getFiles().length;
|
||||
const folderCount = this.config.folders.length;
|
||||
selectionZone.createDiv({
|
||||
cls: 'cc-gen-stat',
|
||||
text: `${folderCount} folders, ${fileCount} total files in vault`,
|
||||
});
|
||||
|
||||
// =============================================
|
||||
// CONFIGURATION ZONE (right) – "How to output"
|
||||
// =============================================
|
||||
|
||||
// === GENERAL ===
|
||||
configZone.createEl('h3', { text: 'General' });
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('Vault description')
|
||||
.setDesc('What is this vault used for?')
|
||||
.addTextArea(text => {
|
||||
text.setPlaceholder('e.g. Personal Zettelkasten for development and knowledge management')
|
||||
.setValue(this.config.vaultDescription)
|
||||
.onChange(v => this.config.vaultDescription = v);
|
||||
text.inputEl.rows = 2;
|
||||
text.inputEl.style.width = '100%';
|
||||
});
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('Language')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('english', 'English')
|
||||
.addOption('german', 'Deutsch')
|
||||
.setValue(this.config.language)
|
||||
.onChange(v => this.config.language = v));
|
||||
|
||||
// === FORMATTING ===
|
||||
configZone.createEl('h3', { text: 'Formatting' });
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('File naming')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('kebab-case', 'kebab-case')
|
||||
.addOption('snake_case', 'snake_case')
|
||||
.addOption('camelCase', 'camelCase')
|
||||
.addOption('free', 'Free / no convention')
|
||||
.setValue(this.config.fileNaming)
|
||||
.onChange(v => this.config.fileNaming = v));
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('Link style')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('wikilinks', '[[Wikilinks]]')
|
||||
.addOption('markdown', '[Markdown](links)')
|
||||
.setValue(this.config.linkStyle)
|
||||
.onChange(v => this.config.linkStyle = v));
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('Heading depth')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('h2', 'H1 - H2')
|
||||
.addOption('h3', 'H1 - H3')
|
||||
.addOption('h4', 'H1 - H4')
|
||||
.addOption('h6', 'Unlimited')
|
||||
.setValue(this.config.headingDepth)
|
||||
.onChange(v => this.config.headingDepth = v));
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('Date format')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('YYYY-MM-DD', 'YYYY-MM-DD (ISO)')
|
||||
.addOption('DD.MM.YYYY', 'DD.MM.YYYY')
|
||||
.addOption('MM/DD/YYYY', 'MM/DD/YYYY')
|
||||
.setValue(this.config.dateFormat)
|
||||
.onChange(v => this.config.dateFormat = v));
|
||||
|
||||
// === RULES ===
|
||||
configZone.createEl('h3', { text: 'Rules' });
|
||||
|
||||
new Setting(configZone)
|
||||
.setName('Custom rules')
|
||||
.setDesc('One rule per line')
|
||||
.addTextArea(text => {
|
||||
|
|
@ -220,7 +298,7 @@ export class ContextGeneratorModal extends Modal {
|
|||
text.inputEl.style.width = '100%';
|
||||
});
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(configZone)
|
||||
.setName('Forbidden actions')
|
||||
.setDesc('Comma-separated (e.g. .obsidian/, certain folders)')
|
||||
.addText(text => text
|
||||
|
|
@ -229,67 +307,10 @@ export class ContextGeneratorModal extends Modal {
|
|||
this.config.forbiddenActions = v.split(',').map(s => s.trim()).filter(s => s);
|
||||
}));
|
||||
|
||||
// === STRUCTURE SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Folder structure' });
|
||||
contentEl.createEl('p', {
|
||||
text: 'Describe the purpose of your folders:',
|
||||
cls: 'setting-item-description'
|
||||
});
|
||||
// === ADDITIONAL CONTEXT ===
|
||||
configZone.createEl('h3', { text: 'Additional Context (this session)' });
|
||||
|
||||
const foldersContainer = contentEl.createDiv({ cls: 'folders-container' });
|
||||
this.renderFolders(foldersContainer);
|
||||
|
||||
// === TEMPLATES SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Note templates' });
|
||||
|
||||
const templatesContainer = contentEl.createDiv({ cls: 'templates-container' });
|
||||
this.renderTemplates(templatesContainer);
|
||||
|
||||
new Setting(contentEl)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('+ Add template')
|
||||
.onClick(() => {
|
||||
this.config.templates.push({ name: '', folder: '', tag: '' });
|
||||
this.renderTemplates(templatesContainer);
|
||||
}));
|
||||
|
||||
// === FILES TO GENERATE ===
|
||||
contentEl.createEl('h3', { text: 'Files to generate' });
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('conventions.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.conventions)
|
||||
.onChange(v => this.config.generateFiles.conventions = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('structure.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.structure)
|
||||
.onChange(v => this.config.generateFiles.structure = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('workflows.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.workflows)
|
||||
.onChange(v => this.config.generateFiles.workflows = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('templates.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.templates)
|
||||
.onChange(v => this.config.generateFiles.templates = v));
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('examples.md')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.config.generateFiles.examples)
|
||||
.onChange(v => this.config.generateFiles.examples = v));
|
||||
|
||||
// === ADDITIONAL CONTEXT SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Additional Context (this session)' });
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(configZone)
|
||||
.setName('Temporary freetext')
|
||||
.setDesc('Add context for this session only')
|
||||
.addTextArea(text => {
|
||||
|
|
@ -300,7 +321,7 @@ export class ContextGeneratorModal extends Modal {
|
|||
text.inputEl.style.width = '100%';
|
||||
});
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(configZone)
|
||||
.setName('Position')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('prefix', 'Prefix (before vault content)')
|
||||
|
|
@ -308,25 +329,24 @@ export class ContextGeneratorModal extends Modal {
|
|||
.setValue(this.temporaryPosition)
|
||||
.onChange(v => this.temporaryPosition = v as SourcePosition));
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(configZone)
|
||||
.setName('Save as default')
|
||||
.setDesc('Save this freetext as a permanent source')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.saveAsDefault)
|
||||
.onChange(v => this.saveAsDefault = v));
|
||||
|
||||
// Show saved sources count
|
||||
const enabledCount = this.plugin.settings.sources.filter(s => s.enabled).length;
|
||||
const sourcesInfo = contentEl.createEl('p', {
|
||||
const sourcesInfo = configZone.createEl('p', {
|
||||
text: `Saved sources: ${enabledCount} enabled (manage in settings)`,
|
||||
cls: 'setting-item-description'
|
||||
});
|
||||
sourcesInfo.style.marginTop = '10px';
|
||||
|
||||
// === PROMPT TEMPLATE SECTION ===
|
||||
contentEl.createEl('h3', { text: 'Prompt Template' });
|
||||
// === PROMPT TEMPLATE ===
|
||||
configZone.createEl('h3', { text: 'Prompt Template' });
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(configZone)
|
||||
.setName('Template')
|
||||
.setDesc('Wrap context with a prompt template')
|
||||
.addDropdown(dropdown => {
|
||||
|
|
@ -340,19 +360,18 @@ export class ContextGeneratorModal extends Modal {
|
|||
});
|
||||
});
|
||||
|
||||
// Show template count
|
||||
const templateCount = this.plugin.settings.promptTemplates.length;
|
||||
const templateInfo = contentEl.createEl('p', {
|
||||
const templateInfo = configZone.createEl('p', {
|
||||
text: `${templateCount} template(s) available (manage in settings)`,
|
||||
cls: 'setting-item-description'
|
||||
});
|
||||
templateInfo.style.marginTop = '5px';
|
||||
|
||||
// === OUTPUT TARGETS SECTION ===
|
||||
// === OUTPUT TARGETS ===
|
||||
if (this.plugin.settings.targets.length > 0) {
|
||||
contentEl.createEl('h3', { text: 'Output Targets' });
|
||||
configZone.createEl('h3', { text: 'Output Targets' });
|
||||
|
||||
const targetsContainer = contentEl.createDiv({ cls: 'targets-checkboxes' });
|
||||
const targetsContainer = configZone.createDiv({ cls: 'targets-checkboxes' });
|
||||
targetsContainer.style.display = 'flex';
|
||||
targetsContainer.style.flexDirection = 'column';
|
||||
targetsContainer.style.gap = '8px';
|
||||
|
|
@ -385,7 +404,6 @@ export class ContextGeneratorModal extends Modal {
|
|||
const label = row.createEl('span', { text: target.name });
|
||||
label.style.flex = '1';
|
||||
|
||||
// Primary indicator
|
||||
if (target.id === primaryId) {
|
||||
const badge = row.createEl('span', { text: 'clipboard' });
|
||||
badge.style.padding = '2px 6px';
|
||||
|
|
@ -408,24 +426,29 @@ export class ContextGeneratorModal extends Modal {
|
|||
tokenInfo.style.color = 'var(--text-muted)';
|
||||
}
|
||||
|
||||
const targetsInfo = contentEl.createEl('p', {
|
||||
const targetsInfo = configZone.createEl('p', {
|
||||
text: 'Primary target is copied to clipboard. Secondary targets are saved as files in the output folder.',
|
||||
cls: 'setting-item-description'
|
||||
});
|
||||
targetsInfo.style.marginTop = '5px';
|
||||
}
|
||||
|
||||
// Copy buttons
|
||||
const copyButtonContainer = contentEl.createDiv();
|
||||
// === TOKEN ESTIMATE ===
|
||||
this.renderTokenEstimate(configZone);
|
||||
|
||||
// =============================================
|
||||
// FOOTER (full-width, below both zones)
|
||||
// =============================================
|
||||
const footer = contentEl.createDiv({ cls: 'cc-gen-footer' });
|
||||
|
||||
const copyButtonContainer = footer.createDiv();
|
||||
copyButtonContainer.style.display = 'flex';
|
||||
copyButtonContainer.style.gap = '10px';
|
||||
copyButtonContainer.style.marginTop = '10px';
|
||||
|
||||
new Setting(copyButtonContainer)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('Copy Context Now')
|
||||
.onClick(async () => {
|
||||
// Save freetext if requested
|
||||
if (this.saveAsDefault && this.temporaryFreetext.trim()) {
|
||||
const source = createFreetextSource(
|
||||
'Generator Context',
|
||||
|
|
@ -437,12 +460,10 @@ export class ContextGeneratorModal extends Modal {
|
|||
new Notice('Freetext saved as default source');
|
||||
}
|
||||
|
||||
// Get selected targets
|
||||
const selectedTargets = this.plugin.settings.targets.filter(
|
||||
t => this.selectedTargetIds.has(t.id)
|
||||
);
|
||||
|
||||
// Copy context with selected template and targets
|
||||
await this.plugin.copyContextToClipboard(
|
||||
false,
|
||||
this.temporaryFreetext,
|
||||
|
|
@ -458,10 +479,9 @@ export class ContextGeneratorModal extends Modal {
|
|||
this.plugin.openFileSelector();
|
||||
}));
|
||||
|
||||
// === GENERATE BUTTON ===
|
||||
contentEl.createEl('hr');
|
||||
footer.createEl('hr');
|
||||
|
||||
new Setting(contentEl)
|
||||
new Setting(footer)
|
||||
.addButton(btn => btn
|
||||
.setButtonText('Generate')
|
||||
.setCta()
|
||||
|
|
@ -532,6 +552,27 @@ export class ContextGeneratorModal extends Modal {
|
|||
});
|
||||
}
|
||||
|
||||
async renderTokenEstimate(container: HTMLElement) {
|
||||
const contextFolder = this.plugin.settings.contextFolder;
|
||||
const folder = this.app.vault.getAbstractFileByPath(contextFolder);
|
||||
let totalChars = 0;
|
||||
|
||||
if (folder instanceof TFolder) {
|
||||
for (const child of folder.children) {
|
||||
if (child instanceof TFile && child.extension === 'md') {
|
||||
const content = await this.app.vault.cachedRead(child);
|
||||
totalChars += content.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tokens = Math.ceil(totalChars / 4);
|
||||
container.createDiv({
|
||||
cls: 'cc-gen-stat',
|
||||
text: `Estimated tokens: ~${tokens.toLocaleString()} (from existing context files)`,
|
||||
});
|
||||
}
|
||||
|
||||
scanVaultStructure(): string[] {
|
||||
const root = this.app.vault.getRoot();
|
||||
const folders: string[] = [];
|
||||
|
|
|
|||
71
styles.css
71
styles.css
|
|
@ -79,3 +79,74 @@
|
|||
opacity: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
/* Generator modal */
|
||||
.cc-gen-modal {
|
||||
width: 70vw;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.cc-gen-modal .modal-content {
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cc-gen-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.cc-gen-zone {
|
||||
overflow-y: auto;
|
||||
max-height: 60vh;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.cc-gen-selection {
|
||||
border-right: 1px solid var(--background-modifier-border);
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.cc-gen-zone h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.cc-gen-footer {
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
padding-top: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.cc-gen-stat {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
padding: 6px 10px;
|
||||
background: var(--background-secondary);
|
||||
border-radius: 4px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Stack on narrow viewports */
|
||||
@media (max-width: 768px) {
|
||||
.cc-gen-modal {
|
||||
width: 90vw;
|
||||
}
|
||||
.cc-gen-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.cc-gen-selection {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
padding-right: 0;
|
||||
padding-bottom: 16px;
|
||||
max-height: none;
|
||||
}
|
||||
.cc-gen-zone {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue