mirror of
https://github.com/h-sphere/sql-seal-charts.git
synced 2026-07-22 12:00:28 +00:00
feat: adding global configurations
This commit is contained in:
parent
22dbb66fcd
commit
299924673f
8 changed files with 656 additions and 4 deletions
5
.changeset/sour-lies-do.md
Normal file
5
.changeset/sour-lies-do.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"sqlseal-charts": minor
|
||||
---
|
||||
|
||||
adding support for global configurations
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
"@types/acorn": "^6.0.4",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/json5": "^2.2.0",
|
||||
"@types/node": "^22.13.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ importers:
|
|||
'@types/jest':
|
||||
specifier: ^29.5.14
|
||||
version: 29.5.14
|
||||
'@types/json5':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
'@types/node':
|
||||
specifier: ^22.13.10
|
||||
version: 22.13.10
|
||||
|
|
@ -1062,6 +1065,10 @@ packages:
|
|||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
'@types/json5@2.2.0':
|
||||
resolution: {integrity: sha512-NrVug5woqbvNZ0WX+Gv4R+L4TGddtmFek2u8RtccAgFZWtS9QXF2xCXY22/M4nzkaKF0q9Fc6M/5rxLDhfwc/A==}
|
||||
deprecated: This is a stub types definition. json5 provides its own type definitions, so you do not need this installed.
|
||||
|
||||
'@types/leaflet@1.9.16':
|
||||
resolution: {integrity: sha512-wzZoyySUxkgMZ0ihJ7IaUIblG8Rdc8AbbZKLneyn+QjYsj5q1QU7TEKYqwTr10BGSzY5LI7tJk9Ifo+mEjdFRw==}
|
||||
|
||||
|
|
@ -3873,6 +3880,10 @@ snapshots:
|
|||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/json5@2.2.0':
|
||||
dependencies:
|
||||
json5: 2.2.3
|
||||
|
||||
'@types/leaflet@1.9.16':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import type { RendererConfig } from "@hypersphere/sqlseal";
|
|||
import { ViewDefinition } from "@hypersphere/sqlseal/dist/src/grammar/parser";
|
||||
import { parseCodeAdvanced } from "./utils/advancedParser";
|
||||
import { FullScreenChartModal } from "./fullscreenModal";
|
||||
import { SQLSealChartsSettings } from "./settings";
|
||||
import * as JSON5 from "json5";
|
||||
|
||||
interface Config {
|
||||
config: string
|
||||
|
|
@ -18,7 +20,7 @@ echarts.registerTransform((ecStat as any).transform.histogram);
|
|||
|
||||
export class ChartRenderer implements RendererConfig {
|
||||
|
||||
constructor(private readonly app: App) {
|
||||
constructor(private readonly app: App, private readonly settings: SQLSealChartsSettings) {
|
||||
}
|
||||
|
||||
get viewDefinition(): ViewDefinition {
|
||||
|
|
@ -51,6 +53,23 @@ export class ChartRenderer implements RendererConfig {
|
|||
})
|
||||
}
|
||||
|
||||
private prepareGlobalConfigVariables(): Record<string, any> {
|
||||
const globalVariables: Record<string, any> = {};
|
||||
|
||||
this.settings.globalConfigs.forEach(config => {
|
||||
try {
|
||||
const parsedConfig = JSON5.parse(config.config);
|
||||
globalVariables[config.name] = parsedConfig;
|
||||
} catch (e) {
|
||||
console.warn(`Failed to parse global configuration '${config.name}' with JSON5:`, e);
|
||||
// Still make it available as a string if JSON5 parsing fails
|
||||
globalVariables[config.name] = config.config;
|
||||
}
|
||||
});
|
||||
|
||||
return globalVariables;
|
||||
}
|
||||
|
||||
render(config: Config, el: HTMLElement) {
|
||||
let isRendered: boolean = false
|
||||
let chart: echarts.ECharts | null = null
|
||||
|
|
@ -64,17 +83,21 @@ export class ChartRenderer implements RendererConfig {
|
|||
throw new Error('To process JavaScript, set ADVANCED MODE flag')
|
||||
}
|
||||
const { functions, variables } = prepareDataVariables({ columns, data })
|
||||
|
||||
// Add global configurations as variables
|
||||
const globalVariables = this.prepareGlobalConfigVariables()
|
||||
const allVariables = { ...variables, ...globalVariables }
|
||||
|
||||
let parsedConfig: Object = {}
|
||||
if (isAdvancedMode) {
|
||||
try {
|
||||
parsedConfig = parseCodeAdvanced({ functions, variables }, config.config)
|
||||
parsedConfig = parseCodeAdvanced({ functions, variables: allVariables }, config.config)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
parsedConfig = parseCode(config.config, functions, variables) as Object
|
||||
parsedConfig = parseCode(config.config, functions, allVariables) as Object
|
||||
}
|
||||
if (!parsedConfig || typeof parsedConfig !== 'object') {
|
||||
throw new Error('Issue with parsing config')
|
||||
|
|
|
|||
20
src/main.ts
20
src/main.ts
|
|
@ -3,6 +3,8 @@ import { ChartRenderer } from "./chartRenderer";
|
|||
import { pluginApi } from "@vanakat/plugin-api";
|
||||
import type { SQLSealApi, SQLSealRegisterApi } from "@hypersphere/sqlseal";
|
||||
import { uniqBy } from "lodash";
|
||||
import { SQLSealChartsSettings, DEFAULT_SETTINGS } from "./settings";
|
||||
import { SQLSealChartsSettingTab } from "./settingsTab";
|
||||
|
||||
const SQLSEAL_API_KEY = "___sqlSeal";
|
||||
const SQLSEAL_QUEUED_PLUGINS = "___sqlSeal_queue";
|
||||
|
|
@ -38,14 +40,30 @@ const registerApi = (plugin: Plugin, fn: (api: SQLSealApi) => void) => {
|
|||
};
|
||||
|
||||
export default class SQLSealCharts extends Plugin {
|
||||
settings: SQLSealChartsSettings;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// Add settings tab
|
||||
this.addSettingTab(new SQLSealChartsSettingTab(this.app, this));
|
||||
|
||||
registerApi(this, (api) => this.sqlSealRegistered(api));
|
||||
}
|
||||
|
||||
sqlSealRegistered(api: SQLSealApi) {
|
||||
api.registerView("sqlseal-charts", new ChartRenderer(this.app));
|
||||
api.registerView("sqlseal-charts", new ChartRenderer(this.app, this.settings));
|
||||
if ((api as any).apiVersion >= 2) {
|
||||
api.registerFlag({ key: "isAdvancedMode", name: "ADVANCED MODE" });
|
||||
}
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
const data = await this.loadData();
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, data);
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
37
src/settings.ts
Normal file
37
src/settings.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
export interface GlobalChartConfig {
|
||||
name: string;
|
||||
config: string; // JSON string
|
||||
}
|
||||
|
||||
export interface SQLSealChartsSettings {
|
||||
globalConfigs: GlobalChartConfig[];
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: SQLSealChartsSettings = {
|
||||
globalConfigs: [
|
||||
{
|
||||
name: "defaultTheme",
|
||||
config: `{
|
||||
backgroundColor: "#ffffff",
|
||||
textStyle: {
|
||||
color: "#333333",
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}
|
||||
}`
|
||||
},
|
||||
{
|
||||
name: "pieChart",
|
||||
config: `{
|
||||
type: 'pie',
|
||||
radius: '70%',
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}`
|
||||
}
|
||||
]
|
||||
};
|
||||
343
src/settingsTab.ts
Normal file
343
src/settingsTab.ts
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
import { App, PluginSettingTab, Setting } from "obsidian";
|
||||
import SQLSealCharts from "./main";
|
||||
import { GlobalChartConfig } from "./settings";
|
||||
import * as JSON5 from "json5";
|
||||
|
||||
export class SQLSealChartsSettingTab extends PluginSettingTab {
|
||||
plugin: SQLSealCharts;
|
||||
private currentView: 'list' | 'edit' = 'list';
|
||||
private editingIndex: number = -1;
|
||||
|
||||
constructor(app: App, plugin: SQLSealCharts) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', { text: 'SQLSeal Charts Settings' });
|
||||
|
||||
if (this.currentView === 'list') {
|
||||
this.displayConfigList();
|
||||
} else if (this.currentView === 'edit') {
|
||||
this.displayConfigEditor();
|
||||
}
|
||||
}
|
||||
|
||||
private displayConfigList(): void {
|
||||
const { containerEl } = this;
|
||||
|
||||
containerEl.createEl('h3', { text: 'Global Chart Configurations' });
|
||||
|
||||
const descriptionContainer = containerEl.createDiv('sqlseal-description-container');
|
||||
descriptionContainer.createEl('p', {
|
||||
text: 'Define reusable JSON5 configurations that can be accessed as variables in your charts.',
|
||||
cls: 'setting-item-description'
|
||||
});
|
||||
|
||||
const exampleContainer = descriptionContainer.createDiv('sqlseal-example-container');
|
||||
exampleContainer.createEl('strong', { text: 'Example Usage:' });
|
||||
exampleContainer.createEl('br');
|
||||
exampleContainer.createEl('code', {
|
||||
text: '1. Create config named "theme" with: {backgroundColor: "#1a1a1a"}',
|
||||
cls: 'sqlseal-example-code'
|
||||
});
|
||||
exampleContainer.createEl('br');
|
||||
exampleContainer.createEl('code', {
|
||||
text: '2. Use in charts: {...theme, title: {text: "My Chart"}}',
|
||||
cls: 'sqlseal-example-code'
|
||||
});
|
||||
|
||||
const featuresContainer = descriptionContainer.createDiv('sqlseal-features-container');
|
||||
featuresContainer.createEl('br');
|
||||
featuresContainer.createEl('small', {
|
||||
text: '✨ Supports JSON5: unquoted keys, single quotes, trailing commas, and comments',
|
||||
cls: 'sqlseal-feature-text'
|
||||
});
|
||||
|
||||
|
||||
// Add new configuration button
|
||||
new Setting(containerEl)
|
||||
.setName('Add New Configuration')
|
||||
.setDesc('Create a new global configuration')
|
||||
.addButton(button => {
|
||||
button
|
||||
.setButtonText('Add Configuration')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
this.editingIndex = -1; // -1 means new config
|
||||
this.currentView = 'edit';
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
// Create table
|
||||
const tableContainer = containerEl.createDiv('sqlseal-config-table-container');
|
||||
const table = tableContainer.createEl('table', { cls: 'sqlseal-config-table' });
|
||||
|
||||
// Table header
|
||||
const thead = table.createEl('thead');
|
||||
const headerRow = thead.createEl('tr');
|
||||
headerRow.createEl('th', { text: 'Name' });
|
||||
headerRow.createEl('th', { text: 'Preview' });
|
||||
headerRow.createEl('th', { text: 'Actions' });
|
||||
|
||||
// Table body
|
||||
const tbody = table.createEl('tbody');
|
||||
|
||||
if (this.plugin.settings.globalConfigs.length === 0) {
|
||||
const emptyRow = tbody.createEl('tr');
|
||||
const emptyCell = emptyRow.createEl('td', {
|
||||
attr: { colspan: '3' },
|
||||
cls: 'sqlseal-empty-state',
|
||||
text: 'No configurations found. Click "Add Configuration" to create one.'
|
||||
});
|
||||
} else {
|
||||
this.plugin.settings.globalConfigs.forEach((config, index) => {
|
||||
this.createConfigRow(tbody, config, index);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createConfigRow(tbody: HTMLTableSectionElement, config: GlobalChartConfig, index: number): void {
|
||||
const row = tbody.createEl('tr');
|
||||
|
||||
// Name column
|
||||
const nameCell = row.createEl('td');
|
||||
nameCell.createEl('span', {
|
||||
text: config.name,
|
||||
cls: 'sqlseal-config-name'
|
||||
});
|
||||
|
||||
// Preview column
|
||||
const previewCell = row.createEl('td');
|
||||
const previewText = this.getConfigPreview(config.config);
|
||||
previewCell.createEl('code', {
|
||||
text: previewText,
|
||||
cls: 'sqlseal-config-preview'
|
||||
});
|
||||
|
||||
// Actions column
|
||||
const actionsCell = row.createEl('td', { cls: 'sqlseal-config-actions' });
|
||||
|
||||
const editButton = actionsCell.createEl('button', {
|
||||
text: 'Edit',
|
||||
cls: 'mod-cta sqlseal-action-button'
|
||||
});
|
||||
editButton.addEventListener('click', () => {
|
||||
this.editingIndex = index;
|
||||
this.currentView = 'edit';
|
||||
this.display();
|
||||
});
|
||||
|
||||
const deleteButton = actionsCell.createEl('button', {
|
||||
text: 'Delete',
|
||||
cls: 'mod-warning sqlseal-action-button'
|
||||
});
|
||||
deleteButton.addEventListener('click', async () => {
|
||||
if (confirm(`Are you sure you want to delete the configuration "${config.name}"?`)) {
|
||||
this.plugin.settings.globalConfigs.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private displayConfigEditor(): void {
|
||||
const { containerEl } = this;
|
||||
|
||||
const isNewConfig = this.editingIndex === -1;
|
||||
const originalConfig = isNewConfig
|
||||
? { name: `config${this.plugin.settings.globalConfigs.length + 1}`, config: '{\n \n}' }
|
||||
: this.plugin.settings.globalConfigs[this.editingIndex];
|
||||
|
||||
// Create a working copy to avoid modifying the original until save
|
||||
const config = {
|
||||
name: originalConfig.name,
|
||||
config: originalConfig.config
|
||||
};
|
||||
|
||||
console.log('Editor debug - isNewConfig:', isNewConfig);
|
||||
console.log('Editor debug - editingIndex:', this.editingIndex);
|
||||
console.log('Editor debug - originalConfig:', originalConfig);
|
||||
console.log('Editor debug - working config:', config);
|
||||
console.log('Editor debug - all configs:', this.plugin.settings.globalConfigs);
|
||||
|
||||
// Header with back button
|
||||
const headerContainer = containerEl.createDiv('sqlseal-editor-header');
|
||||
const backButton = headerContainer.createEl('button', {
|
||||
text: '← Back to List',
|
||||
cls: 'sqlseal-back-button'
|
||||
});
|
||||
backButton.addEventListener('click', () => {
|
||||
this.currentView = 'list';
|
||||
this.display();
|
||||
});
|
||||
|
||||
headerContainer.createEl('h3', {
|
||||
text: isNewConfig ? 'Add New Configuration' : `Edit Configuration: ${config.name}`
|
||||
});
|
||||
|
||||
// Name input
|
||||
new Setting(containerEl)
|
||||
.setName('Configuration Name')
|
||||
.setDesc('A unique name to identify this configuration')
|
||||
.addText(text => {
|
||||
text.setValue(config.name);
|
||||
text.onChange(async (value) => {
|
||||
config.name = value;
|
||||
// Don't auto-save for existing configs since we're using a working copy
|
||||
});
|
||||
// Save on blur
|
||||
text.inputEl.addEventListener('blur', async () => {
|
||||
const newValue = text.getValue();
|
||||
config.name = newValue;
|
||||
// Don't auto-save for existing configs since we're using a working copy
|
||||
});
|
||||
});
|
||||
|
||||
// JSON editor
|
||||
const jsonContainer = containerEl.createDiv('sqlseal-json-editor-container');
|
||||
const labelContainer = jsonContainer.createDiv('sqlseal-json-label-container');
|
||||
labelContainer.createEl('label', {
|
||||
text: 'JSON5 Configuration:',
|
||||
cls: 'sqlseal-json-label'
|
||||
});
|
||||
labelContainer.createEl('small', {
|
||||
text: 'Supports unquoted keys, single quotes, trailing commas, and // comments',
|
||||
cls: 'sqlseal-json-help'
|
||||
});
|
||||
|
||||
const jsonTextarea = jsonContainer.createEl('textarea', {
|
||||
placeholder: `{
|
||||
type: 'pie',
|
||||
radius: '70%',
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10
|
||||
}
|
||||
}
|
||||
}`,
|
||||
cls: 'sqlseal-json-textarea'
|
||||
});
|
||||
|
||||
// Set the value separately to ensure it loads properly
|
||||
console.log('Setting textarea value to:', config.config);
|
||||
jsonTextarea.value = config.config || '';
|
||||
|
||||
// Also set it after a short delay to ensure DOM is ready
|
||||
requestAnimationFrame(() => {
|
||||
jsonTextarea.value = config.config || '';
|
||||
console.log('Textarea value after setting:', jsonTextarea.value);
|
||||
});
|
||||
|
||||
// Auto-save on blur and input with debounce
|
||||
let saveTimeout: NodeJS.Timeout;
|
||||
const saveConfig = async () => {
|
||||
try {
|
||||
// Use JSON5 for more flexible parsing
|
||||
JSON5.parse(jsonTextarea.value);
|
||||
jsonTextarea.classList.remove('error');
|
||||
jsonTextarea.title = '';
|
||||
config.config = jsonTextarea.value;
|
||||
// Don't auto-save since we're using a working copy
|
||||
} catch (e) {
|
||||
jsonTextarea.classList.add('error');
|
||||
jsonTextarea.title = `Invalid JSON5: ${(e as Error).message}`;
|
||||
}
|
||||
};
|
||||
|
||||
jsonTextarea.addEventListener('input', () => {
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(saveConfig, 500); // Debounce for 500ms
|
||||
});
|
||||
|
||||
jsonTextarea.addEventListener('blur', saveConfig);
|
||||
|
||||
// Helper buttons
|
||||
const helperContainer = containerEl.createDiv('sqlseal-helper-buttons');
|
||||
|
||||
const formatButton = helperContainer.createEl('button', {
|
||||
text: 'Format JSON',
|
||||
cls: 'mod-cta'
|
||||
});
|
||||
|
||||
formatButton.addEventListener('click', () => {
|
||||
try {
|
||||
// Parse with JSON5 (allows unquoted keys, trailing commas, etc.)
|
||||
const parsed = JSON5.parse(jsonTextarea.value);
|
||||
// Format as standard JSON for consistency
|
||||
const formatted = JSON.stringify(parsed, null, 2);
|
||||
jsonTextarea.value = formatted;
|
||||
config.config = formatted;
|
||||
jsonTextarea.classList.remove('error');
|
||||
jsonTextarea.title = '';
|
||||
|
||||
// Show success feedback
|
||||
formatButton.textContent = '✓ Formatted';
|
||||
formatButton.style.backgroundColor = '#4caf50';
|
||||
setTimeout(() => {
|
||||
formatButton.textContent = 'Format JSON';
|
||||
formatButton.style.backgroundColor = '';
|
||||
}, 1500);
|
||||
} catch (e) {
|
||||
formatButton.textContent = '✗ Invalid JSON5';
|
||||
formatButton.style.backgroundColor = '#ff6b6b';
|
||||
setTimeout(() => {
|
||||
formatButton.textContent = 'Format JSON';
|
||||
formatButton.style.backgroundColor = '';
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
// Action buttons
|
||||
const buttonContainer = containerEl.createDiv('sqlseal-editor-buttons');
|
||||
|
||||
const saveButton = buttonContainer.createEl('button', {
|
||||
text: isNewConfig ? 'Create Configuration' : 'Save Changes',
|
||||
cls: 'mod-cta'
|
||||
});
|
||||
|
||||
saveButton.addEventListener('click', async () => {
|
||||
try {
|
||||
JSON5.parse(config.config);
|
||||
|
||||
if (isNewConfig) {
|
||||
this.plugin.settings.globalConfigs.push(config);
|
||||
} else {
|
||||
// Update the original config with our working copy
|
||||
this.plugin.settings.globalConfigs[this.editingIndex].name = config.name;
|
||||
this.plugin.settings.globalConfigs[this.editingIndex].config = config.config;
|
||||
}
|
||||
|
||||
await this.plugin.saveSettings();
|
||||
this.currentView = 'list';
|
||||
this.display();
|
||||
} catch (e) {
|
||||
alert(`Invalid JSON5: ${(e as Error).message}`);
|
||||
}
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl('button', {
|
||||
text: 'Cancel',
|
||||
cls: 'mod-warning'
|
||||
});
|
||||
|
||||
cancelButton.addEventListener('click', () => {
|
||||
this.currentView = 'list';
|
||||
this.display();
|
||||
});
|
||||
}
|
||||
|
||||
private getConfigPreview(configString: string): string {
|
||||
try {
|
||||
const parsed = JSON5.parse(configString);
|
||||
const preview = JSON.stringify(parsed);
|
||||
return preview.length > 50 ? preview.substring(0, 47) + '...' : preview;
|
||||
} catch (e) {
|
||||
return 'Invalid JSON5';
|
||||
}
|
||||
}
|
||||
}
|
||||
214
styles.css
214
styles.css
|
|
@ -66,4 +66,218 @@
|
|||
width: 100%;
|
||||
height: calc(90vh - 40px);
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
/* Settings Panel Styles */
|
||||
.sqlseal-config-table-container {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.sqlseal-config-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sqlseal-config-table th,
|
||||
.sqlseal-config-table td {
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.sqlseal-config-table th {
|
||||
background: var(--background-modifier-hover);
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.sqlseal-config-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.sqlseal-config-table tr:hover {
|
||||
background: var(--background-modifier-hover-alt);
|
||||
}
|
||||
|
||||
.sqlseal-config-name {
|
||||
font-weight: 500;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.sqlseal-config-preview {
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 12px;
|
||||
background: var(--background-modifier-border);
|
||||
padding: 4px 8px;
|
||||
border-radius: var(--radius-s);
|
||||
color: var(--text-muted);
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sqlseal-config-actions {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sqlseal-action-button {
|
||||
padding: 4px 12px;
|
||||
margin-right: 8px;
|
||||
border: none;
|
||||
border-radius: var(--radius-s);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.sqlseal-action-button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.sqlseal-empty-state {
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
/* Editor Styles */
|
||||
.sqlseal-editor-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.sqlseal-back-button {
|
||||
padding: 6px 12px;
|
||||
background: var(--interactive-normal);
|
||||
border: none;
|
||||
border-radius: var(--radius-s);
|
||||
cursor: pointer;
|
||||
color: var(--text-normal);
|
||||
font-size: 14px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.sqlseal-back-button:hover {
|
||||
background: var(--interactive-hover);
|
||||
}
|
||||
|
||||
.sqlseal-json-editor-container {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.sqlseal-json-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.sqlseal-json-textarea {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
background: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
resize: vertical;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.sqlseal-json-textarea:focus {
|
||||
border-color: var(--interactive-accent);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sqlseal-json-textarea.error {
|
||||
border-color: var(--text-error);
|
||||
}
|
||||
|
||||
.sqlseal-editor-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-start;
|
||||
margin-top: 20px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.sqlseal-editor-buttons button {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: var(--radius-s);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
/* Documentation Styles */
|
||||
.sqlseal-description-container {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background: var(--background-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
border-left: 4px solid var(--interactive-accent);
|
||||
}
|
||||
|
||||
.sqlseal-example-container {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.sqlseal-example-code {
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 12px;
|
||||
background: var(--background-modifier-border);
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.sqlseal-feature-text {
|
||||
color: var(--text-accent);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sqlseal-json-label-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sqlseal-json-help {
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Helper Button Styles */
|
||||
.sqlseal-helper-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin: 12px 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.sqlseal-helper-buttons button {
|
||||
padding: 6px 12px;
|
||||
border: none;
|
||||
border-radius: var(--radius-s);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
Loading…
Reference in a new issue