diff --git a/manifest.json b/manifest.json index 73d5cca..559e46c 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "sync-engine", "name": "Sync Engine", - "version": "3.0.4", + "version": "3.0.5", "minAppVersion": "1.12.3", "authorUrl": "https://hesprs.github.io", "description": "The next-generation syncing plugin: Fast · Free · Extend with Modules. Supports WebDAV and S3.", diff --git a/modules.json b/modules.json index 7c476e0..2843b18 100644 --- a/modules.json +++ b/modules.json @@ -29,29 +29,29 @@ { "id": "i18n-zh", "name": "I18n 简体中文", - "version": "0.0.10", + "version": "0.0.11", "description": "Simplified Chinese UI language pack. / 简体中文介面语言包。", "icon": "languages", "main": "https://sync.consensia.cc/modules/i18n-zh.js", - "minPluginVersion": "3.0.3" + "minPluginVersion": "3.0.5" }, { "id": "i18n-zh-TW", "name": "I18n 繁體中文", - "version": "0.0.2", + "version": "0.0.3", "description": "Traditional Chinese UI language pack. / 繁體中文介面語言包。", "icon": "languages", "main": "https://sync.consensia.cc/modules/i18n-zh-TW.js", - "minPluginVersion": "3.0.3" + "minPluginVersion": "3.0.5" }, { "id": "i18n-ru", "name": "I18n Русский", - "version": "0.0.2", + "version": "0.0.3", "description": "Russian UI language pack. / Пакет русского интерфейса.", "icon": "languages", "main": "https://sync.consensia.cc/modules/i18n-ru.js", - "minPluginVersion": "3.0.3" + "minPluginVersion": "3.0.5" }, { "id": "smart-merge", diff --git a/packages/i18n/src/ru/translations.ts b/packages/i18n/src/ru/translations.ts index 4eeedfe..b431ceb 100644 --- a/packages/i18n/src/ru/translations.ts +++ b/packages/i18n/src/ru/translations.ts @@ -104,7 +104,6 @@ const ru: Translations = { development: 'Разработка', diffMatchPatch: 'Объединить', disableModule: 'Отключить модуль', - disabled: 'Отключено', done: 'Готово', download: 'Скачать', downloadModule: 'Скачать модуль', @@ -114,13 +113,14 @@ const ru: Translations = { editSources: 'Редактировать источники', enable: 'Включить', enableModule: 'Включить модуль', - enabled: 'Включено', exclusionRules: 'Правила исключения', exclusionRulesDescription: 'Файлы и папки, соответствующие этим glob-шаблонам, не будут синхронизироваться. Не забудьте указать расширения файлов (например, .md), если хотите исключить файлы.', executing: 'Выполняется', export: 'Экспорт', - exportLogsDescription: 'Экспортировать логи плагина в файл внутри хранилища.', + exportLogsDescription: + 'Экспортировать логи плагина в файл внутри хранилища. Укажите папку для экспорта логов в поле ниже.', + exportLogsDirectoryPlaceholder: 'Укажите папку для экспорта логов', exportLogsFailed: 'Не удалось экспортировать логи', exportLogsToFile: 'Экспортировать логи в файл', failed: 'Ошибка', @@ -204,10 +204,10 @@ const ru: Translations = { noMatchingModulesFound: 'Подходящие модули не найдены.', noModulesAvailable: 'Нет доступных модулей.', none: 'Нет', - notInstalled: 'Не установлено', noticeStatusOnMobile: 'Уведомления о статусе на мобильных устройствах', noticeStatusOnMobileDescription: 'Отображать всплывающее уведомление на мобильных устройствах во время синхронизации. Заменяет строку состояния, используемую на ПК.', + official: 'Официальный', omittedInvalidEntry: 'Пропущено недействительных записей: {{count}}.', openPanel: 'Открыть панель', realtimeSync: 'Синхронизация в реальном времени', diff --git a/packages/i18n/src/zh-TW/translations.ts b/packages/i18n/src/zh-TW/translations.ts index 1d1f48e..6f8df23 100644 --- a/packages/i18n/src/zh-TW/translations.ts +++ b/packages/i18n/src/zh-TW/translations.ts @@ -102,7 +102,6 @@ const zhTW: Translations = { development: '開發者選項', diffMatchPatch: '文字合併', disableModule: '停用模組', - disabled: '已停用', done: '完成', download: '下載', downloadModule: '下載模組', @@ -112,13 +111,13 @@ const zhTW: Translations = { editSources: '編輯來源', enable: '啟用', enableModule: '啟用模組', - enabled: '已啟用', exclusionRules: '排除規則', exclusionRulesDescription: '符合這些 Glob 萬用字元模式的檔案或資料夾將不會進行同步。若要排除特定檔案,請記得加上副檔名(例如 .md)。', executing: '執行中', export: '匯出', - exportLogsDescription: '將外掛程式紀錄匯出至儲存庫中的檔案。', + exportLogsDescription: '將外掛程式紀錄匯出至儲存庫中的檔案。請在欄位中設定紀錄匯出目錄。', + exportLogsDirectoryPlaceholder: '設定紀錄匯出的目標目錄', exportLogsFailed: '匯出紀錄失敗', exportLogsToFile: '匯出紀錄至檔案', failed: '失敗', @@ -198,9 +197,9 @@ const zhTW: Translations = { noMatchingModulesFound: '未找到符合條件的模組。', noModulesAvailable: '無可用模組。', none: '無', - notInstalled: '未安裝', noticeStatusOnMobile: '行動裝置同步狀態通知', noticeStatusOnMobileDescription: '同步進行時於行動裝置上顯示通知訊息(取代桌面版的狀態列)。', + official: '官方', omittedInvalidEntry: '已忽略 {{count}} 項無效條目。', openPanel: '開啟面板', realtimeSync: '即時同步', diff --git a/packages/i18n/src/zh/translations.ts b/packages/i18n/src/zh/translations.ts index 332e332..9d410ea 100644 --- a/packages/i18n/src/zh/translations.ts +++ b/packages/i18n/src/zh/translations.ts @@ -90,7 +90,6 @@ const zh: Translations = { development: '开发', diffMatchPatch: '合并', disableModule: '禁用模块', - disabled: '已禁用', done: '完成', download: '下载', downloadModule: '下载模块', @@ -100,13 +99,13 @@ const zh: Translations = { editSources: '编辑源', enable: '启用', enableModule: '启用模块', - enabled: '已启用', exclusionRules: '排除规则', exclusionRulesDescription: '匹配这些 Glob 模式的文件 / 文件夹将不会被同步。如果您想排除文件,请记得添加文件扩展名(例如 .md)。', executing: '正在执行', export: '导出', - exportLogsDescription: '将插件日志导出到库中的文件中。', + exportLogsDescription: '将插件日志导出到仓库中的文件。请在输入框中设置日志导出目录。', + exportLogsDirectoryPlaceholder: '设置日志导出的目录', exportLogsFailed: '导出日志失败', exportLogsToFile: '导出日志到文件', failed: '失败', @@ -185,10 +184,10 @@ const zh: Translations = { noMatchingModulesFound: '未找到匹配的模块。', noModulesAvailable: '没有可用模块。', none: '无', - notInstalled: '未安装', noticeStatusOnMobile: '移动端同步状态提示', noticeStatusOnMobileDescription: '同步进行时在移动设备上显示通知提示。在桌面端则会替换状态栏显示。', + official: '官方', omittedInvalidEntry: '已忽略 {{count}} 条无效条目。', openPanel: '打开面板', realtimeSync: '实时同步', diff --git a/packages/plugin/CHANGELOG.md b/packages/plugin/CHANGELOG.md index 858dff0..a92544d 100644 --- a/packages/plugin/CHANGELOG.md +++ b/packages/plugin/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## Sync Engine v3.0.5 - 2026-08-12 + +- Fixed the bug that non-root hidden files and folders cannot be discovered in syncing by @pedrovillalobos. +- Improved module card display and adjusted panel size on mobile by @hesprs. +- Made log export directory configurable by @hesprs. + ## Sync Engine v3.0.4 - 2026-08-10 - Fixed the bug that "Rename and keep both" conflict resolution strategy duplicates content locally instead of propagate over remote. diff --git a/packages/plugin/dist/dev.spec.d.ts b/packages/plugin/dist/dev.spec.d.ts index 5784217..36a2be7 100644 --- a/packages/plugin/dist/dev.spec.d.ts +++ b/packages/plugin/dist/dev.spec.d.ts @@ -1,4 +1,4 @@ -import { Ct as FolderStat, St as FileStat, f as Request, p as RequestParam, ut as Fs, vt as RootFs, xt as Binary, yt as WrappedFs } from "./index-zKP375wq.spec.js"; +import { Ct as FolderStat, St as FileStat, f as Request, p as RequestParam, ut as Fs, vt as RootFs, xt as Binary, yt as WrappedFs } from "./index-Ddt5UAXp.spec.js"; //#region src/sdk/debug-wrapper.d.ts declare function debugWrapper(original: Fs, log: (content: string) => void): WrappedFs; //#endregion diff --git a/packages/plugin/dist/index-zKP375wq.spec.d.ts b/packages/plugin/dist/index-Ddt5UAXp.spec.d.ts similarity index 99% rename from packages/plugin/dist/index-zKP375wq.spec.d.ts rename to packages/plugin/dist/index-Ddt5UAXp.spec.d.ts index b1fd665..5d1fbe0 100644 --- a/packages/plugin/dist/index-zKP375wq.spec.d.ts +++ b/packages/plugin/dist/index-Ddt5UAXp.spec.d.ts @@ -545,6 +545,7 @@ declare class Observability { private readonly progressText; readonly settings: { noticeStatusOnMobile: boolean; + exportLogsDirectory: string; }; readonly i18n: { startSync: string; @@ -802,6 +803,7 @@ type DevelopmentSettingTranslations = { clearRecordsDescription: string; export: string; exportLogsDescription: string; + exportLogsDirectoryPlaceholder: string; exportLogsToFile: string; }; //#endregion @@ -948,20 +950,18 @@ declare class Bootstrap { //#region src/components/module-management/index.d.ts type ModuleManagementTranslations = { disableModule: string; - disabled: string; downloadModule: string; enableModule: string; - enabled: string; installed: string; loadingModules: string; noInstalledModulesFound: string; noMatchingModulesFound: string; noModulesAvailable: string; - notInstalled: string; updateAvailable: string; updateModule: string; deleteModule: string; editModuleInformation: string; + official: string; }; //#endregion //#region src/components/SourceEditorModal.d.ts diff --git a/packages/plugin/dist/index.spec.d.ts b/packages/plugin/dist/index.spec.d.ts index 71d6174..0a43aeb 100644 --- a/packages/plugin/dist/index.spec.d.ts +++ b/packages/plugin/dist/index.spec.d.ts @@ -1,2 +1,2 @@ -import { $ as TranslationResource, A as SyncTerminateReason, B as MoveRemote, C as SelectFromContext, Ct as FolderStat, D as writeWithValue, Dt as RecordStatsMap, E as readWithSize, Et as RecordStat, F as Upload, G as BaseTask, H as Download, I as ResolveConflict, J as TaskNames, K as ConflictResolver, L as RemoveRemote, M as Decider, N as DeciderInput, O as prefixWrapper, Ot as Stat, P as TaskFactory, Q as Translate, R as RemoveRecord, S as ModuleMeta, St as FileStat, T as pipe, Tt as Progress, U as CreateRemoteDir, V as MoveLocal, W as AddRecord, X as Fragment, Y as RecordStore, Z as ObsidianLanguageCode, _ as Events, _t as OutputAtom, a as FsWrapperEntry, at as StoreOperations, b as ExistingMemoryDB, bt as WriteAtom, c as RemoteFsEntry, ct as CustomAtom, d as RemoteRequestMiddlewareEntry, dt as InputAtom, et as Dispatch, f as Request, ft as ListReporter, g as Context, gt as OptimizerOutput, h as SettingEntry, ht as OptimizerInput, i as DeciderEntry, it as StoreAsync, j as CreateLocalDir, k as setNeedMigration, kt as StatsMap, l as RemoteLister, lt as DeleteAtom, m as RequestResponse, mt as MoveAtom, n as CheckConnectionResult, nt as DatabaseAsync, o as LocalRequestMiddlewareEntry, ot as StoreSync, p as RequestParam, pt as MkdirAtom, q as ConflictResolverPayload, r as ConflictResolverEntry, rt as DatabaseSync, s as OptimizerEntry, st as BatchOptimizer, t as VaultRequest, tt as On, u as RemoteListerEntry, ut as Fs, v as Settings, vt as RootFs, w as digOriginal, wt as MaybePromise, x as AugmentedModuleMeta, xt as Binary, y as Translations, yt as WrappedFs, z as RemoveLocal } from "./index-zKP375wq.spec.js"; +import { $ as TranslationResource, A as SyncTerminateReason, B as MoveRemote, C as SelectFromContext, Ct as FolderStat, D as writeWithValue, Dt as RecordStatsMap, E as readWithSize, Et as RecordStat, F as Upload, G as BaseTask, H as Download, I as ResolveConflict, J as TaskNames, K as ConflictResolver, L as RemoveRemote, M as Decider, N as DeciderInput, O as prefixWrapper, Ot as Stat, P as TaskFactory, Q as Translate, R as RemoveRecord, S as ModuleMeta, St as FileStat, T as pipe, Tt as Progress, U as CreateRemoteDir, V as MoveLocal, W as AddRecord, X as Fragment, Y as RecordStore, Z as ObsidianLanguageCode, _ as Events, _t as OutputAtom, a as FsWrapperEntry, at as StoreOperations, b as ExistingMemoryDB, bt as WriteAtom, c as RemoteFsEntry, ct as CustomAtom, d as RemoteRequestMiddlewareEntry, dt as InputAtom, et as Dispatch, f as Request, ft as ListReporter, g as Context, gt as OptimizerOutput, h as SettingEntry, ht as OptimizerInput, i as DeciderEntry, it as StoreAsync, j as CreateLocalDir, k as setNeedMigration, kt as StatsMap, l as RemoteLister, lt as DeleteAtom, m as RequestResponse, mt as MoveAtom, n as CheckConnectionResult, nt as DatabaseAsync, o as LocalRequestMiddlewareEntry, ot as StoreSync, p as RequestParam, pt as MkdirAtom, q as ConflictResolverPayload, r as ConflictResolverEntry, rt as DatabaseSync, s as OptimizerEntry, st as BatchOptimizer, t as VaultRequest, tt as On, u as RemoteListerEntry, ut as Fs, v as Settings, vt as RootFs, w as digOriginal, wt as MaybePromise, x as AugmentedModuleMeta, xt as Binary, y as Translations, yt as WrappedFs, z as RemoveLocal } from "./index-Ddt5UAXp.spec.js"; export { type AddRecord, type AugmentedModuleMeta, type BaseTask, type BatchOptimizer, type Binary, type CheckConnectionResult, type ConflictResolver, type ConflictResolverEntry, type ConflictResolverPayload, type Context, type CreateLocalDir, type CreateRemoteDir, type CustomAtom, type DatabaseAsync, type DatabaseSync, type Decider, type DeciderEntry, type DeciderInput, type DeleteAtom, type Dispatch, type Download, type Events, type ExistingMemoryDB, type FileStat, type FolderStat, type Fragment, type Fs, type FsWrapperEntry, type InputAtom, type ListReporter, type LocalRequestMiddlewareEntry, type MaybePromise, type MkdirAtom, type ModuleMeta, type MoveAtom, type MoveLocal, type MoveRemote, type ObsidianLanguageCode, type On, type OptimizerEntry, type OptimizerInput, type OptimizerOutput, type OutputAtom, type Progress, type RecordStat, type RecordStatsMap, type RecordStore, type RemoteFsEntry, type RemoteLister, type RemoteListerEntry, type RemoteRequestMiddlewareEntry, type RemoveLocal, type RemoveRecord, type RemoveRemote, type Request, type RequestParam, type RequestResponse, type ResolveConflict, type RootFs, SelectFromContext, type SettingEntry, type Settings, type Stat, type StatsMap, type StoreAsync, type StoreOperations, type StoreSync, type SyncTerminateReason, type TaskFactory, type TaskNames, type Translate, type TranslationResource, type Translations, type Upload, type VaultRequest, type WrappedFs, type WriteAtom, digOriginal, pipe, prefixWrapper, readWithSize, setNeedMigration, writeWithValue }; \ No newline at end of file diff --git a/packages/plugin/package.json b/packages/plugin/package.json index aaeea9f..62ea660 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@hesprs/sync-engine-sdk", - "version": "3.0.4", + "version": "3.0.5", "description": "Official SDK for developing modules targeting Sync Engine, the extensible Obsidian syncing plugin.", "keywords": [ "obsidian-plugin", diff --git a/packages/plugin/src/components/module-management/Card.tsx b/packages/plugin/src/components/module-management/Card.tsx index 6316fff..0e01fad 100644 --- a/packages/plugin/src/components/module-management/Card.tsx +++ b/packages/plugin/src/components/module-management/Card.tsx @@ -3,6 +3,7 @@ import { Show, createEffect } from 'solid-js'; import { compare } from 'verkit'; import type { AugmentedModuleMeta } from '@/modules/Extensibility'; import type { MaybePromise } from '@/types'; +import { OFFICIAL_SOURCE } from '@/modules/Extensibility'; import type { ModuleManagementContext, PendingAction } from './index'; import ModuleEditorModal from '../ModuleEditorModal'; @@ -36,7 +37,7 @@ export default function Card(props: { }; return ( -
+
- - {isInstalled() - ? props.ctx.translate('installed') - : props.ctx.translate('notInstalled')} - + + {props.ctx.translate('official')} + - - {props.isLoaded - ? props.ctx.translate('enabled') - : props.ctx.translate('disabled')} - + {props.ctx.translate('installed')} - {props.ctx.translate('updateAvailable')} + {props.ctx.translate('updateAvailable')}
diff --git a/packages/plugin/src/components/module-management/index.ts b/packages/plugin/src/components/module-management/index.ts index 5662192..49d8684 100644 --- a/packages/plugin/src/components/module-management/index.ts +++ b/packages/plugin/src/components/module-management/index.ts @@ -9,20 +9,18 @@ export type PendingAction = 'delete' | 'disable' | 'download' | 'enable' | 'edit export type ModuleManagementTranslations = { disableModule: string; - disabled: string; downloadModule: string; enableModule: string; - enabled: string; installed: string; loadingModules: string; noInstalledModulesFound: string; noMatchingModulesFound: string; noModulesAvailable: string; - notInstalled: string; updateAvailable: string; updateModule: string; deleteModule: string; editModuleInformation: string; + official: string; }; export type ModuleManagementHooks = { diff --git a/packages/plugin/src/en.ts b/packages/plugin/src/en.ts index 86d3f45..0ac9979 100644 --- a/packages/plugin/src/en.ts +++ b/packages/plugin/src/en.ts @@ -104,7 +104,6 @@ const en: Translations = { development: 'Development', diffMatchPatch: 'Merge', disableModule: 'Disable module', - disabled: 'Disabled', done: 'Done', download: 'Download', downloadModule: 'Download module', @@ -114,13 +113,14 @@ const en: Translations = { editSources: 'Edit sources', enable: 'Enable', enableModule: 'Enable module', - enabled: 'Enabled', exclusionRules: 'Exclusion rules', exclusionRulesDescription: 'Files / folders matching these glob patterns will not be synced. Please remember to add file extensions (for example, .md) if you want to exclude files.', executing: 'Executing', export: 'Export', - exportLogsDescription: 'Export plugin logs to a file in the vault.', + exportLogsDescription: + 'Export plugin logs to a file in the vault. Set the log export directory in the field.', + exportLogsDirectoryPlaceholder: 'Set the directory to export logs to', exportLogsFailed: 'Failed to export logs', exportLogsToFile: 'Export logs to file', failed: 'Failed', @@ -204,10 +204,10 @@ const en: Translations = { noMatchingModulesFound: 'No matching modules found.', noModulesAvailable: 'No modules available.', none: 'None', - notInstalled: 'Not installed', noticeStatusOnMobile: 'Notice sync status on mobile', noticeStatusOnMobileDescription: 'Display a notice on mobile devices when synchronization is in progress. Replaces the status bar on desktop.', + official: 'Official', omittedInvalidEntry: 'Omitted {{count}} invalid entry(s).', openPanel: 'Open panel', realtimeSync: 'Realtime sync', diff --git a/packages/plugin/src/global.css b/packages/plugin/src/global.css index 47820cb..9efa385 100644 --- a/packages/plugin/src/global.css +++ b/packages/plugin/src/global.css @@ -56,3 +56,24 @@ input[type='checkbox']:indeterminate { flex-direction: column; } } + +.sync-engine-large-modal { + width: var(--modal-width); + height: var(--modal-height); + max-width: var(--modal-max-width); + max-height: var(--modal-max-height); +} + +.is-phone .sync-engine-large-modal { + height: 100%; + margin-top: auto; +} + +.sync-engine-card .flair { + margin: 0; +} + +.sync-engine-card .flair.prominent { + --flair-background: var(--color-accent); + --flair-color: var(--text-on-accent); +} diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index d92ccf0..f5dba5f 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -7,7 +7,7 @@ import type { AddRibbonIcon } from './modules/Observability'; import type { GlobMatchRule } from './types'; import Bootstrap from './modules/Bootstrap'; import EventBus from './modules/EventBus'; -import Extensibility from './modules/Extensibility'; +import Extensibility, { OFFICIAL_SOURCE } from './modules/Extensibility'; import I18n from './modules/I18n'; import ModulesModal from './modules/ModulesModal'; import Observability from './modules/Observability'; @@ -17,10 +17,6 @@ import Scheduler from './modules/Scheduler'; import Storage from './modules/Storage'; import Sync from './modules/Sync'; -function createGlobMatchOptions(expr: string) { - return { caseSensitive: false, expr }; -} - const internalModules = [ EventBus, I18n, @@ -84,14 +80,15 @@ export default class SyncEngine extends Plugin { '**/~$*.xlsx', '.trash', this.app.vault.configDir, - ].map(createGlobMatchOptions), + ].map((expr) => ({ caseSensitive: false, expr })), + exportLogsDirectory: 'Sync Engine Logs/', inclusionRules: [], maxFileSize: { enabled: false, value: 31_457_280 }, maxMemoryConsumption: { enabled: true, value: 100 * 1024 ** 2 }, maxRequestConcurrency: { enabled: true, value: 50 }, minRequestInterval: { enabled: false, value: 0 }, moduleAutoUpdate: true, - moduleSources: ['https://sync.consensia.cc/modules.json'], + moduleSources: [OFFICIAL_SOURCE], modules: {}, noticeStatusOnMobile: true, realtimeSync: { enabled: false, value: 5000 }, diff --git a/packages/plugin/src/modules/Extensibility.ts b/packages/plugin/src/modules/Extensibility.ts index 31c0632..df3a8ff 100644 --- a/packages/plugin/src/modules/Extensibility.ts +++ b/packages/plugin/src/modules/Extensibility.ts @@ -44,6 +44,7 @@ export type AugmentedModuleMeta = ModuleMeta & { const MODULE_EXTENSION = '.js'; const AUTO_UPDATE_DELAY = 200; +export const OFFICIAL_SOURCE = 'https://sync.consensia.cc/modules.json'; export default class Extensibility { private readonly moduleDir: string; @@ -397,7 +398,7 @@ function isValidMeta(meta: unknown): meta is ModuleMeta { ); } -// TODO: remove after August 12 +// TODO: remove after August 16 async function migrateModules({ moduleStore, fileList, diff --git a/packages/plugin/src/modules/ModulesModal.ts b/packages/plugin/src/modules/ModulesModal.ts index b2d44a2..dfb62da 100644 --- a/packages/plugin/src/modules/ModulesModal.ts +++ b/packages/plugin/src/modules/ModulesModal.ts @@ -42,6 +42,8 @@ export default class ModulesModal extends Modal { ) { super(ctx.app); this.t = ctx.translate; + this.containerEl.getElementsByClassName('modal-bg')[0].addClass('opacity-0!'); + this.modalEl.addClasses(['sync-engine-large-modal', 'shadow-none!']); } declare readonly i18n: ModulesModalTranslations; @@ -53,15 +55,7 @@ export default class ModulesModal extends Modal { }; onOpen() { - this.containerEl.getElementsByClassName('modal-bg')[0].addClass('opacity-0!'); this.setTitle(this.t('moduleManagement')); - this.modalEl.addClasses([ - 'w-[--modal-width]', - 'h-[--modal-height]', - 'max-w-[--modal-max-width]', - 'max-h-[--modal-max-height]', - 'shadow-none!', - ]); const controlsEl = this.contentEl.createDiv('flex items-center gap-2 pb-4'); const searchEl = controlsEl.createDiv('min-w-0 flex-1'); const listEl = this.contentEl.createDiv('min-h-0 overflow-y-auto'); diff --git a/packages/plugin/src/modules/Observability.ts b/packages/plugin/src/modules/Observability.ts index 7c1aff3..cbf957b 100644 --- a/packages/plugin/src/modules/Observability.ts +++ b/packages/plugin/src/modules/Observability.ts @@ -1,5 +1,5 @@ import type { Events, Translations } from '@'; -import type { App, Command, IconName } from 'obsidian'; +import type { App, Command, DataAdapter, IconName } from 'obsidian'; import type { Ref } from 'synthkernel'; import { Notice, Platform, setIcon } from 'obsidian'; import { computed, ref } from 'synthkernel'; @@ -65,7 +65,7 @@ export default class Observability { }, ); - declare readonly settings: { noticeStatusOnMobile: boolean }; + declare readonly settings: { noticeStatusOnMobile: boolean; exportLogsDirectory: string }; declare readonly i18n: { startSync: string; startNonInteractiveSync: string; @@ -290,11 +290,10 @@ export default class Observability { const log = getLogs(); const timestamp = new Date().toISOString().replaceAll(/[:.]/gv, '-'); const fileName = `${timestamp}.md`; - const dirPath = 'Sync Engine Logs'; - const filePath = `${dirPath}/${fileName}`; + const { exportLogsDirectory } = this.settings; + const filePath = `${exportLogsDirectory === '/' ? '' : exportLogsDirectory}${fileName}`; try { - const folderExists = app.vault.getFolderByPath(dirPath); - if (!folderExists) await app.vault.createFolder(dirPath); + await mkdirRecursive(app.vault.adapter, exportLogsDirectory); const file = await app.vault.create(filePath, log); await app.workspace.getLeaf().openFile(file); } catch (error) { @@ -316,3 +315,13 @@ export default class Observability { walkProgress: this.walkProgress, }; } + +async function mkdirRecursive(adapter: DataAdapter, path: string): Promise { + const parts = path.split('/'); + let currentPath = ''; + for (const part of parts) { + if (!part) return; + currentPath = currentPath ? `${currentPath}/${part}` : part; + if (!(await adapter.exists(currentPath))) await adapter.mkdir(currentPath); + } +} diff --git a/packages/plugin/src/settings/development.ts b/packages/plugin/src/settings/development.ts index b66f362..f201bc6 100644 --- a/packages/plugin/src/settings/development.ts +++ b/packages/plugin/src/settings/development.ts @@ -1,3 +1,5 @@ +import type { Settings } from '@'; +import { normalizeBaseDir } from '@repo/shared/path'; import { Notice, Setting } from 'obsidian'; import type { Translate } from '@/modules/I18n'; import type { MaybePromise } from '@/sdk'; @@ -10,6 +12,7 @@ export type DevelopmentSettingTranslations = { clearRecordsDescription: string; export: string; exportLogsDescription: string; + exportLogsDirectoryPlaceholder: string; exportLogsToFile: string; }; @@ -19,9 +22,11 @@ export default function developmentSettings( translate: Translate; deleteRecordStore: (namespace?: string) => MaybePromise; exportLogs: () => Promise; + settings: Settings; + saveSettings: () => Promise; }, ) { - const { translate, exportLogs, deleteRecordStore } = ctx; + const { translate, exportLogs, deleteRecordStore, settings, saveSettings } = ctx; new Setting(el).setName(translate('development')).setHeading(); new Setting(el) @@ -40,6 +45,19 @@ export default function developmentSettings( new Setting(el) .setName(translate('exportLogsToFile')) .setDesc(translate('exportLogsDescription')) + .addText((text) => + text + .setValue(settings.exportLogsDirectory) + .setPlaceholder(translate('exportLogsDirectoryPlaceholder')) + .inputEl.addEventListener('blur', () => { + const normalized = normalizeBaseDir(text.getValue().trim()); + if (settings.exportLogsDirectory !== normalized) { + settings.exportLogsDirectory = normalized; + void saveSettings(); + } + text.setValue(normalized); + }), + ) .addButton((button) => { button.setButtonText(translate('export')).onClick(exportLogs); }); diff --git a/versions.json b/versions.json index 3ea89c3..43b16b3 100644 --- a/versions.json +++ b/versions.json @@ -3,5 +3,6 @@ "3.0.1": "1.12.3", "3.0.2": "1.12.3", "3.0.3": "1.12.3", - "3.0.4": "1.12.3" + "3.0.4": "1.12.3", + "3.0.5": "1.12.3" }