mirror of
https://github.com/olcubo/obsidian-cubox.git
synced 2026-07-22 05:43:25 +00:00
Merge branch 'develop'
This commit is contained in:
commit
87dac77cbc
14 changed files with 243 additions and 361 deletions
|
|
@ -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
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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`;
|
||||
}
|
||||
|
|
|
|||
33
src/main.ts
33
src/main.ts
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
166
styles.css
166
styles.css
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
Loading…
Reference in a new issue