feat: add more styles in the exported file in snippets

This commit is contained in:
mara-li 2025-04-23 13:33:37 +02:00
parent 7e0d61dcd0
commit d4ce4ee361
10 changed files with 156 additions and 71 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 135 KiB

View file

@ -5,14 +5,15 @@ import {
type PluginManifest,
type TAbstractFile,
} from "obsidian";
import type {
Prefix,
SimpleColoredFolderSettings,
StyleSettingValue,
import {
type Prefix,
type SimpleColoredFolderSettings,
STYLE_SPLIT,
type StyleSettingValue,
} from "./interfaces";
import type SimpleColoredFolder from "./main";
import { convertStyleSettings, convertToCSS, generateName, themes } from "./template";
import { minifyCss, removeExtraNewLine } from "./utils";
import { formatCss, removeExtraNewLine } from "./utils";
import dedent from "dedent";
import i18next from "i18next";
@ -39,27 +40,37 @@ export class ColorCompiler {
id: ${this.manifest.id}
settings:
-
id: FolderRadius
type: variable-number-slider
id: spf-FolderRadius
type: variable-number
title: ${i18next.t("common.radius")}
description: "Format: px"
default: 5
min: 0
max: 20
step: 1
format: px
-
id: space-between
type: variable-number-slider
id: spf-space-between
type: variable-number
default: 0.3
max: 5
step: 0.1
min: 0
format: em
description: "Format: em"
title: ${i18next.t("common.space")}
-
id: spf-saturate
type: variable-number
default: 500
title: ${i18next.t("common.saturate")}
format: "%"
description: "Format: %"
-
id: spf-saturate-hover
default: 150
type: variable-number
format: "%"
title: ${i18next.t("common.saturateHover")}
description: "Format: %"
-`);
}
createStyles(folders: TFolder[]) {
createStyles(folders: TFolder[], minify?: boolean) {
let darkTheme = `.theme-dark {`;
let lightTheme = `.theme-light {`;
let css = "";
@ -80,7 +91,7 @@ export class ColorCompiler {
darkTheme += "}";
lightTheme += "}";
stylesSettings = `${stylesSettings.replace(/-+$/, "").trimEnd()}\n*/`;
return `\n${removeExtraNewLine(stylesSettings)}\n${minifyCss(darkTheme)}\n${minifyCss(lightTheme)}\n${minifyCss(css)}`;
return `\n${removeExtraNewLine(stylesSettings)}\n${formatCss(darkTheme, minify)}\n${formatCss(lightTheme, minify)}\n${formatCss(css, minify)}`;
}
async injectToBody(content: string) {
@ -94,18 +105,38 @@ export class ColorCompiler {
}
async injectToSnippets(content: string) {
const obsidianDir = normalizePath(`${this.app.vault.configDir}/snippets`);
if (!(await this.app.vault.adapter.exists(obsidianDir))) {
const snippetsDir = normalizePath(`${this.app.vault.configDir}/snippets`);
if (!(await this.app.vault.adapter.exists(snippetsDir))) {
//create
console.log("Creating snippets directory");
await this.app.vault.adapter.mkdir(obsidianDir);
console.warn("Creating snippets directory");
await this.app.vault.adapter.mkdir(snippetsDir);
}
const rules = `/* This file is generated by Simple Colored Folder. Do not edit it manually */\n${content}`;
let rules = `/* This file is generated by Simple Colored Folder. Do not edit it manually */`;
if (this.settings.includeStyleInExport)
rules += await this.injectPluginStyles();
rules += `\n${content}\n`;
await this.app.vault.adapter.write(this.snippetPath, rules);
//enable the snippet
this.app.customCss.setCssEnabledStatus("generated.colored-folder", true);
}
private async injectPluginStyles() {
const pluginDir = normalizePath(
`${this.app.vault.configDir}/plugins/${this.manifest.id}`
);
const stylesPath = normalizePath(`${pluginDir}/styles.css`);
if (await this.app.vault.adapter.exists(stylesPath)) {
const styles = (await this.app.vault.adapter.read(stylesPath))
.split(STYLE_SPLIT)
.filter((x) => x.length > 0)[0];
return `\n\n/* ---- */\n${styles}`;
} else
console.error("The plugin folder doesn't exists.");
return "";
}
async injectStyles(reload = true) {
const folders = this.app.vault
.getAllFolders()
@ -113,8 +144,9 @@ export class ColorCompiler {
(folder: TFolder) => folder.parent && folder.parent === this.app.vault.getRoot()
);
this.style?.detach();
const style = this.createStyles(folders);
if (this.settings.exportToCSS) await this.injectToSnippets(style);
const exportToCSS = this.settings.exportToCSS;
const style = this.createStyles(folders, !exportToCSS);
if (exportToCSS) await this.injectToSnippets(style);
else await this.injectToBody(style);
if (reload) this.reload();
}

View file

@ -4,19 +4,27 @@
"color": "Text color",
"folderName": "FolderName",
"radius": "Folder radius",
"saturate": "Saturation while being dragged",
"saturateHover": "Saturation during hover",
"space": "Space between folder",
"warning": "Warning"
},
"notEnabled": "Style Settings is not installed or enabled. Please, activate it before using this plugin.",
"prefix": {
"settings": {
"generation": "The variable will be generated as follow:"
}
},
"reload": "It is possible that you need to reload Obsidian to make it works.",
"settings": {
"customCss": {
"desc": "Custom CSS template to generate a style from folders. Please use the following variables:",
"title": "Custom CSS"
},
"export": {
"desc": "Add the plugin style into the exported style snippets.",
"title": "Export plugin style"
},
"prefix": {
"desc": "Prefix for variable generation. This will allow Style Settings to works, but you can edit them also in a CSS snippets.",
"folderName": "where folder name will be standardized (removing all points, space and set to lowercase).",
@ -37,4 +45,4 @@
"warning": "This will reset your Style Settings! "
},
"warning": "Please enable 'Style Settings' plugin"
}
}

View file

@ -4,19 +4,27 @@
"color": "Couleur du texte",
"folderName": "NomDossier",
"radius": "Radius du dossier",
"saturate": "Saturation durant le déplacement",
"saturateHover": "Saturation durant la sélection",
"space": "Espace entre les dossiers",
"warning": "Attention"
},
"notEnabled": "Style Settings n'est pas installé ou activé. Merci de l'activer avant d'utiliser ce plugin.",
"prefix": {
"settings": {
"generation": "Les variables seront générées tel que :"
}
},
"reload": "Il est possible que vous ayez besoin de recharger Obsidian pour que cela fonctionne.",
"settings": {
"customCss": {
"desc": "Modèle CSS personnalisé qui sera utilisé pour générer un CSS depuis les dossiers. Merci d'utiliser les variables suivantes : ",
"title": "CSS personnalisé"
},
"export": {
"desc": "Ajouter les styles du plugin dans le fichier exporté.",
"title": "Exporter le style du plugin"
},
"prefix": {
"desc": "Préfixe pour la génération de variable. Cela permettra au plugin \"Style settings\" de fonctionner, mais vous pourrez aussi les utiliser dans un snippet css.",
"folderName": "où le nom du dossier sera normalisé (en supprimant tous les points, les espaces et en mettant le nom en minuscules).",
@ -37,4 +45,4 @@
"warning": "Cela aura pour conséquence de réinitialiser vos paramètres Style Settings ! "
},
"warning": "Merci d'activer le module \"Style Settings\"."
}
}

View file

@ -11,6 +11,11 @@ export interface SimpleColoredFolderSettings {
customStyleSettings: string;
exportToCSS: boolean;
defaultColors: Colors;
/**
* Allow to includes some other style in the export to CSS file
* See the first part of the style.css file for more information.
*/
includeStyleInExport: boolean;
}
export type ThemedColors = {
@ -42,6 +47,9 @@ export const DEFAULT_SETTINGS: SimpleColoredFolderSettings = {
customStyleSettings: "",
exportToCSS: false,
defaultColors: DEFAULT_COLORS,
includeStyleInExport: false,
};
export type StyleSettingValue = number | string | boolean;
export const STYLE_SPLIT = "/** ---- **/";

View file

@ -42,7 +42,6 @@ export default class SimpleColoredFolder extends Plugin {
}
await this.compiler.injectStyles();
this.app.workspace.trigger("parse-style-settings");
this.addSettingTab(new SimpleColoredFolderSettingTab(this.app, this));
this.app.vault.on("rename", async (file, oldPath) => {
@ -60,7 +59,6 @@ export default class SimpleColoredFolder extends Plugin {
this.compiler.style?.detach();
this.compiler.style?.remove();
this.app.workspace.trigger("css-change");
this.app.workspace.trigger("parse-style-settings");
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());

View file

@ -1,9 +1,10 @@
import { type App, PluginSettingTab, sanitizeHTMLToDom, Setting } from "obsidian";
import { type App, MarkdownRenderer, PluginSettingTab, sanitizeHTMLToDom, Setting } from "obsidian";
import type SimpleColoredFolder from "./main";
import i18next from "i18next";
import type { ColorCompiler } from "./compiler";
import type { SimpleColoredFolderSettings } from "./interfaces";
import { PickerSettingsComponent } from "./color-picker";
import dedent from "dedent";
export class SimpleColoredFolderSettingTab extends PluginSettingTab {
plugin: SimpleColoredFolder;
@ -17,8 +18,19 @@ export class SimpleColoredFolderSettingTab extends PluginSettingTab {
this.compiler = plugin.compiler;
}
display(): void {
async display() {
const { containerEl } = this;
containerEl.empty();
if (!this.app.plugins.enabledPlugins.has("obsidian-style-settings")) {
await MarkdownRenderer.render(this.app, dedent`> [!warning]
> ${i18next.t("notEnabled")}
>
> ${i18next.t("reload")}
`, this.containerEl, "", this.plugin);
return;
}
this.containerEl.addClass(`spf`);
containerEl.empty();
@ -31,8 +43,22 @@ export class SimpleColoredFolderSettingTab extends PluginSettingTab {
this.settings.exportToCSS = value;
await this.plugin.saveSettings();
await this.compiler.injectStyles();
this.display();
})
);
if (this.settings.exportToCSS) {
new Setting(containerEl)
.setName(i18next.t("settings.export.title"))
.setDesc(i18next.t("settings.export.desc"))
.setClass("no-border")
.addToggle((cb) =>
cb.setValue(this.settings.includeStyleInExport).onChange(async (value) => {
this.settings.includeStyleInExport = value;
await this.plugin.saveSettings();
await this.compiler.injectStyles();
})
);
}
this.containerEl.createEl("hr");
new Setting(containerEl).setName("Default color").setClass("no-border").setHeading();
@ -61,12 +87,12 @@ export class SimpleColoredFolderSettingTab extends PluginSettingTab {
this.containerEl.createEl("hr");
new Setting(containerEl).setName(i18next.t("settings.prefix.title")).setHeading();
this.containerEl.appendChild(
sanitizeHTMLToDom(`${i18next.t("settings.prefix.desc")}<br>${i18next.t("prefix.settings.generation")} <code>prefix.[${i18next.t("common.folderName")}]</code> ${i18next.t("settings.prefix.folderName")}
<div data-callout-metadata="" data-callout-fold="" data-callout="warning" class="callout"><div class="callout-title" dir="auto"><div class="callout-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-alert-triangle"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg></div><div class="callout-title-inner">${i18next.t("common.warning")}</div></div><div class="callout-content">
<p dir="auto">${i18next.t("settings.warning")}</p>
</div></div>`)
);
await MarkdownRenderer.render(this.app, dedent`
${i18next.t("settings.prefix.desc")}
${i18next.t("prefix.settings.generation")} « \`prefix.${i18next.t("common.folderName")}\` » ${i18next.t("settings.prefix.folderName")}
> [!warning] ${i18next.t("common.warning")}
> ${i18next.t("settings.warning")}
`, this.containerEl, "", this.plugin);
new Setting(containerEl)
.setName(i18next.t("common.color"))

View file

@ -1,6 +1,9 @@
/** ---- **/
:root {
--FolderRadius: 5px;
--space-between: 1px;
--spf-FolderRadius: 5px;
--spf-space-between: 1px;
--spf-saturate-dragging: 500%;
--spaf-saturate-hover: 150%;
}
.nav-file-title,
@ -29,59 +32,49 @@ body:not(.is-grabbing) .nav-folder-title:hover .nav-folder-collapse-indicator {
.nav-folder-title.is-being-dragged-over {
background-color: transparent !important;
color: currentColor !important;
filter: saturate(500%) !important;
filter: saturate(var(--spf-saturate-dragging)) !important;
}
.nav-files-container.node-insert-event
> div
> .tree-item.nav-folder
> .tree-item-self.nav-folder-title {
.nav-files-container.node-insert-event>div>.tree-item.nav-folder>.tree-item-self.nav-folder-title {
margin-top: 5px !important;
}
.nav-files-container.node-insert-event
> div
> .tree-item.nav-folder
> .tree-item-self.nav-folder-title {
.nav-files-container.node-insert-event>div>.tree-item.nav-folder>.tree-item-self.nav-folder-title {
margin-bottom: 0;
}
/*& Set up explorer container margins */
.nav-files-container {
margin: 0 5px;
}
.nav-folder.mod-root > .nav-folder-children > .nav-folder > .nav-folder-title {
.nav-folder.mod-root>.nav-folder-children>.nav-folder>.nav-folder-title {
padding-left: 5px;
margin-top: 7px;
/* space between top level sections */
border-radius: var(--FolderRadius) var(--FolderRadius) 0 0;
border-radius: var(--spf-FolderRadius) var(--spf-FolderRadius) 0 0;
}
.nav-folder.mod-root
> .nav-folder-children
> .nav-folder.is-collapsed
> .nav-folder-title {
border-radius: var(--FolderRadius);
.nav-folder.mod-root>.nav-folder-children>.nav-folder.is-collapsed>.nav-folder-title {
border-radius: var(--spf-FolderRadius);
}
.nav-folder.mod-root > .nav-folder-children > .nav-folder > .nav-folder-children {
.nav-folder.mod-root>.nav-folder-children>.nav-folder>.nav-folder-children {
padding-left: 0;
border-radius: 0 0 var(--FolderRadius) var(--FolderRadius);
border-radius: 0 0 var(--spf-FolderRadius) var(--spf-FolderRadius);
}
.nav-file-title[class*="is-active"],
.nav-file-title:hover,
body:not(.is-grabbing) .nav-folder-title:hover {
border-radius: var(--FolderRadius);
border-radius: var(--spf-FolderRadius);
}
.nav-files-container > div > .tree-item.nav-folder:not(.mod-root) {
margin-bottom: var(--space-between) !important;
.nav-files-container>div>.tree-item.nav-folder:not(.mod-root) {
margin-bottom: var(--spf-space-between) !important;
}
/** ---- Settings ---- **/
/** ---- **/
/* ---- Settings ---- */
.spf textarea {
width: 100%;
@ -109,14 +102,14 @@ body:not(.is-grabbing) .nav-folder-title:hover {
--hr-thickness: 1px;
}
/* Aligne le composant avec les autres settings */
/* Settings alignments */
.spf .picker-settings-component .setting-item-control {
display: flex;
gap: 16px;
align-items: center;
}
/* Le wrapper des deux pickers */
/* Picker wrapper */
.spf .dual-alpha-picker-controls {
display: flex;
gap: 4px;
@ -129,7 +122,7 @@ body:not(.is-grabbing) .nav-folder-title:hover {
position: relative;
}
/* Conteneur individuel dun picker + bouton reset */
/* Picker + reset button */
.spf .alpha-picker-wrapper {
display: flex;
flex-direction: column;
@ -148,7 +141,7 @@ body:not(.is-grabbing) .nav-folder-title:hover {
background-color: #1e1e1e;
}
/* Boutons de reset (↺) */
/* Reset button */
.spf .alpha-picker-wrapper .clickable-icon {
font-size: 14px;
opacity: 0.6;
@ -162,4 +155,4 @@ body:not(.is-grabbing) .nav-folder-title:hover {
.spf .setting-item .pickr .pcr-button:after,
.spf .setting-item .pickr .pcr-button:before {
border-radius: 0 !important;
}
}

View file

@ -34,7 +34,7 @@ export function convertToCSS(folderName: string, prefix: Prefix, template: strin
.tree-item.nav-folder:has([data-path="${folderName}"]) {
background-color: var(${variableNames.bg}) !important;
border-radius: var(--FolderRadius);
border-radius: var(--spf-FolderRadius);
}
.nav-file-title[class*="is-active"][data-path^="${folderName}"],
@ -43,7 +43,7 @@ export function convertToCSS(folderName: string, prefix: Prefix, template: strin
.nav-folder-title[class*="is-active"][data-path^="${folderName}"] {
color: var(${variableNames.color}) !important;
background-color: var(${variableNames.bg}) !important;
filter: saturate(150%);
filter: saturate(var(--spf-saturate-hover));
}
${dedent(remplaceTemplate(template, folderName, variableNames.bg, variableNames.color))}

View file

@ -1,8 +1,20 @@
export function minifyCss(css: string): string {
export function formatCss(css: string, minify?: boolean): string {
const styleSheet = new CSSStyleSheet();
styleSheet.replaceSync(css);
return [...styleSheet.cssRules].map((rule) => rule.cssText).join("");
const formattedCss = [...styleSheet.cssRules].map((rule) => {
if (!minify) {
return rule.cssText
.replaceAll("{", "{\n ")
.replaceAll(";", ";\n ")
.replace(/\n\s*}/g, "\n}")
.replace(/\s*,/g, ",\n")
.replace(/ \./g, ".")
}
return rule.cssText
}).join(minify ? "" : "\n");
return formattedCss;
}
export function standardize(str: string) {
return str
.standardize()