Merge branch 'develop'

This commit is contained in:
delphi 2025-04-07 20:32:05 +08:00
commit 87dac77cbc
14 changed files with 243 additions and 361 deletions

View file

@ -1,9 +1,9 @@
{
"id": "cubox-sync",
"name": "Cubox",
"version": "1.0.3",
"version": "1.0.4",
"minAppVersion": "0.15.0",
"description": "Sync your Cubox articles & annotation",
"description": "Sync your Cubox articles & annotations",
"author": "delphi-2015",
"isDesktopOnly": false
}

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "obsidian-cubox",
"version": "1.0.3",
"version": "1.0.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-cubox",
"version": "1.0.3",
"version": "1.0.4",
"license": "MIT",
"dependencies": {
"luxon": "^3.1.1",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-cubox",
"version": "1.0.3",
"version": "1.0.4",
"description": "This is a cubox plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {

View file

@ -1,4 +1,4 @@
import { Notice } from 'obsidian';
import { Notice, requestUrl } from 'obsidian';
import { ALL_FOLDERS_ID } from './modal/folderSelectModal';
import { ALL_ITEMS } from './modal/tagSelectModal';
import { ALL_STATUS_ID } from './modal/statusSelectModal';
@ -87,24 +87,31 @@ export class CuboxApi {
private async request(path: string, options: RequestInit = {}) {
const url = `${this.endpoint}${path}`;
const headers = {
const headers: Record<string, string> = {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
};
const response = await fetch(url, {
...options,
headers: {
...headers,
...options.headers,
},
});
if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
if (options.headers) {
Object.entries(options.headers).forEach(([key, value]) => {
if (typeof value === 'string') {
headers[key] = value;
}
});
}
return response.json();
const response = await requestUrl({
url: url,
method: options.method || 'GET',
body: options.body as string | ArrayBuffer | undefined,
headers: headers,
});
if (response.status >= 400) {
throw new Error(`API request failed: ${response.status}`);
}
return JSON.parse(response.text);
}
/**

View file

@ -6,6 +6,7 @@ import { filenameTemplateInstructions, metadataVariablesInstructions, contentTem
import { ALL_CONTENT_TYPES, TypeSelectModal } from './modal/typeSelectModal';
import { ALL_STATUS_ID, StatusSelectModal } from './modal/statusSelectModal';
import { ALL_ITEMS, TagSelectModal } from './modal/tagSelectModal';
import { normalizePath } from 'obsidian';
export const DEFAULT_CONTENT_TEMPLATE = `# {{{title}}}
@ -56,13 +57,13 @@ export interface CuboxSyncSettings {
export const DEFAULT_SETTINGS: CuboxSyncSettings = {
domain: '',
apiKey: '',
folderFilter: [ALL_FOLDERS_ID],
typeFilter: ALL_CONTENT_TYPES,
statusFilter: [ALL_STATUS_ID],
folderFilter: [],
typeFilter: [],
statusFilter: [],
isRead: true,
isStarred: true,
isAnnotated: true,
tagsFilter: [ALL_ITEMS],
tagsFilter: [],
syncFrequency: 30, // 分钟
targetFolder: 'Cubox',
filenameTemplate: '{{{title}}}-{{{create_time}}}',
@ -89,21 +90,16 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
const {containerEl} = this;
containerEl.empty();
// 添加 Cubox 标题和版本信息
new Setting(containerEl)
.setName('Cubox')
.setDesc(`Created by Cubox, version ${this.plugin.manifest.version}`)
// 连接设置部分
containerEl.createEl('h3', {text: 'Connect Obsidian to Your Cubox'});
new Setting(containerEl).setName('Connect obsidian to your Cubox').setHeading();
// 修改域名选择下拉框
new Setting(containerEl)
.setName('Cubox Server Domain')
.setName('Cubox server domain')
.setDesc('Select the correct domain name of the Cubox you are using.')
.addDropdown(dropdown => dropdown
.addOption('', 'Choose Region')
.addOption('', 'Choose region')
.addOption('cubox.cc', 'cubox.cc (international)')
.addOption('cubox.pro', 'cubox.pro')
.setValue(this.plugin.settings.domain)
@ -123,7 +119,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
// 添加 API Key 设置
this.apiKeySetting = new Setting(containerEl)
.setName('Your Cubox API Key')
.setName('Your Cubox API key')
.setDesc('Please select a region first')
.addText(text => {
text.setPlaceholder('Enter your API key')
@ -172,10 +168,10 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
this.updateApiKeySetting();
// 过滤器设置部分
containerEl.createEl('h3', {text: 'Filter'});
new Setting(containerEl).setName('Filter').setHeading();
new Setting(containerEl)
.setName('Folder Filter')
.setName('Folder filter')
.setDesc('Manage Cubox folders to be synced')
.addButton(button => button
.setButtonText(this.getFolderFilterButtonText())
@ -217,7 +213,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('Tag Filter')
.setName('Tag filter')
.setDesc('Manage Cubox tags to be synced')
.addButton(button => button
.setButtonText(this.getTagFilterButtonText())
@ -259,7 +255,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('Type Filter')
.setName('Type filter')
.setDesc('Manage Cubox content types to be synced')
.addButton(button => button
.setButtonText(this.getTypeFilterButtonText())
@ -285,7 +281,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('Status Filter')
.setName('Status filter')
.setDesc('Manage Cubox content status to be synced')
.addButton(button => button
.setButtonText(this.getStatusFilterButtonText())
@ -323,10 +319,10 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
}));
// 同步设置部分
containerEl.createEl('h3', {text: 'Sync'});
new Setting(containerEl).setName('Sync').setHeading();
new Setting(containerEl)
.setName('Sync Interval')
.setName('Sync interval')
.setDesc('Auto sync interval (in minutes). 0 means manual sync. Each item syncs only once. Subsequent updates won\'t be synced, and modifications in Obsidian won\'t affect Cubox. We recommend avoiding frequent updates.')
.addText(text => {
const textField = text
@ -357,7 +353,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.targetFolder)
.onChange(async (value) => {
this.plugin.settings.lastSyncCardId = null;
this.plugin.settings.targetFolder = value;
this.plugin.settings.targetFolder = normalizePath(value);
await this.plugin.saveSettings();
}));
@ -365,7 +361,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
const filenameInstructionsFragment = document.createRange().createContextualFragment(filenameTemplateInstructions);
new Setting(containerEl)
.setName('File Name Template')
.setName('File name template')
.setDesc(filenameInstructionsFragment)
.addText(text => text
.setPlaceholder('Enter file name template')
@ -391,7 +387,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
const metadataInstructionsFragment = document.createRange().createContextualFragment(metadataVariablesInstructions);
new Setting(containerEl)
.setName('Metadata Variables')
.setName('Metadata variables')
.setDesc(metadataInstructionsFragment)
.addTextArea(text => text
.setPlaceholder('Enter front matter variables')
@ -431,7 +427,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
const contentInstructionsFragment = document.createRange().createContextualFragment(contentTemplateInstructions);
new Setting(containerEl)
.setName('Content Template')
.setName('Content template')
.setDesc(contentInstructionsFragment)
.addTextArea(text => text
.setPlaceholder('Enter content template')
@ -464,7 +460,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
const dateFormatInstructionsFragment = document.createRange().createContextualFragment(cuboxDateFormat);
new Setting(containerEl)
.setName('Date Format')
.setName('Date format')
.setDesc(dateFormatInstructionsFragment)
.addText(text => text
.setPlaceholder('Enter date format')
@ -476,8 +472,8 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
}));
// 状态部分
containerEl.createEl('h3', {text: 'Status'});
containerEl.createEl('p', {text: `Last sync: ${this.plugin.formatLastSyncTime()}`});
new Setting(containerEl).setName('Status').setHeading();
new Setting(containerEl).setDesc(`Last sync: ${this.plugin.formatLastSyncTime()}`);
// 初始化帮助链接
setTimeout(() => {
@ -573,9 +569,9 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
private getFolderFilterButtonText(): string {
const folderFilters = this.plugin.settings.folderFilter;
if (!folderFilters || folderFilters.length === 0) {
return 'Manage';
return 'Select...';
} else if (folderFilters.includes(ALL_FOLDERS_ID)) {
return 'All Folders';
return 'All folders';
} else {
return `${folderFilters.length} selected`;
}
@ -584,9 +580,9 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
private getTypeFilterButtonText(): string {
const typeFilters = this.plugin.settings.typeFilter;
if (!typeFilters || typeFilters.length === 0) {
return 'Manage';
return 'Select...';
} else if (typeFilters.length === ALL_CONTENT_TYPES.length) {
return 'All Types';
return 'All types';
} else {
return `${typeFilters.length} selected`;
}
@ -595,9 +591,9 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
private getStatusFilterButtonText(): string {
const statusFilters = this.plugin.settings.statusFilter;
if (!statusFilters || statusFilters.length === 0) {
return 'Manage';
return 'Select...';
} else if (statusFilters.includes('all')) {
return 'All Items';
return 'All items';
} else {
return `${statusFilters.length} selected`;
}
@ -606,11 +602,11 @@ export class CuboxSyncSettingTab extends PluginSettingTab {
private getTagFilterButtonText(): string {
const tagFilters = this.plugin.settings.tagsFilter;
if (!tagFilters || tagFilters.length === 0) {
return 'Manage';
return 'Select...';
} else if (tagFilters.includes(ALL_ITEMS)) {
return 'All Items';
return 'All items';
} else if (tagFilters.includes('')) {
return 'No Tags';
return 'No tags';
} else {
return `${tagFilters.length} selected`;
}

View file

@ -1,4 +1,4 @@
import { addIcon, Notice, Plugin, TFile } from 'obsidian';
import { addIcon, Notice, Plugin, TFile, TFolder } from 'obsidian';
import { CuboxApi } from './cuboxApi';
import { TemplateProcessor } from './templateProcessor';
import { formatDateTime } from './utils';
@ -50,10 +50,6 @@ export default class CuboxSyncPlugin extends Plugin {
if (this.syncIntervalId) {
window.clearInterval(this.syncIntervalId);
}
// 移除样式
const styleEl = document.getElementById('cubox-sync-styles');
if (styleEl) styleEl.remove();
}
async loadSettings() {
@ -86,6 +82,12 @@ export default class CuboxSyncPlugin extends Plugin {
return;
}
// Check if filters are configured
if (this.settings.folderFilter.length === 0 || this.settings.typeFilter.length === 0 || this.settings.statusFilter.length === 0 || this.settings.tagsFilter.length === 0) {
new Notice('Please select the filter first');
return;
}
try {
// 设置同步状态为进行中
this.settings.syncing = true;
@ -149,11 +151,11 @@ export default class CuboxSyncPlugin extends Plugin {
const file = this.app.vault.getAbstractFileByPath(filePath);
if (file instanceof TFile) {
let foundMatchingId = false
await this.app.fileManager.processFrontMatter(file, (frontmatter) => {
if (frontmatter.id && frontmatter.id === article.id) {
foundMatchingId = true;
}
});
const itemId = this.app.metadataCache.getFileCache(file)?.frontmatter?.id
if (itemId && itemId === article.id) {
foundMatchingId = true;
console.log('found matching id', itemId, article.id);
}
if (foundMatchingId) {
skipCount++;
@ -178,7 +180,7 @@ export default class CuboxSyncPlugin extends Plugin {
}
finalContent += contentTemplate;
await this.app.vault.adapter.write(filePath, finalContent);
await this.app.vault.create(filePath, finalContent);
syncCount++;
@ -216,15 +218,16 @@ export default class CuboxSyncPlugin extends Plugin {
}
async ensureTargetFolder() {
const folderPath = this.settings.targetFolder;
if (!(await this.app.vault.adapter.exists(folderPath))) {
await this.app.vault.createFolder(folderPath);
const folderName = this.settings.targetFolder;
const cuboxFolder = this.app.vault.getAbstractFileByPath(folderName)
if (!(cuboxFolder instanceof TFolder)) {
await this.app.vault.createFolder(folderName)
}
}
formatLastSyncTime(): string {
if (!this.settings.lastSyncTime) {
return 'Never';
return 'never';
}
return formatDateTime(new Date(this.settings.lastSyncTime).toISOString(), 'yyyy-MM-dd HH:mm');

View file

@ -1,6 +1,5 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { CuboxFolder } from '../cuboxApi';
import { ModalStyleManager } from './modalStyles';
export const ALL_FOLDERS_ID = 'all_folders';
@ -25,9 +24,6 @@ export class FolderSelectModal extends Modal {
initialSelectedFolders.forEach(id => {
if (id) this.selectedFolders.add(id);
});
} else {
// 如果没有初始选择,默认选中"All Items"
this.selectedFolders.add(ALL_FOLDERS_ID);
}
this.onConfirm = onConfirm;
@ -37,9 +33,9 @@ export class FolderSelectModal extends Modal {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'Manage Cubox folders to be synced' });
contentEl.addClass('cubox-folder-select-modal');
contentEl.addClass('cubox-modal');
this.listEl = contentEl.createDiv({ cls: 'folder-list-container' });
this.listEl = contentEl.createDiv({ cls: 'folder-list-container cubox-list-container' });
this.footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 创建文件夹列表
@ -67,25 +63,13 @@ export class FolderSelectModal extends Modal {
this.onConfirm(resultFolders);
this.close();
});
// 添加样式
this.addStyles();
}
private addStyles() {
// 使用通用样式管理器添加样式
ModalStyleManager.addModalStyles(
'cubox-folder-modal-styles',
'cubox-folder-select-modal',
'folder-list-container'
);
}
private createFolderList() {
this.listEl.empty();
const allItemsSetting = new Setting(this.listEl)
.setName('All Items');
.setName('All items');
if (this.selectedFolders.has(ALL_FOLDERS_ID)) {
allItemsSetting.settingEl.addClass('is-selected');
@ -153,7 +137,5 @@ export class FolderSelectModal extends Modal {
onClose() {
const { contentEl } = this;
contentEl.empty();
ModalStyleManager.removeModalStyles('cubox-folder-modal-styles');
}
}

View file

@ -1,171 +0,0 @@
export class ModalStyleManager {
/**
*
* @param styleId ID
* @param modalClass
* @param listContainerClass
* @returns
*/
public static addModalStyles(
styleId: string,
modalClass: string,
listContainerClass: string
): HTMLStyleElement {
const styleEl = document.createElement('style');
styleEl.id = styleId;
styleEl.textContent = `
.${modalClass} {
display: flex;
flex-direction: column;
height: 100%;
}
.${listContainerClass} {
flex-grow: 1;
overflow-y: auto;
padding-top: 20px;
padding-right: 10px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
padding: 16px;
border-top: 1px solid var(--background-modifier-border);
}
.modal-footer button {
margin-left: 8px;
}
/* 调整设置项的布局,为左侧的 checkbox 腾出空间 */
.${listContainerClass} .setting-item {
position: relative;
padding-left: 36px;
border-radius: 4px;
transition: background-color 0.1s ease;
display: flex;
align-items: center;
min-height: 40px;
box-sizing: border-box;
}
.${listContainerClass} .setting-item:hover {
background-color: var(--background-modifier-hover);
}
/* 确保所有设置项的名称垂直居中 */
.${listContainerClass} .setting-item-name {
display: flex;
align-items: center;
padding: 0;
margin: 0;
}
/* 添加自定义 checkbox 样式 - 通用样式 */
.${listContainerClass} .setting-item::before {
content: "";
position: absolute;
width: 16px;
height: 16px;
border: 1px solid var(--checkbox-border-color, var(--background-modifier-border));
border-radius: 3px;
background-color: var(--checkbox-color, var(--background-primary));
box-sizing: border-box;
}
/* 为第一行和其他行分别设置不同的定位 */
.${listContainerClass} .setting-item:first-child::before {
left: 10px;
top: 12px; /* 为第一行特别调整 */
}
.${listContainerClass} .setting-item:not(:first-child)::before {
left: 10px;
top: 50%;
transform: translateY(-50%);
}
/* 选中状态的 checkbox */
.${listContainerClass} .setting-item.is-selected::before {
background-color: var(--checkbox-color-checked, var(--interactive-accent));
border-color: var(--checkbox-border-color-checked, var(--interactive-accent));
}
/* 添加选中状态的勾选标记 - 通用样式 */
.${listContainerClass} .setting-item.is-selected::after {
content: "";
position: absolute;
width: 5px;
height: 9px;
border-right: 2px solid var(--text-on-accent);
border-bottom: 2px solid var(--text-on-accent);
}
/* 为第一行和其他行分别设置不同的定位 */
.${listContainerClass} .setting-item:first-child.is-selected::after {
left: 14px;
top: 13px; /* 为第一行特别调整 */
transform: rotate(45deg);
}
.${listContainerClass} .setting-item:not(:first-child).is-selected::after {
left: 14px;
top: 50%;
transform: translateY(-60%) rotate(45deg);
}
/* 确保第一行和其他行的对齐一致 */
.${listContainerClass} .setting-item:first-child {
margin-top: 0;
padding-top: 0;
}
/* 确保第一行文本与复选框对齐 */
.${listContainerClass} .setting-item:first-child .setting-item-name {
margin-top: 0;
padding-top: 0;
position: relative;
top: 6px; /* 微调第一行文本位置 */
font-weight: bold; /* 为第一行文字添加加粗样式 */
}
/* 禁用状态的样式 */
.${listContainerClass} .setting-item.is-disabled {
opacity: 0.3;
pointer-events: none;
}
/* 添加横线样式用于表示禁用状态的复选框 */
.${listContainerClass} .setting-item.is-disabled::before {
background-color: var(--background-primary);
border-color: var(--background-modifier-border);
}
.${listContainerClass} .setting-item.is-disabled::after {
content: "";
position: absolute;
width: 8px;
height: 2px;
background-color: var(--text-muted);
left: 14px;
top: 50%;
transform: translateY(-50%);
border: none;
}
/* 确保所有行的高度一致 */
.${listContainerClass} .setting-item-heading,
.${listContainerClass} .setting-item-name {
line-height: normal;
margin: auto 0;
}
`;
document.head.appendChild(styleEl);
return styleEl;
}
/**
* ID的样式元素
* @param styleId ID
*/
public static removeModalStyles(styleId: string): void {
const styleEl = document.getElementById(styleId);
if (styleEl) styleEl.remove();
}
}

View file

@ -1,5 +1,4 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { ModalStyleManager } from './modalStyles';
export interface ContentStatus {
id: string;
@ -11,7 +10,7 @@ export const ALL_STATUS_ID = 'all';
export class StatusSelectModal extends Modal {
private statuses: ContentStatus[] = [
{ id: 'all', name: 'All Items' },
{ id: 'all', name: 'All items' },
{ id: 'read', name: 'Already read items only', value: true },
{ id: 'starred', name: 'Starred items only', value: true },
{ id: 'annotated', name: 'Annotated items only', value: true }
@ -41,8 +40,6 @@ export class StatusSelectModal extends Modal {
initialSelected.forEach(id => {
if (id) this.selectedStatuses.add(id);
});
} else {
this.selectedStatuses.add(ALL_STATUS_ID);
}
}
@ -50,9 +47,9 @@ export class StatusSelectModal extends Modal {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'Manage Cubox content status to be synced' });
contentEl.addClass('cubox-status-select-modal');
contentEl.addClass('cubox-modal');
this.listEl = contentEl.createDiv({ cls: 'status-list-container' });
this.listEl = contentEl.createDiv({ cls: 'status-list-container cubox-list-container' });
this.footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 创建状态列表
@ -81,16 +78,6 @@ export class StatusSelectModal extends Modal {
this.onSave(selectedIds, statusValues);
this.close();
});
this.addStyles();
}
private addStyles() {
ModalStyleManager.addModalStyles(
'cubox-status-modal-styles',
'cubox-status-select-modal',
'status-list-container'
);
}
private createStatusList() {
@ -139,10 +126,10 @@ export class StatusSelectModal extends Modal {
this.selectedStatuses.clear();
this.selectedStatuses.add(ALL_STATUS_ID);
// 当选择 All 时,所有状态值设为 true
// 当选择 All 时,所有状态值设为 false
this.statuses.forEach(status => {
if (status.id !== 'all') {
this.statusValues.set(status.id, true);
this.statusValues.set(status.id, false);
}
});
} else {
@ -154,16 +141,10 @@ export class StatusSelectModal extends Modal {
// 如果选择了其他选项,移除"All Items"
this.selectedStatuses.delete(ALL_STATUS_ID);
this.selectedStatuses.add(statusId);
this.statuses.forEach(status => {
if (status.id !== 'all') {
this.statusValues.set(status.id, status.id === statusId);
}
});
} else {
this.selectedStatuses.delete(statusId);
this.statusValues.set(statusId, false);
}
this.statusValues.set(statusId, isSelected);
}
}
@ -174,7 +155,5 @@ export class StatusSelectModal extends Modal {
onClose() {
const { contentEl } = this;
contentEl.empty();
ModalStyleManager.removeModalStyles('cubox-status-modal-styles');
}
}

View file

@ -1,6 +1,5 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { CuboxTag } from '../cuboxApi';
import { ModalStyleManager } from './modalStyles';
export const ALL_ITEMS = 'all_items';
export const NO_TAGS_ID = '';
@ -26,9 +25,6 @@ export class TagSelectModal extends Modal {
initialSelectedTags.forEach(id => {
this.selectedTags.add(id);
});
} else {
// 如果没有初始选择,默认选中"All Items"
this.selectedTags.add(ALL_ITEMS);
}
this.onConfirm = onConfirm;
@ -38,9 +34,9 @@ export class TagSelectModal extends Modal {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'Manage Cubox tags to be synced' });
contentEl.addClass('cubox-tag-select-modal');
contentEl.addClass('cubox-modal');
this.listEl = contentEl.createDiv({ cls: 'tag-list-container' });
this.listEl = contentEl.createDiv({ cls: 'tag-list-container cubox-list-container' });
this.footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 创建标签列表
@ -65,16 +61,6 @@ export class TagSelectModal extends Modal {
this.onConfirm(resultTags);
this.close();
});
this.addStyles();
}
private addStyles() {
ModalStyleManager.addModalStyles(
'cubox-tag-modal-styles',
'cubox-tag-select-modal',
'tag-list-container'
);
}
private createTagList() {
@ -82,7 +68,7 @@ export class TagSelectModal extends Modal {
// 添加"All Items"选项
const allItemsSetting = new Setting(this.listEl)
.setName('All Items');
.setName('All items');
if (this.selectedTags.has(ALL_ITEMS)) {
allItemsSetting.settingEl.addClass('is-selected');
@ -97,7 +83,7 @@ export class TagSelectModal extends Modal {
// 添加"No Tags"选项
const noTagsSetting = new Setting(this.listEl)
.setName('No Tags');
.setName('No tags');
// 如果"All Items"被选中,禁用其他选项
if (this.selectedTags.has(ALL_ITEMS)) {
@ -169,7 +155,5 @@ export class TagSelectModal extends Modal {
onClose() {
const { contentEl } = this;
contentEl.empty();
ModalStyleManager.removeModalStyles('cubox-tag-modal-styles');
}
}

View file

@ -1,5 +1,4 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { ModalStyleManager } from './modalStyles';
export const ALL_CONTENT_TYPES = [
'Article',
@ -27,11 +26,6 @@ export class TypeSelectModal extends Modal {
initialSelected.forEach(id => {
if (id) this.selectedTypes.add(id);
});
} else {
// 如果没有初始选择,默认选中所有类型
ALL_CONTENT_TYPES.forEach(type => {
this.selectedTypes.add(type);
});
}
}
@ -40,9 +34,9 @@ export class TypeSelectModal extends Modal {
contentEl.empty();
contentEl.createEl('h2', { text: 'Manage Cubox content types to be synced' });
contentEl.addClass('cubox-type-select-modal');
contentEl.addClass('cubox-modal');
this.listEl = contentEl.createDiv({ cls: 'type-list-container' });
this.listEl = contentEl.createDiv({ cls: 'type-list-container cubox-list-container' });
this.footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 创建类型列表
@ -65,27 +59,6 @@ export class TypeSelectModal extends Modal {
this.onSave(selectedTypes);
this.close();
});
this.addStyles();
}
private addStyles() {
ModalStyleManager.addModalStyles(
'cubox-modal-styles',
'cubox-type-select-modal',
'type-list-container'
);
// 添加额外的样式,覆盖第一行字体加粗的样式
const styleEl = document.createElement('style');
styleEl.id = 'cubox-type-modal-additional-styles';
styleEl.textContent = `
/* 覆盖第一行字体加粗的样式 */
.type-list-container .setting-item:first-child .setting-item-name {
font-weight: normal;
}
`;
document.head.appendChild(styleEl);
}
private createTypeList() {
@ -125,10 +98,5 @@ export class TypeSelectModal extends Modal {
onClose() {
const { contentEl } = this;
contentEl.empty();
ModalStyleManager.removeModalStyles('cubox-modal-styles');
const additionalStyleEl = document.getElementById('cubox-type-modal-additional-styles');
if (additionalStyleEl) additionalStyleEl.remove();
}
}

View file

@ -1,4 +1,5 @@
import { DateTime } from 'luxon';
import { normalizePath } from 'obsidian';
// 文件名非法字符正则表达式
export const ILLEGAL_CHAR_REGEX_FILE = /[<>:"/\\|?*\u0000-\u001F]/g;
@ -30,7 +31,7 @@ export const replaceIllegalCharsFolder = (str: string): string => {
*/
export const generateSafeFileArticleName = (title: string): string => {
const safeTitle = replaceIllegalCharsFile(title).trim();
return safeTitle;
return normalizePath(safeTitle);
};
/**

View file

@ -7,29 +7,15 @@ If your plugin does not need CSS, delete this file.
*/
/* 文件夹选择模态框样式 */
.folder-list {
max-height: 300px;
overflow-y: auto;
margin: 10px 0;
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
padding: 5px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
gap: 10px;
padding: 16px;
border-top: 1px solid var(--background-modifier-border);
}
.modal-footer button {
padding: 6px 12px;
border-radius: 4px;
background-color: var(--interactive-normal);
color: var(--text-normal);
cursor: pointer;
margin-left: 8px;
}
.modal-footer button.mod-cta {
@ -78,4 +64,150 @@ If your plugin does not need CSS, delete this file.
.reference-link:hover {
text-decoration: underline;
}
/* Modal Dialog Styles */
.cubox-modal {
display: flex;
flex-direction: column;
height: 100%;
}
.cubox-list-container {
flex-grow: 1;
overflow-y: auto;
padding-top: 20px;
padding-right: 10px;
overflow-anchor: none;
scroll-behavior: auto;
height: 100%;
max-height: 65vh;
}
/* Setting item styles for modal dialogs */
.cubox-list-container .setting-item {
position: relative;
padding-left: 36px;
border-radius: 4px;
transition: background-color 0.1s ease;
display: flex;
align-items: center;
min-height: 40px;
box-sizing: border-box;
}
.cubox-list-container .setting-item:hover {
background-color: var(--background-modifier-hover);
}
/* Setting item name styles */
.cubox-list-container .setting-item-name {
display: flex;
align-items: center;
padding: 0;
margin: 0;
}
/* Custom checkbox styles */
.cubox-list-container .setting-item::before {
content: "";
position: absolute;
width: 16px;
height: 16px;
border: 1px solid var(--checkbox-border-color, var(--background-modifier-border));
border-radius: 3px;
background-color: var(--checkbox-color, var(--background-primary));
box-sizing: border-box;
}
/* First child checkbox positioning */
.cubox-list-container .setting-item:first-child::before {
left: 10px;
top: 12px;
}
/* Other items checkbox positioning */
.cubox-list-container .setting-item:not(:first-child)::before {
left: 10px;
top: 50%;
transform: translateY(-50%);
}
/* Selected checkbox state */
.cubox-list-container .setting-item.is-selected::before {
background-color: var(--checkbox-color-checked, var(--interactive-accent));
border-color: var(--checkbox-border-color-checked, var(--interactive-accent));
}
/* Checkmark for selected items */
.cubox-list-container .setting-item.is-selected::after {
content: "";
position: absolute;
width: 5px;
height: 9px;
border-right: 2px solid var(--text-on-accent);
border-bottom: 2px solid var(--text-on-accent);
}
/* First child checkmark positioning */
.cubox-list-container .setting-item:first-child.is-selected::after {
left: 14px;
top: 13px;
transform: rotate(45deg);
}
/* Other items checkmark positioning */
.cubox-list-container .setting-item:not(:first-child).is-selected::after {
left: 14px;
top: 50%;
transform: translateY(-60%) rotate(45deg);
}
/* First item special styling */
.cubox-list-container .setting-item:first-child {
margin-top: 0;
padding-top: 0;
}
.cubox-list-container .setting-item:first-child .setting-item-name {
margin-top: 0;
padding-top: 0;
position: relative;
top: 6px;
font-weight: bold;
}
/* Disabled state styling */
.cubox-list-container .setting-item.is-disabled {
opacity: 0.3;
pointer-events: none;
}
.cubox-list-container .setting-item.is-disabled::before {
background-color: var(--background-primary);
border-color: var(--background-modifier-border);
}
.cubox-list-container .setting-item.is-disabled::after {
content: "";
position: absolute;
width: 8px;
height: 2px;
background-color: var(--text-muted);
left: 14px;
top: 50%;
transform: translateY(-50%);
border: none;
}
/* Consistent line height for all items */
.cubox-list-container .setting-item-heading,
.cubox-list-container .setting-item-name {
line-height: normal;
margin: auto 0;
}
/* Override first row bold font for type modal */
.type-list-container .setting-item:first-child .setting-item-name {
font-weight: normal;
}

View file

@ -2,5 +2,6 @@
"1.0.0": "0.15.0",
"1.0.1": "0.15.0",
"1.0.2": "0.15.0",
"1.0.3": "0.15.0"
"1.0.3": "0.15.0",
"1.0.4": "0.15.0"
}