]*>(.*?)<\/em>/gi, "*$1*").replace(/]*>(.*?)<\/i>/gi, "*$1*").replace(/]*>(.*?)<\/u>/gi, "$1").replace(/]*>(.*?)<\/p>/gi, "$1\n\n").replace(/
]*>/gi, "\n").replace(/
]*>(.*?)<\/div>/gi, "$1\n").replace(/<[^>]*>/g, "").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n\s*\n\s*\n/g, "\n\n").trim();
+ return markdown || "Empty document";
+ }
+ // Add a manual save method as fallback
+ async manualSaveFromEditor() {
+ if (!this.file) {
+ console.log("Cannot save: No file is open.");
+ return;
+ }
+ try {
+ const arrayBuffer = await this.getEditorContent();
+ if (arrayBuffer) {
+ await this.app.vault.modifyBinary(this.file, arrayBuffer);
+ new import_obsidian.Notice("Document saved successfully");
+ } else {
+ if (this.leaf.view) {
+ new import_obsidian.Notice("Could not get document from editor. Save failed.");
+ }
+ }
+ } catch (error) {
+ console.error("Error in manual save:", error);
+ if (this.leaf.view) {
+ new import_obsidian.Notice("Save failed. " + (error instanceof Error ? error.message : "Please check the console for details."));
+ }
+ }
+ }
+ // Add the missing saveAsDocument method with improved functionality
+ async saveAsDocument(forceSaveAs = false, saveAsData) {
+ var _a, _b, _c;
+ if (this.fallbackEditor) {
+ await this.saveFallbackToObsidian();
+ return;
+ }
+ try {
+ const activeExt = this.currentExt || ((_b = (_a = this.file) == null ? void 0 : _a.extension) == null ? void 0 : _b.toLowerCase()) || "docx";
+ if (saveAsData && saveAsData.url) {
+ if (this.plugin.settings.useSystemSaveAs) {
+ let fileName2 = this.pendingSaveAsName || saveAsData.title || `Document.${activeExt}`;
+ this.pendingSaveAsName = null;
+ if (!fileName2.toLowerCase().endsWith("." + activeExt))
+ fileName2 += "." + activeExt;
+ const js = `(() => { try {
+ const url = ${JSON.stringify(saveAsData.url)};
+ const name = ${JSON.stringify(fileName2)};
+ // Create hidden anchor to force download
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = name;
+ a.style.display='none';
+ document.body.appendChild(a);
+ a.click();
+ setTimeout(()=>{ try { a.remove(); } catch(_){} }, 4000);
+} catch(e){ console.error('System Save As injection failed', e); } })();`;
+ (_c = this.webview) == null ? void 0 : _c.executeJavaScript(js).catch((e) => console.error("executeJavaScript download inject failed", e));
+ new import_obsidian.Notice("Opening system Save dialog...");
+ return;
+ }
+ new import_obsidian.Notice("Downloading document...");
+ const response = await (0, import_obsidian.requestUrl)({ url: saveAsData.url, method: "GET" });
+ if (response.status !== 200 || !response.arrayBuffer)
+ throw new Error(`Failed to download file: ${response.status}`);
+ let fileName = this.pendingSaveAsName || saveAsData.title || "";
+ this.pendingSaveAsName = null;
+ if (!fileName)
+ fileName = await this.promptForFileName() || "";
+ if (!fileName) {
+ new import_obsidian.Notice("Save As cancelled");
+ return;
+ }
+ if (!fileName.toLowerCase().endsWith("." + activeExt))
+ fileName += "." + activeExt;
+ let attempt = 0;
+ const originalBase = fileName.replace(new RegExp(`.${activeExt}$`, "i"), "");
+ while (this.app.vault.getAbstractFileByPath(fileName)) {
+ attempt++;
+ const alt = `${originalBase} (${attempt}).${activeExt}`;
+ if (attempt === 1) {
+ const retry = await this.promptForFileName();
+ if (retry) {
+ fileName = retry.toLowerCase().endsWith("." + activeExt) ? retry : retry + "." + activeExt;
+ continue;
+ }
+ }
+ fileName = alt;
+ }
+ const newFile = await this.app.vault.createBinary(fileName, response.arrayBuffer);
+ new import_obsidian.Notice(`Document saved as: ${fileName}`);
+ await this.plugin.openOnlyOfficeFile(newFile);
+ return;
+ }
+ if (this.webview) {
+ if (!this.plugin.settings.useSystemSaveAs) {
+ if (forceSaveAs) {
+ const chosen = await this.promptForFileName();
+ if (!chosen) {
+ new import_obsidian.Notice("Save As cancelled");
+ return;
+ }
+ this.pendingSaveAsName = chosen;
+ }
+ new import_obsidian.Notice("Requesting document data...");
+ } else {
+ new import_obsidian.Notice("Preparing system download...");
+ }
+ const format = activeExt === "pdf" ? "docx" : activeExt;
+ this.webview.executeJavaScript(`try { window.docEditor && window.docEditor.downloadAs && window.docEditor.downloadAs('${format}'); } catch(e){ console.error('downloadAs failed', e); }`);
+ } else {
+ new import_obsidian.Notice("Save As not available (editor not ready)");
+ }
+ } catch (error) {
+ console.error("Error during 'Save As':", error);
+ new import_obsidian.Notice(`Save As failed: ${error.message}`);
+ }
+ }
+ // Fallback path for system Save As when OnlyOffice doesn't return a URL (new unsaved doc)
+ async fallbackSystemSaveAsFromVault() {
+ var _a;
+ try {
+ if (!this.file) {
+ new import_obsidian.Notice("No file to save");
+ this.performingSystemSaveAsFallback = false;
+ return;
+ }
+ const bin = await this.app.vault.readBinary(this.file);
+ const base = this.file.basename || "Document";
+ const ext = this.currentExt || this.file.extension || "docx";
+ const name = base.toLowerCase().endsWith("." + ext) ? base : base + "." + ext;
+ const outName = name.endsWith("." + ext) ? name : name + "." + ext;
+ const b64 = Buffer.from(bin).toString("base64");
+ const js = `(() => { try {
+ const b64='${b64}';
+ const byteChars = atob(b64);
+ const len = byteChars.length;
+ const bytes = new Uint8Array(len);
+ for (let i=0;i{ try { URL.revokeObjectURL(a.href); a.remove(); } catch(_){} },4000);
+ } catch(e){ console.error('fallbackSystemSaveAsFromVault failed', e); } })();`;
+ (_a = this.webview) == null ? void 0 : _a.executeJavaScript(js).catch((e) => console.error("fallback executeJavaScript failed", e));
+ new import_obsidian.Notice("Opening system Save dialog (fallback)");
+ } catch (e) {
+ console.error("OnlyOffice: fallbackSystemSaveAsFromVault error", e);
+ new import_obsidian.Notice("System Save As fallback failed");
+ } finally {
+ this.performingSystemSaveAsFallback = false;
+ }
+ }
+ // New helper method to get content from the editor
+ getEditorContent(forceContentRetrieval = false) {
+ return Promise.resolve(null);
+ }
+ // Helper to prompt for a new file name
+ async promptForFileName() {
+ return new Promise((resolve2) => {
+ new SaveAsModal(this.app, (result) => {
+ resolve2(result);
+ }).open();
+ });
+ }
+ // Clean up when view is closed
+ async onClose() {
+ if (this.boundMessageHandler) {
+ window.removeEventListener("message", this.boundMessageHandler);
+ this.boundMessageHandler = null;
+ }
+ if (this.dirtyCheckInterval) {
+ clearInterval(this.dirtyCheckInterval);
+ this.dirtyCheckInterval = null;
+ }
+ }
+};
+var SaveAsModal = class extends import_obsidian.Modal {
+ constructor(app, onSubmit) {
+ super(app);
+ this.submitted = false;
+ this.onSubmit = onSubmit;
+ }
+ onOpen() {
+ const { contentEl } = this;
+ contentEl.createEl("h2", { text: "Save As" });
+ contentEl.createEl("p", { text: "Enter the new file name:" });
+ this.input = contentEl.createEl("input", { type: "text", placeholder: "NewDocument.docx" });
+ const saveButton = contentEl.createEl("button", { text: "Save" });
+ saveButton.addEventListener("click", () => {
+ const value = this.input.value.trim();
+ if (value) {
+ this.submitted = true;
+ this.onSubmit(value);
+ this.close();
+ }
+ });
+ const cancelButton = contentEl.createEl("button", { text: "Cancel" });
+ cancelButton.style.marginLeft = "10px";
+ cancelButton.addEventListener("click", () => {
+ this.close();
+ });
+ }
+ onClose() {
+ if (!this.submitted) {
+ this.onSubmit(null);
+ }
+ this.contentEl.empty();
+ }
+};
+var OnlyOfficePlugin = class extends import_obsidian.Plugin {
+ constructor() {
+ super(...arguments);
+ this.httpServer = null;
+ this.callbackServer = null;
+ this.localServerPort = 0;
+ this.callbackServerPort = 0;
+ this.internalServerReady = null;
+ this._resolveInternalServerReady = null;
+ this._rejectInternalServerReady = null;
+ this.keyFileMap = {};
+ this.saveAsPromise = null;
+ }
+ async onload() {
+ await this.loadSettings();
+ await this.startInternalServer();
+ await this.startCallbackServer();
+ this.addRibbonIcon("file-plus", "New OnlyOffice Document", () => {
+ this.openNewDocument();
+ });
+ this.registerView(
+ VIEW_TYPE_ONLYOFFICE,
+ (leaf) => new OnlyOfficeDocumentView(leaf, this)
+ );
+ const officeExts = ["docx", "xlsx", "pptx", "pdf"];
+ const failed = [];
+ for (const ext of officeExts) {
+ try {
+ this.registerExtensions([ext], VIEW_TYPE_ONLYOFFICE);
+ console.log(`OnlyOffice: registered extension .${ext}`);
+ } catch (e) {
+ failed.push(ext);
+ console.warn(`OnlyOffice: failed to register .${ext}:`, (e == null ? void 0 : e.message) || e);
+ }
+ }
+ if (failed.length) {
+ console.log("OnlyOffice: installing context menu fallback for", failed.join(","));
+ this.registerEvent(
+ this.app.workspace.on("file-menu", (menu, file) => {
+ if (file instanceof import_obsidian.TFile && officeExts.includes(file.extension)) {
+ menu.addItem((item) => {
+ item.setTitle("Open with OnlyOffice").setIcon("file-text").onClick(() => {
+ this.openOnlyOfficeFile(file);
+ });
+ });
+ }
+ })
+ );
+ }
+ this.addCommands();
+ this.addSettingTab(new OnlyOfficeSettingTab(this.app, this));
+ }
+ // Add commands to the command palette
+ addCommands() {
+ this.addCommand({
+ id: "open-docx-in-onlyoffice",
+ name: "Open DOCX in OnlyOffice",
+ checkCallback: (checking) => {
+ const file = this.app.workspace.getActiveFile();
+ if (file && file.extension === "docx") {
+ if (!checking) {
+ this.openDocxFile(file);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ this.addCommand({
+ id: "open-xlsx-in-onlyoffice",
+ name: "Open XLSX in OnlyOffice",
+ checkCallback: (checking) => {
+ const file = this.app.workspace.getActiveFile();
+ if (file && file.extension === "xlsx") {
+ if (!checking) {
+ this.openOnlyOfficeFile(file);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ this.addCommand({
+ id: "open-pptx-in-onlyoffice",
+ name: "Open PPTX in OnlyOffice",
+ checkCallback: (checking) => {
+ const file = this.app.workspace.getActiveFile();
+ if (file && file.extension === "pptx") {
+ if (!checking) {
+ this.openOnlyOfficeFile(file);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ this.addCommand({
+ id: "open-pdf-in-onlyoffice",
+ name: "Open PDF in OnlyOffice (viewer)",
+ checkCallback: (checking) => {
+ const file = this.app.workspace.getActiveFile();
+ if (file && file.extension === "pdf") {
+ if (!checking) {
+ this.openOnlyOfficeFile(file);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ this.addCommand({
+ id: "onlyoffice-list-office-files",
+ name: "OnlyOffice: List Office Files (debug)",
+ callback: async () => {
+ try {
+ const vaultFiles = this.app.vault.getFiles().filter((f) => ["docx", "xlsx", "pptx", "pdf"].includes(f.extension));
+ console.log("OnlyOffice DEBUG: vault reports", vaultFiles.length, "office files");
+ vaultFiles.forEach((f) => console.log(" VAULT:", f.path));
+ if (!(this.app.vault.adapter instanceof import_obsidian.FileSystemAdapter)) {
+ console.log("OnlyOffice DEBUG: not FS adapter, cannot disk-scan");
+ return;
+ }
+ const root = this.app.vault.adapter.getBasePath();
+ const found = [];
+ const exts = /* @__PURE__ */ new Set([".docx", ".xlsx", ".pptx", ".pdf"]);
+ const walk = (dir) => {
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const e of entries) {
+ if (e.name.startsWith("."))
+ continue;
+ const full = path.join(dir, e.name);
+ if (e.isDirectory()) {
+ walk(full);
+ } else {
+ const lower = e.name.toLowerCase();
+ for (const x of exts) {
+ if (lower.endsWith(x)) {
+ found.push(full);
+ break;
+ }
+ }
+ }
+ }
+ };
+ walk(root);
+ console.log("OnlyOffice DEBUG: disk scan found", found.length, "office files");
+ const rel = (p) => p.replace(/\\/g, "/").substring(root.replace(/\\/g, "/").length + 1);
+ const vaultSet = new Set(vaultFiles.map((f) => f.path));
+ const missingInVault = found.map(rel).filter((p) => !vaultSet.has(p));
+ if (missingInVault.length === 0)
+ console.log("OnlyOffice DEBUG: no discrepancies");
+ else {
+ console.warn("OnlyOffice DEBUG: files on disk NOT in vault index:", missingInVault);
+ }
+ new import_obsidian.Notice("OnlyOffice: debug listing complete (see console)");
+ } catch (e) {
+ console.error("OnlyOffice DEBUG: listing failed", e);
+ new import_obsidian.Notice("OnlyOffice: listing failed (see console)");
+ }
+ }
+ });
+ this.addCommand({
+ id: "open-xlsx-in-onlyoffice",
+ name: "Open XLSX in OnlyOffice",
+ checkCallback: (checking) => {
+ const file = this.app.workspace.getActiveFile();
+ if (file && file.extension === "xlsx") {
+ if (!checking) {
+ this.openOnlyOfficeFile(file);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ this.addCommand({
+ id: "create-new-onlyoffice-document",
+ name: "Create New OnlyOffice Document",
+ callback: () => {
+ this.openNewDocument();
+ }
+ });
+ }
+ // Open a new blank document in the OnlyOffice view
+ async openNewDocument() {
+ var _a;
+ try {
+ let requestedName = null;
+ let chosenType = "docx";
+ if (((_a = this.settings) == null ? void 0 : _a.promptNameOnCreate) !== false) {
+ requestedName = await new Promise((resolve2) => {
+ const modal = new class extends import_obsidian.Modal {
+ constructor() {
+ super(...arguments);
+ this.submitted = false;
+ }
+ onOpen() {
+ const radius = "10px";
+ const fieldHeight = "40px";
+ const { contentEl } = this;
+ contentEl.empty();
+ contentEl.style.padding = "26px 32px 22px";
+ contentEl.style.minWidth = "600px";
+ contentEl.style.maxWidth = "600px";
+ contentEl.style.boxSizing = "border-box";
+ contentEl.style.overflow = "hidden";
+ contentEl.style.overflowX = "hidden";
+ if (contentEl.parentElement) {
+ contentEl.parentElement.style.overflowX = "hidden";
+ }
+ contentEl.createEl("h2", { text: "New OnlyOffice Document", attr: { style: "margin:0 0 16px 0; font-weight:600; font-size:22px; letter-spacing:.4px;" } });
+ this.wrapper = contentEl.createEl("div", { attr: { style: "display:flex; flex-direction:column; gap:16px;" } });
+ const formGap = 18;
+ const col1 = 320;
+ const col2 = 118;
+ const formWidth = col1 + col2 + formGap;
+ const row = this.wrapper.createEl("div", { attr: { style: `display:grid; grid-template-columns: ${col1}px ${col2}px; gap:${formGap}px; align-items:end; width:${formWidth}px;` } });
+ const nameCol = row.createEl("div", { attr: { style: "display:flex; flex-direction:column; gap:6px; min-width:0;" } });
+ nameCol.createEl("label", { text: "Filename", attr: { style: "font-size:11px; font-weight:600; letter-spacing:.5px; text-transform:uppercase; color:var(--text-muted);" } });
+ this.input = nameCol.createEl("input", { type: "text", placeholder: "MyDocument", attr: { style: `padding:8px 12px; font-size:14px; line-height:20px; height:${fieldHeight}; border:1px solid var(--background-modifier-border); border-radius:${radius}; width:100%; max-width:${col1}px; box-sizing:border-box;` } });
+ const typeCol = row.createEl("div", { attr: { style: "display:flex; flex-direction:column; gap:6px;" } });
+ typeCol.createEl("label", { text: "Type", attr: { style: "font-size:11px; font-weight:600; letter-spacing:.5px; text-transform:uppercase; color:var(--text-muted);" } });
+ this.select = typeCol.createEl("select", { attr: { style: `padding:8px 12px; font-size:14px; line-height:20px; height:${fieldHeight}; border:1px solid var(--background-modifier-border); border-radius:${radius}; background:var(--background-primary); box-sizing:border-box; width:${col2}px;` } });
+ ["docx", "xlsx", "pptx"].forEach((t) => {
+ const o = document.createElement("option");
+ o.value = t;
+ o.textContent = t.toUpperCase();
+ this.select.appendChild(o);
+ });
+ const btnRow = this.wrapper.createEl("div", { attr: { style: `display:flex; justify-content:flex-end; gap:10px; margin-top:6px; width:${formWidth}px;` } });
+ const baseBtn = `padding:8px 20px; font-size:14px; font-weight:500; border:none; border-radius:${radius}; cursor:pointer; line-height:20px;`;
+ const cancelBtn = btnRow.createEl("button", { text: "Cancel", attr: { style: baseBtn + "background:var(--background-modifier-border); color:var(--text-normal);" } });
+ this.okBtn = btnRow.createEl("button", { text: "Create", attr: { style: baseBtn + "background:var(--interactive-accent); color:var(--text-on-accent,#fff);" } });
+ this.okBtn.addEventListener("click", () => {
+ const v = this.input.value.trim();
+ if (v) {
+ chosenType = this.select.value;
+ this.submitted = true;
+ this.close();
+ resolve2(v);
+ }
+ });
+ cancelBtn.addEventListener("click", () => {
+ this.close();
+ resolve2(null);
+ });
+ this.input.addEventListener("keydown", (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ this.okBtn.click();
+ }
+ });
+ this.input.focus();
+ }
+ onClose() {
+ if (!this.submitted)
+ resolve2(null);
+ this.contentEl.empty();
+ }
+ }(this.app);
+ modal.open();
+ });
+ if (requestedName === null) {
+ new import_obsidian.Notice("Creation cancelled");
+ return;
+ }
+ requestedName = requestedName.replace(/[\\/:*?"<>|]/g, "").trim();
+ if (!requestedName) {
+ new import_obsidian.Notice("Invalid name");
+ return;
+ }
+ const extRegex = new RegExp(`.${chosenType}$`, "i");
+ if (!extRegex.test(requestedName))
+ requestedName += `.${chosenType}`;
+ if (this.app.vault.getAbstractFileByPath(requestedName)) {
+ let base2 = requestedName.replace(new RegExp(`.${chosenType}$`, "i"), "");
+ let i = 1;
+ let candidate = `${base2} (${i}).${chosenType}`;
+ while (this.app.vault.getAbstractFileByPath(candidate)) {
+ i++;
+ candidate = `${base2} (${i}).${chosenType}`;
+ }
+ requestedName = candidate;
+ }
+ }
+ let templateData;
+ const adapter = this.app.vault.adapter;
+ const base = adapter.getBasePath();
+ const pluginDir = this.manifest.dir && path.isAbsolute(this.manifest.dir) ? this.manifest.dir : path.join(base, this.manifest.dir || "");
+ const wantedExt = chosenType;
+ const candidateNames = wantedExt === "docx" ? ["Start.docx", "Start.dotx"] : wantedExt === "xlsx" ? ["Start.xlsx"] : ["Start.pptx"];
+ const candidatePaths = [];
+ for (const nm of candidateNames) {
+ candidatePaths.push(path.join(pluginDir, "assets", nm));
+ candidatePaths.push(path.join(base, nm));
+ }
+ for (const p of candidatePaths) {
+ if (fs.existsSync(p)) {
+ const buf = await fs.promises.readFile(p);
+ const slice = buf.subarray(0);
+ const copy = new Uint8Array(slice.length);
+ copy.set(slice);
+ templateData = copy.buffer;
+ console.log("OnlyOffice: using template", p, "for new", wantedExt);
+ break;
+ }
+ }
+ if (!templateData && wantedExt === "docx") {
+ try {
+ const minimalB64 = "UEsDBBQABgAIAAAAIQAAAAAAAAAAAAAAAAAJAAAAd29yZC9VVAkAA0oJZ2ZKCWdzdXgLAAEE9QEAAAQUAAAAAABQSwMEFAAIAAgAAAAhAAAAAAAAAAAAAAAAABwAAAB3b3JkL19yZWxzL2RvY3VtZW50LnhtbC5yZWxzVVQJAAOSAmdmkgJndXgLAAEE9QEAAAQUAAAAAABQSwMEFAAIAAgAAAAhAAAAAAAAAAAAAAAAADwAAAB3b3JkL2RvY3VtZW50LnhtbFVUCQADjQJnZ40CZ3V4CwABBPUBAAAQFAAAAAA8eG1sIHZlcnNpb249IjEuMCI+PHc6d29yZGRvYyB4bWxucz13PSJodHRwOi8vc2NoZW1hcy5vcGVub3hwb3J5Lm9yZy93b3JkcHJvYy8yMDA2L21haW4iPjx3OnNlY3Rpb24geG1sbnM6dyA9ICJodHRwOi8vc2NoZW1hcy5vcGVub3hwb3J5Lm9yZy93b3JkcHJvYy8yMDA2L21haW4iPjx3OnA+PC93OnA+PC93OnNlY3Rpb24+PC93OndvcmRkb2M+";
+ const buf = Buffer.from(minimalB64, "base64");
+ const copy = new Uint8Array(buf.length);
+ copy.set(buf);
+ templateData = copy.buffer;
+ console.log("OnlyOffice: using embedded minimal DOCX fallback (no template found)");
+ } catch (e) {
+ console.error("OnlyOffice: failed to build minimal DOCX fallback, creating empty file", e);
+ templateData = new ArrayBuffer(0);
+ }
+ } else if (!templateData) {
+ new import_obsidian.Notice(`No template found for ${wantedExt.toUpperCase()} (place Start.${wantedExt} in plugin assets). Creation cancelled.`);
+ return;
+ }
+ let finalName;
+ if (requestedName) {
+ finalName = requestedName;
+ } else {
+ let baseName = "Untitled Document";
+ let idx = 1;
+ finalName = `${baseName}.${wantedExt}`;
+ while (this.app.vault.getAbstractFileByPath(finalName)) {
+ idx++;
+ finalName = `${baseName} ${idx}.${wantedExt}`;
+ }
+ }
+ const file = await this.app.vault.createBinary(finalName, templateData);
+ console.log("OnlyOffice: created new document", finalName, "size", templateData.byteLength, "prompted?", !!requestedName);
+ await this.openOnlyOfficeFile(file);
+ } catch (e) {
+ console.error("OnlyOffice: failed to create new document", e);
+ new import_obsidian.Notice("OnlyOffice: Failed to create new document");
+ }
+ }
+ // Start an internal HTTP server to serve vault files and editor.html
+ async startInternalServer() {
+ var _a, _b, _c;
+ if (!(this.app.vault.adapter instanceof import_obsidian.FileSystemAdapter)) {
+ new import_obsidian.Notice("OnlyOffice plugin requires a file system adapter to run the local server.");
+ return;
+ }
+ this.internalServerReady = new Promise((resolve2, reject) => {
+ this._resolveInternalServerReady = resolve2;
+ this._rejectInternalServerReady = reject;
+ });
+ const vaultPath = this.app.vault.adapter.getBasePath();
+ let pluginPath = this.manifest.dir;
+ if (pluginPath && !path.isAbsolute(pluginPath)) {
+ pluginPath = path.resolve(vaultPath, pluginPath);
+ }
+ const resolvedPluginPath = pluginPath && path.isAbsolute(pluginPath) ? pluginPath : __dirname;
+ const editorHtmlPath = path.join(resolvedPluginPath, "editor.html");
+ console.log("Vault path:", vaultPath);
+ console.log("Plugin path:", pluginPath);
+ console.log("Resolved plugin path:", resolvedPluginPath);
+ console.log("Editor HTML path:", editorHtmlPath);
+ console.log("Editor HTML exists:", fs.existsSync(editorHtmlPath));
+ const explicitPort = this.settings.htmlServerPort && this.settings.htmlServerPort > 0 ? this.settings.htmlServerPort : 0;
+ const hashVault = (s) => {
+ let h = 0;
+ for (let i = 0; i < s.length; i++)
+ h = Math.imul(31, h) + s.charCodeAt(i) >>> 0;
+ return h;
+ };
+ let candidatePorts = [];
+ if (explicitPort) {
+ candidatePorts.push(explicitPort);
+ } else {
+ const base = 28100 + hashVault(vaultPath) % 700;
+ for (let i = 0; i < 12; i++)
+ candidatePorts.push(base + i);
+ candidatePorts.push(0);
+ }
+ let serverStarted = false;
+ let port = 0;
+ for (const tryPort of candidatePorts) {
+ if (serverStarted)
+ break;
+ port = tryPort;
+ try {
+ this.httpServer = http.createServer((req, res) => {
+ var _a2;
+ console.log("[HTTP] Request URL:", req.url);
+ res.setHeader("Access-Control-Allow-Origin", "*");
+ res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, OPTIONS");
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
+ if (req.method === "OPTIONS") {
+ res.writeHead(200);
+ res.end();
+ return;
+ }
+ try {
+ let reqPath = decodeURIComponent(((_a2 = req.url) == null ? void 0 : _a2.split("?")[0]) || "/");
+ if (reqPath === "/api/createNew") {
+ console.log("[HTTP] Create New document request");
+ res.writeHead(200, { "Content-Type": "text/html" });
+ res.end(`Creating new document...`);
+ return;
+ }
+ if (reqPath === "/api/saveAs") {
+ console.log("[HTTP] Save As request");
+ const urlObj = new URL(req.url || "", `http://${req.headers.host || "localhost"}`);
+ const url = urlObj.searchParams.get("url") || "";
+ const title = urlObj.searchParams.get("title") || "";
+ res.writeHead(200, { "Content-Type": "text/html" });
+ res.end(`Saving document as...`);
+ return;
+ }
+ if (reqPath === "/" || reqPath === "/embedded-editor.html") {
+ const embeddedPath = path.join(resolvedPluginPath, "embedded-editor.html");
+ if (!fs.existsSync(embeddedPath)) {
+ res.writeHead(404, { "Content-Type": "text/plain" });
+ res.end("embedded-editor.html not found");
+ return;
+ }
+ fs.readFile(embeddedPath, (err, data) => {
+ if (err) {
+ res.writeHead(500, { "Content-Type": "text/plain" });
+ res.end("Error reading embedded-editor.html");
+ return;
+ }
+ res.writeHead(200, { "Content-Type": "text/html; charset=UTF-8", "Cache-Control": "no-store" });
+ res.end(data);
+ });
+ } else {
+ const filePath = path.join(vaultPath, reqPath.replace(/^\//, ""));
+ console.log("Attempting to serve file:", filePath);
+ console.log("File exists:", fs.existsSync(filePath));
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
+ const mimeType = mime.lookup(filePath) || "application/octet-stream";
+ res.writeHead(200, {
+ "Content-Type": String(mimeType),
+ "Access-Control-Allow-Origin": "*",
+ "Cache-Control": "no-cache"
+ });
+ fs.createReadStream(filePath).pipe(res);
+ } else {
+ const pluginFilePath = path.join(resolvedPluginPath, reqPath.replace(/^\//, ""));
+ if (fs.existsSync(pluginFilePath) && fs.statSync(pluginFilePath).isFile()) {
+ const mimeType = mime.lookup(pluginFilePath) || "application/octet-stream";
+ res.writeHead(200, {
+ "Content-Type": String(mimeType),
+ "Access-Control-Allow-Origin": "*",
+ "Cache-Control": "no-cache"
+ });
+ fs.createReadStream(pluginFilePath).pipe(res);
+ } else {
+ res.writeHead(404, { "Content-Type": "text/plain" });
+ res.end(`File not found: ${filePath}`);
+ }
+ }
+ }
+ } catch (e) {
+ console.error("Server error:", e);
+ res.writeHead(500, { "Content-Type": "text/plain" });
+ res.end("Internal server error: " + e.message);
+ }
+ });
+ await new Promise((resolve2, reject) => {
+ this.httpServer.once("error", (err) => reject(err));
+ const bindHost = this.settings.restrictToLocalhost ? "127.0.0.1" : "0.0.0.0";
+ this.httpServer.listen(port, bindHost, () => resolve2());
+ });
+ if (port === 0) {
+ const addr = this.httpServer.address();
+ if (addr && typeof addr === "object")
+ port = addr.port;
+ }
+ this.localServerPort = port;
+ serverStarted = true;
+ const os = require("os");
+ const interfaces = os.networkInterfaces();
+ let lanIps = [];
+ for (const name of Object.keys(interfaces)) {
+ for (const iface of interfaces[name]) {
+ if (iface.family === "IPv4" && !iface.internal) {
+ lanIps.push(iface.address);
+ }
+ }
+ }
+ console.log(`OnlyOffice internal HTML server running at:`);
+ console.log(` Local: http://127.0.0.1:${port}/embedded-editor.html`);
+ if (!this.settings.restrictToLocalhost) {
+ lanIps.forEach((ip) => console.log(` LAN: http://${ip}:${port}/`));
+ } else {
+ console.log(" LAN: (disabled by restrictToLocalhost setting)");
+ }
+ try {
+ const testResponse = await (0, import_obsidian.requestUrl)({
+ url: `http://127.0.0.1:${port}/embedded-editor.html`,
+ method: "HEAD",
+ headers: { "Cache-Control": "no-cache" }
+ });
+ console.log("Server test result:", testResponse.status);
+ } catch (err) {
+ console.error("Server test failed:", err);
+ }
+ } catch (err) {
+ console.warn(`OnlyOffice internal server: port ${port} failed (${(err == null ? void 0 : err.message) || err}).`);
+ try {
+ (_a = this.httpServer) == null ? void 0 : _a.close();
+ } catch (e) {
+ }
+ }
+ }
+ if (!serverStarted) {
+ new import_obsidian.Notice("OnlyOffice: failed to start internal server (no free port)");
+ (_b = this._rejectInternalServerReady) == null ? void 0 : _b.call(this, new Error("failed to start"));
+ } else {
+ (_c = this._resolveInternalServerReady) == null ? void 0 : _c.call(this);
+ }
+ }
+ // Start an internal OnlyOffice callback server
+ async startCallbackServer() {
+ var _a, _b;
+ if (!(this.app.vault.adapter instanceof import_obsidian.FileSystemAdapter)) {
+ console.warn("OnlyOffice callback server requires a file system adapter.");
+ this.callbackServerPort = 0;
+ return;
+ }
+ const vaultPath = this.app.vault.adapter.getBasePath();
+ const explicitPort = this.settings.callbackServerPort && this.settings.callbackServerPort > 0 ? this.settings.callbackServerPort : 0;
+ const hashVault = (s) => {
+ let h = 0;
+ for (let i = 0; i < s.length; i++)
+ h = Math.imul(31, h) + s.charCodeAt(i) >>> 0;
+ return h;
+ };
+ let candidatePorts = [];
+ if (explicitPort) {
+ candidatePorts.push(explicitPort);
+ } else {
+ const base = 28800 + hashVault(vaultPath) % 700;
+ for (let i = 0; i < 12; i++)
+ candidatePorts.push(base + i);
+ candidatePorts.push(0);
+ }
+ let serverStarted = false;
+ let chosenPort = 0;
+ for (const tryPort of candidatePorts) {
+ if (serverStarted)
+ break;
+ try {
+ this.callbackServer = http.createServer(async (req, res) => {
+ res.setHeader("Access-Control-Allow-Origin", "*");
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
+ if (req.method === "OPTIONS") {
+ res.writeHead(200);
+ res.end();
+ return;
+ }
+ console.log(`[Callback] ${req.method} ${req.url}`);
+ const rawUrl = req.url || "/";
+ const pathOnly = rawUrl.split("?")[0];
+ if (req.method === "POST" && pathOnly === "/callback") {
+ let body = "";
+ req.on("data", (c) => {
+ body += c;
+ });
+ req.on("end", async () => {
+ var _a2;
+ try {
+ console.log("[Callback] Received data:", body);
+ let data;
+ try {
+ data = JSON.parse(body);
+ } catch (e) {
+ console.error("[Callback] JSON parse failed, attempting form:", e);
+ if (body.includes("=") && body.includes("&")) {
+ const params = new URLSearchParams(body);
+ data = Object.fromEntries(params);
+ } else {
+ res.writeHead(200, { "Content-Type": "application/json" });
+ res.end(JSON.stringify({ error: 0 }));
+ return;
+ }
+ }
+ const status = parseInt(data == null ? void 0 : data.status) || 0;
+ if (data && (status === 2 || status === 6 || status === 0) && data.url) {
+ console.log("[Callback] Processing save with URL:", data.url);
+ let key = data.key;
+ if (!key && data.url) {
+ try {
+ const u = new URL(data.url);
+ key = u.searchParams.get("key");
+ } catch (e) {
+ console.error("[Callback] URL parse error:", e);
+ }
+ }
+ if (key) {
+ let filePath = null;
+ if ((_a2 = this.keyFileMap) == null ? void 0 : _a2[key])
+ filePath = this.keyFileMap[key];
+ else {
+ const m = /^obsidian_(.+?)_\d+_[a-z0-9]+$/.exec(key);
+ if (m)
+ filePath = decodeURIComponent(m[1]);
+ }
+ if (filePath) {
+ const file = this.app.vault.getAbstractFileByPath(filePath);
+ if (file instanceof import_obsidian.TFile) {
+ try {
+ const response = await (0, import_obsidian.requestUrl)({ url: data.url, method: "GET", headers: { "Cache-Control": "no-cache" } });
+ if (response.status === 200 && response.arrayBuffer) {
+ await this.app.vault.modifyBinary(file, response.arrayBuffer);
+ new import_obsidian.Notice("OnlyOffice: Document saved (callback)");
+ console.log("[Callback] Saved successfully");
+ } else {
+ console.error("[Callback] Download failed status:", response.status);
+ }
+ } catch (err) {
+ console.error("[Callback] Download error:", err);
+ }
+ } else
+ console.error("[Callback] File not found in vault:", filePath);
+ } else
+ console.error("[Callback] Could not resolve file path for key:", key);
+ } else
+ console.error("[Callback] No key in callback payload");
+ } else {
+ console.log("[Callback] Ignored status / missing URL:", data);
+ }
+ res.writeHead(200, { "Content-Type": "application/json" });
+ res.end(JSON.stringify({ error: 0 }));
+ } catch (err) {
+ console.error("[Callback] Handler error:", err);
+ res.writeHead(200, { "Content-Type": "application/json" });
+ res.end(JSON.stringify({ error: 0 }));
+ }
+ });
+ } else {
+ res.writeHead(200, { "Content-Type": "application/json" });
+ res.end(JSON.stringify({ error: 0, info: "OnlyOffice callback server running" }));
+ }
+ });
+ await new Promise((resolve2, reject) => {
+ const timeoutId = setTimeout(() => reject(new Error("Callback server start timeout")), 3e3);
+ this.callbackServer.once("error", (err) => {
+ clearTimeout(timeoutId);
+ reject(err);
+ });
+ const bindHost = this.settings.restrictToLocalhost ? "127.0.0.1" : "0.0.0.0";
+ this.callbackServer.listen(tryPort, bindHost, () => {
+ clearTimeout(timeoutId);
+ resolve2();
+ });
+ });
+ const actual = ((_a = this.callbackServer.address()) == null ? void 0 : _a.port) || tryPort;
+ chosenPort = actual;
+ this.callbackServerPort = actual;
+ serverStarted = true;
+ console.log(`OnlyOffice callback server running at http://127.0.0.1:${actual}/ (candidate requested ${tryPort})`);
+ try {
+ const test = await (0, import_obsidian.requestUrl)({ url: `http://127.0.0.1:${actual}/`, method: "HEAD", headers: { "Cache-Control": "no-cache" } });
+ console.log("Callback server test status:", test.status);
+ } catch (e) {
+ console.warn("Callback server test failed:", e.message);
+ }
+ } catch (err) {
+ console.warn(`Callback server port ${tryPort} failed:`, err.message);
+ try {
+ (_b = this.callbackServer) == null ? void 0 : _b.close();
+ } catch (e) {
+ }
+ this.callbackServer = null;
+ }
+ }
+ if (!serverStarted) {
+ new import_obsidian.Notice("OnlyOffice: failed to start callback server (no free port)");
+ this.callbackServerPort = 0;
+ }
+ }
+ // Load settings from disk
+ async loadSettings() {
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
+ }
+ // Save settings to disk
+ async saveSettings() {
+ await this.saveData(this.settings);
+ }
+ // Clean up when the plugin is unloaded
+ async onunload() {
+ if (this.httpServer) {
+ this.httpServer.close();
+ this.httpServer = null;
+ }
+ if (this.callbackServer) {
+ this.callbackServer.close();
+ this.callbackServer = null;
+ }
+ }
+ // Open a DOCX file in the OnlyOffice view
+ async openDocxFile(file) {
+ const leaf = this.app.workspace.getLeaf(true);
+ await leaf.setViewState({
+ type: VIEW_TYPE_ONLYOFFICE,
+ state: { file: file.path }
+ });
+ this.app.workspace.setActiveLeaf(leaf, { focus: true });
+ }
+ // Generic opener for any supported OnlyOffice extension
+ async openOnlyOfficeFile(file) {
+ return this.openDocxFile(file);
+ }
+};
+var OnlyOfficeSettingTab = class extends import_obsidian.PluginSettingTab {
+ constructor(app, plugin) {
+ super(app, plugin);
+ this.plugin = plugin;
+ }
+ display() {
+ const { containerEl } = this;
+ containerEl.empty();
+ new import_obsidian.Setting(containerEl).setName("OnlyOffice Server Port").setDesc("The port where your OnlyOffice Document Server is running.").addText((text) => text.setPlaceholder("8080").setValue(this.plugin.settings.onlyOfficeServerPort.toString()).onChange(async (value) => {
+ this.plugin.settings.onlyOfficeServerPort = Number(value) || 8080;
+ await this.plugin.saveSettings();
+ }));
+ new import_obsidian.Setting(containerEl).setName("JWT Secret").setDesc("The secret key for JWT token generation (leave empty to disable).").addText((text) => text.setPlaceholder("your-secret-key").setValue(this.plugin.settings.jwtSecret || "").onChange(async (value) => {
+ this.plugin.settings.jwtSecret = value;
+ await this.plugin.saveSettings();
+ }));
+ new import_obsidian.Setting(containerEl).setName("Local Server Address for Docker").setDesc("The address of your machine that the OnlyOffice Docker container can reach (e.g., host.docker.internal, or your LAN IP).").addText((text) => text.setPlaceholder("host.docker.internal").setValue(this.plugin.settings.localServerAddress || "").onChange(async (value) => {
+ this.plugin.settings.localServerAddress = value;
+ await this.plugin.saveSettings();
+ }));
+ const advHeader = containerEl.createEl("h4", { text: "Advanced (normally leave defaults)" });
+ advHeader.style.marginTop = "1.5em";
+ new import_obsidian.Setting(containerEl).setName("Override HTML server port").setDesc("0 = automatic per-vault dynamic. Set only if you need a fixed port.").addText((text) => {
+ var _a;
+ return text.setPlaceholder("0 (auto)").setValue(String((_a = this.plugin.settings.htmlServerPort) != null ? _a : 0)).onChange(async (value) => {
+ const v = Number(value) || 0;
+ this.plugin.settings.htmlServerPort = v;
+ await this.plugin.saveSettings();
+ });
+ });
+ new import_obsidian.Setting(containerEl).setName("Override callback server port").setDesc("0 = automatic per-vault dynamic. Set only for special routing needs.").addText((text) => {
+ var _a;
+ return text.setPlaceholder("0 (auto)").setValue(String((_a = this.plugin.settings.callbackServerPort) != null ? _a : 0)).onChange(async (value) => {
+ const v = Number(value) || 0;
+ this.plugin.settings.callbackServerPort = v;
+ await this.plugin.saveSettings();
+ });
+ });
+ new import_obsidian.Setting(containerEl).setName("Restrict internal servers to localhost").setDesc("If ON, bind to 127.0.0.1 only (hide LAN URLs). Leave OFF if Docker/another host must reach them.").addToggle((toggle) => {
+ var _a;
+ return toggle.setValue((_a = this.plugin.settings.restrictToLocalhost) != null ? _a : false).onChange(async (value) => {
+ this.plugin.settings.restrictToLocalhost = value;
+ await this.plugin.saveSettings();
+ });
+ });
+ new import_obsidian.Setting(containerEl).setName("Append request token to URLs").setDesc("Toggle adding ?token=... to document and callback URLs (disable if your server does not use request verification).").addToggle((toggle) => {
+ var _a;
+ return toggle.setValue((_a = this.plugin.settings.useRequestToken) != null ? _a : true).onChange(async (value) => {
+ this.plugin.settings.useRequestToken = value;
+ await this.plugin.saveSettings();
+ });
+ });
+ new import_obsidian.Setting(containerEl).setName("Use system Save As UI").setDesc("Open the document copy in your default browser (download UI) instead of Obsidian modal when using Save As / Download Copy.").addToggle((toggle) => {
+ var _a;
+ return toggle.setValue((_a = this.plugin.settings.useSystemSaveAs) != null ? _a : false).onChange(async (value) => {
+ this.plugin.settings.useSystemSaveAs = value;
+ await this.plugin.saveSettings();
+ });
+ });
+ }
+};
+/*! Bundled license information:
+
+mime-db/index.js:
+ (*!
+ * mime-db
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2015-2022 Douglas Christopher Wilson
+ * MIT Licensed
+ *)
+
+mime-types/index.js:
+ (*!
+ * mime-types
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2015 Douglas Christopher Wilson
+ * MIT Licensed
+ *)
+*/
diff --git a/.obsidian/plugins/OnlyOffice/manifest.json b/.obsidian/plugins/OnlyOffice/manifest.json
new file mode 100644
index 00000000..2c808284
--- /dev/null
+++ b/.obsidian/plugins/OnlyOffice/manifest.json
@@ -0,0 +1,10 @@
+{
+ "id": "onlyoffice",
+ "name": "OnlyOffice",
+ "version": "1.0.0",
+ "minAppVersion": "0.15.0",
+ "description": "Edit DOCX files using OnlyOffice Document Server",
+ "author": "Jan Sansdtröm",
+ "authorUrl": "https://github.com/yourusername",
+ "isDesktopOnly": true
+}
\ No newline at end of file
diff --git a/.obsidian/plugins/cubox-sync/data.json b/.obsidian/plugins/cubox-sync/data.json
index 6098d9d6..3a7f50d1 100644
--- a/.obsidian/plugins/cubox-sync/data.json
+++ b/.obsidian/plugins/cubox-sync/data.json
@@ -34,7 +34,7 @@
"contentTemplate": "# {{{title}}}\n\n{{{description}}}\n\n{{{article_title}}} //文章内容标题\n{{{content}}} //文章正文\n\n\n[Read in Cubox]({{{cubox_url}}}) \n[Read Original]({{{url}}}) \n\n---\n\n{{#highlights.length}}\n## Annotations \n\n{{#highlights}}\n> {{{text}}} \n\n{{#note}}\n{{{note}}}\n{{/note}}\n[Link️]({{{cubox_url}}}) \n\n{{/highlights}}\n{{/highlights.length}}",
"highlightInContent": true,
"dateFormat": "yyyy-MM-dd",
- "lastSyncTime": 1756217339834,
+ "lastSyncTime": 1756219139631,
"lastSyncCardId": "7358083230366436785",
"lastCardUpdateTime": "2025-08-21T13:52:28.950+0800",
"syncing": false
diff --git a/.obsidian/plugins/recent-files-obsidian/data.json b/.obsidian/plugins/recent-files-obsidian/data.json
index 62e3a744..4bf4b02f 100644
--- a/.obsidian/plugins/recent-files-obsidian/data.json
+++ b/.obsidian/plugins/recent-files-obsidian/data.json
@@ -4,10 +4,18 @@
"basename": "2025-08-26",
"path": "01-工作日记/2025-08-26.md"
},
+ {
+ "basename": "Start",
+ "path": "08-Inbox/Office/Start.pptx"
+ },
{
"basename": "数据备份方案",
"path": "03-学习笔记/数据备份方案.md"
},
+ {
+ "basename": "Start",
+ "path": "08-Inbox/Office/Start.docx"
+ },
{
"basename": "2025-08-25",
"path": "01-工作日记/2025-08-25.md"
@@ -191,14 +199,6 @@
{
"basename": "软件下载安装",
"path": "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/软件下载安装.md"
- },
- {
- "basename": "每周定期汇总小结",
- "path": "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/每周定期汇总小结.md"
- },
- {
- "basename": "开箱写日志",
- "path": "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/开箱写日志.md"
}
],
"omittedPaths": [],
diff --git a/.obsidian/plugins/update-time-on-edit/data.json b/.obsidian/plugins/update-time-on-edit/data.json
index 05c0e789..0c7ebcb7 100644
--- a/.obsidian/plugins/update-time-on-edit/data.json
+++ b/.obsidian/plugins/update-time-on-edit/data.json
@@ -18,6 +18,6 @@
"01-工作日记/2025-08-24.md": "b82a4f49459f0b3dd8f8b4b2411fc2c682fea28f69b72d5c2cdf28ecac49cfd4",
"01-工作日记/2025-08-25.md": "2c6441d63702944db046c5637e0db95a969a26abc661ebd500662acfbf1e3a22",
"60-Imports/63-WuCai/2025/08/Plex重大变更:无论使用自己还是别人的媒体服务器远程播放都必须付费 – 蓝点网.md": "7a61c7330f970a8283cc70a0511e8b43bd6a160fc65e42fb2bee5f46668c752f",
- "01-工作日记/2025-08-26.md": "21a60c32fb116a5e9fa4ce0c36735214b885c22c661a0a1130f67692d847d272"
+ "01-工作日记/2025-08-26.md": "95ef1aaf127c499d538809bb85054c191268e360119c7c716505a73dc5a6c51b"
}
}
\ No newline at end of file
diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json
index 54a8174f..cf35f12d 100644
--- a/.obsidian/workspace.json
+++ b/.obsidian/workspace.json
@@ -4,45 +4,21 @@
"type": "split",
"children": [
{
- "id": "68ba61e199a3006a",
+ "id": "cf20749da23c30a8",
"type": "tabs",
"children": [
{
- "id": "210f6e255abf09ac",
+ "id": "88ed9de744392085",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "01-工作日记/2025-08-26.md",
"mode": "source",
- "source": false,
- "backlinks": true,
- "backlinkOpts": {
- "collapseAll": false,
- "extraContext": false,
- "sortOrder": "alphabetical",
- "showSearch": false,
- "searchQuery": "",
- "backlinkCollapsed": true,
- "unlinkedCollapsed": true
- }
- },
- "icon": "lucide-file",
- "title": "2025-08-26"
- }
- },
- {
- "id": "79999341f21ff9a9",
- "type": "leaf",
- "state": {
- "type": "markdown",
- "state": {
- "file": "03-学习笔记/数据备份方案.md",
- "mode": "source",
"source": false
},
"icon": "lucide-file",
- "title": "数据备份方案"
+ "title": "2025-08-26"
}
}
]
@@ -321,7 +297,6 @@
"obsidian-importer:Open Importer": false,
"copilot:Open Copilot Chat": false,
"DEVONlink-obsidian:DEVONlink": false,
- "periodic-notes:Open today": false,
"memos-ai-sync:Sync Memos": false,
"translate:Open translation view": false,
"obsidian-calibre-plugin:Calibre": false,
@@ -330,20 +305,26 @@
"diary-ics:日记日历订阅": false,
"cubox-sync:Cubox": false,
"file-tree-alternative:File Tree Alternative Plugin": true,
- "hidden-folder-obsidian:隐藏文件夹": false,
"password-protection:开启密码保护": false,
"soundscapes:Soundscapes: My Music": false,
"sheet-plus:创建 Excel 文件": false,
"wucai-highlights-official:Start WuCai syncing": false,
"note-to-mp:复制到公众号": false,
"timelines-revamped:Insert Timeline Event": false,
- "timelines-revamped:Insert Timeline Event (Frontmatter)": false
+ "timelines-revamped:Insert Timeline Event (Frontmatter)": false,
+ "periodic-notes:Open today": false,
+ "onlyoffice:New OnlyOffice Document": false
}
},
- "active": "210f6e255abf09ac",
+ "active": "88ed9de744392085",
"lastOpenFiles": [
+ "08-Inbox/Office/Start.pptx",
"03-学习笔记/数据备份方案.md",
+ "08-Inbox/Office/Start.docx",
"01-工作日记/2025-08-26.md",
+ "08-Inbox/Office/Start.xlsx",
+ "08-Inbox/Office",
+ "08-Inbox/未命名文件夹",
"01-工作日记/2025-08-25.md",
"50-Memos/2025/08/党员组织看山河为证2个多小时 晚上在最大的杨裕兴面馆吃面 (2025-08-25 19-25).md",
"60-Imports/63-WuCai/2025/08/Plex重大变更:无论使用自己还是别人的媒体服务器远程播放都必须付费 – 蓝点网.md",
@@ -367,7 +348,6 @@
"10-WebClipper/扩展无限可能:Obsidian Web Viewer插件解析.md",
"10-WebClipper/Pocket.md",
"02-技术评测/AI专题/免费使用Gemini 2.5 Pro的完整教程.md",
- "02-技术评测/AI专题/AI使用评测.md",
"50-Memos/2025/08/resources/npLDiAioDzxYNMCxDndP3Q_A0FB7954-FD46-4F7F-A39E-E5284AAD9223.jpg",
"08-Inbox/B99016FB-F1A3-433A-BF44-75086CF8B7F6.txt",
"08-Inbox/assets/68691ee74ca1b.png",
@@ -375,11 +355,6 @@
"70-SimpRead/output/Obsidian 和 Logseq 对比 - 拾月/assets",
"70-SimpRead/output/Obsidian 和 Logseq 对比 - 拾月",
"70-SimpRead/output/本地快照系统.pdf",
- "70-SimpRead/output/assets",
- "70-SimpRead/Inbox/assets",
- "70-SimpRead/output/tmp-本地快照系统.html",
- "70-SimpRead/simpread_config.json",
- "simpread_config.json",
"50-Memos/2025/08/resources/JghbgqDFVMTm2ZbzCdqT3T_22902977-BC80-405E-8D23-996AFB1B7939.jpg",
"50-Memos/2025/08/resources/QxEtttRT6WPdBi6WWzSgMj_E6DB1120-D906-409D-99DE-374ADE4B4E7E.jpg",
"50-Memos/2025/08/resources/iBGNVYtLw8a8HuefAMGv6L_FD566CF2-5ECB-479D-844E-36A3977F3EE3.jpg",
diff --git a/01-工作日记/2025-08-26.md b/01-工作日记/2025-08-26.md
index fed97795..9778dd1a 100644
--- a/01-工作日记/2025-08-26.md
+++ b/01-工作日记/2025-08-26.md
@@ -6,7 +6,7 @@ tags:
rating: ⭐️
excerpt:
created: 2025-08-26T10:08
-updated: 2025-08-26T17:43
+updated: 2025-08-26T23:01
---
## 1TODO
- [x] 多喝水,避免久坐不动 ✅ 2025-08-26
@@ -128,6 +128,7 @@ nextcloud也需要域名信任,修改config.php文件!ssh->root权限!
- fnOS-T:更新下载镜像到90%后失败?
## 3 Tips
+- 偶然发现:OB有onlyoffice插件,实现office文件联动
## 4 Questions
diff --git a/08-Inbox/Office/Start.docx b/08-Inbox/Office/Start.docx
new file mode 100644
index 00000000..4c0241a3
Binary files /dev/null and b/08-Inbox/Office/Start.docx differ
diff --git a/08-Inbox/Office/Start.pptx b/08-Inbox/Office/Start.pptx
new file mode 100644
index 00000000..a2f4c71f
Binary files /dev/null and b/08-Inbox/Office/Start.pptx differ
diff --git a/08-Inbox/Office/Start.xlsx b/08-Inbox/Office/Start.xlsx
new file mode 100644
index 00000000..8d896833
Binary files /dev/null and b/08-Inbox/Office/Start.xlsx differ