From 75266621685d908b192e4dc43496f2a7262ce9a1 Mon Sep 17 00:00:00 2001 From: Awin Huang Date: Wed, 19 Oct 2022 10:52:44 +0800 Subject: [PATCH] Update --- .obsidian/community-plugins.json | 4 +- .obsidian/daily-notes.json | 2 +- .obsidian/plugins/obsidian-git/data.json | 8 +- .obsidian/plugins/obsidian-git/main.js | 1445 +++++++++++------ .obsidian/plugins/obsidian-git/manifest.json | 2 +- .obsidian/plugins/obsidian-git/styles.css | 451 ++--- .obsidian/snippets/obsidian.css | 5 +- .obsidian/workspace.json | 64 +- 00. Inbox/00. Inbox/Coding style.md | 8 + 01. 個人/01. Daily/2022-10-17(週一).md | 61 + 01. 個人/01. Daily/2022-10-18(週二).md | 58 + 04. Programming/Python/Poetry.md | 1030 ++++++++++++ .../Tool Setup/Software/Visual Studio Code.md | 5 +- .../{Windows Setup.md => Windows 10 Setup.md} | 0 .../Tool Setup/Software/Windows 11 Setup.md | 191 +++ .../Tool Setup/Software/Windows Terminal.md | 415 +++-- 16 files changed, 2894 insertions(+), 855 deletions(-) create mode 100644 00. Inbox/00. Inbox/Coding style.md create mode 100644 01. 個人/01. Daily/2022-10-17(週一).md create mode 100644 01. 個人/01. Daily/2022-10-18(週二).md create mode 100644 04. Programming/Python/Poetry.md rename 05. 資料收集/Tool Setup/Software/{Windows Setup.md => Windows 10 Setup.md} (100%) create mode 100644 05. 資料收集/Tool Setup/Software/Windows 11 Setup.md diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index e2f69a0..0a402fd 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -5,9 +5,9 @@ "todoist-sync-plugin", "obsidian-columns", "obsidian-tasks-plugin", - "obsidian-git", "oz-image-plugin", "periodic-notes", "obsidian-kanban", - "obsidian-mind-map" + "obsidian-mind-map", + "obsidian-git" ] \ No newline at end of file diff --git a/.obsidian/daily-notes.json b/.obsidian/daily-notes.json index ed5a618..0fdd6ba 100644 --- a/.obsidian/daily-notes.json +++ b/.obsidian/daily-notes.json @@ -1,5 +1,5 @@ { "folder": "01. 個人/01. Daily", "format": "YYYY-MM-DD(ddd)", - "template": "04. 資料收集/99. templates/日記" + "template": "05. 資料收集/99. templates/日記" } \ No newline at end of file diff --git a/.obsidian/plugins/obsidian-git/data.json b/.obsidian/plugins/obsidian-git/data.json index a02f136..7774daf 100644 --- a/.obsidian/plugins/obsidian-git/data.json +++ b/.obsidian/plugins/obsidian-git/data.json @@ -2,19 +2,19 @@ "commitMessage": "vault backup: {{date}}", "autoCommitMessage": "vault backup: {{date}}", "commitDateFormat": "YYYY-MM-DD HH:mm:ss", - "autoSaveInterval": 10, + "autoSaveInterval": 3, "autoPushInterval": 0, "autoPullInterval": 0, - "autoPullOnBoot": true, + "autoPullOnBoot": false, "disablePush": false, "pullBeforePush": true, "disablePopups": false, - "listChangedFilesInMessageBody": true, + "listChangedFilesInMessageBody": false, "showStatusBar": true, "updateSubmodules": false, "syncMethod": "merge", "customMessageOnAutoBackup": false, - "autoBackupAfterFileChange": false, + "autoBackupAfterFileChange": true, "treeStructure": false, "refreshSourceControl": true, "basePath": "", diff --git a/.obsidian/plugins/obsidian-git/main.js b/.obsidian/plugins/obsidian-git/main.js index 9c81c25..0990c5b 100644 --- a/.obsidian/plugins/obsidian-git/main.js +++ b/.obsidian/plugins/obsidian-git/main.js @@ -19204,7 +19204,7 @@ var GitManager = class { getPath(path2, relativeToVault) { return relativeToVault && this.plugin.settings.basePath.length > 0 ? path2.substring(this.plugin.settings.basePath.length + 1) : path2; } - getTreeStructure(children2, beginLength = 0) { + _getTreeStructure(children2, beginLength = 0) { const list = []; children2 = [...children2]; while (children2.length > 0) { @@ -19216,17 +19216,66 @@ var GitManager = class { return item.path.substring(beginLength).startsWith(title + "/"); }); childrenWithSameTitle.forEach((item) => children2.remove(item)); + const path2 = first2.path.substring(0, restPath.indexOf("/") + beginLength); list.push({ title, - children: this.getTreeStructure(childrenWithSameTitle, (beginLength > 0 ? beginLength + title.length : title.length) + 1) + path: path2, + vaultPath: this.getVaultPath(path2), + children: this._getTreeStructure(childrenWithSameTitle, (beginLength > 0 ? beginLength + title.length : title.length) + 1) }); } else { - list.push({ title: restPath, statusResult: first2 }); + list.push({ + title: restPath, + statusResult: first2, + path: first2.path, + vaultPath: this.getVaultPath(first2.path) + }); children2.remove(first2); } } return list; } + simplify(tree) { + var _a2, _b, _c, _d; + for (const node of tree) { + while (true) { + const singleChild = ((_a2 = node.children) == null ? void 0 : _a2.length) == 1; + const singleChildIsDir = ((_c = (_b = node.children) == null ? void 0 : _b.first()) == null ? void 0 : _c.statusResult) == void 0; + if (!(node.children != void 0 && singleChild && singleChildIsDir)) + break; + const child = node.children.first(); + node.title += "/" + child.title; + node.statusResult = child.statusResult; + node.path = child.path; + node.vaultPath = child.vaultPath; + node.children = child.children; + } + if (node.children != void 0) { + this.simplify(node.children); + } + (_d = node.children) == null ? void 0 : _d.sort((a, b) => { + const dirCompare = (b.statusResult == void 0 ? 1 : 0) - (a.statusResult == void 0 ? 1 : 0); + if (dirCompare != 0) { + return dirCompare; + } else { + return a.title.localeCompare(b.title); + } + }); + } + return tree.sort((a, b) => { + const dirCompare = (b.statusResult == void 0 ? 1 : 0) - (a.statusResult == void 0 ? 1 : 0); + if (dirCompare != 0) { + return dirCompare; + } else { + return a.title.localeCompare(b.title); + } + }); + } + getTreeStructure(children2) { + const tree = this._getTreeStructure(children2); + const res = this.simplify(tree); + return res; + } async formatCommitMessage(template) { let status2; if (template.includes("{{numFiles}}")) { @@ -19732,7 +19781,7 @@ var IsomorphicGit = class extends GitManager { const res = await this.getStagedFiles(dir != null ? dir : "."); staged = res.map(({ filepath }) => filepath); } - await Promise.all(staged.map((file) => this.unstage(file, false))); + await this.wrapFS(Promise.all(staged.map((file) => isomorphic_git_default.resetIndex({ ...this.getRepo(), filepath: file })))); } catch (error) { this.plugin.displayError(error); throw error; @@ -19747,6 +19796,24 @@ var IsomorphicGit = class extends GitManager { throw error; } } + async discardAll({ dir, status: status2 }) { + let files = []; + if (status2) { + if (dir != void 0) { + files = status2.changed.filter((file) => file.path.startsWith(dir)).map((file) => file.path); + } else { + files = status2.changed.map((file) => file.path); + } + } else { + files = (await this.getUnstagedFiles(dir)).map(({ filepath }) => filepath); + } + try { + await this.wrapFS(isomorphic_git_default.checkout({ ...this.getRepo(), filepaths: files, force: true })); + } catch (error) { + this.plugin.displayError(error); + throw error; + } + } getProgressText(action, event) { let out = `${action} progress:`; if (event.phase) { @@ -24098,9 +24165,9 @@ var SimpleGit = class extends GitManager { await this.git.add(dir != null ? dir : "-A", (err) => this.onError(err)); this.plugin.setState(PluginState.idle); } - async unstageAll() { + async unstageAll({ dir }) { this.plugin.setState(PluginState.add); - await this.git.reset([], (err) => this.onError(err)); + await this.git.reset(dir != void 0 ? ["--", dir] : [], (err) => this.onError(err)); this.plugin.setState(PluginState.idle); } async unstage(path2, relativeToVault) { @@ -24114,6 +24181,9 @@ var SimpleGit = class extends GitManager { await this.git.checkout(["--", filepath], (err) => this.onError(err)); this.plugin.setState(PluginState.idle); } + async discardAll({ dir }) { + return this.discard(dir != null ? dir : "."); + } async pull() { this.plugin.setState(PluginState.pull); if (this.plugin.settings.updateSubmodules) @@ -24139,7 +24209,7 @@ var SimpleGit = class extends GitManager { } else if (this.plugin.settings.syncMethod === "reset") { try { await this.git.raw(["update-ref", `refs/heads/${branchInfo.current}`, upstreamCommit], (err) => this.onError(err)); - await this.unstageAll(); + await this.unstageAll({}); } catch (err) { this.plugin.displayError(`Sync failed (${this.plugin.settings.syncMethod}): ${err.message}`); } @@ -24162,7 +24232,7 @@ var SimpleGit = class extends GitManager { const status2 = await this.git.status(); const trackingBranch = status2.tracking; const currentBranch2 = status2.current; - const remoteChangedFiles = (await this.git.diffSummary([currentBranch2, trackingBranch], (err) => this.onError(err))).changed; + const remoteChangedFiles = (await this.git.diffSummary([currentBranch2, trackingBranch, "--"], (err) => this.onError(err))).changed; this.plugin.setState(PluginState.push); if (this.plugin.settings.updateSubmodules) { await this.git.env({ ...process.env, "OBSIDIAN_GIT": 1 }).subModule(["foreach", "--recursive", `tracking=$(git for-each-ref --format='%(upstream:short)' "$(git symbolic-ref -q HEAD)"); echo $tracking; if [ ! -z "$(git diff --shortstat $tracking)" ]; then git push; fi`], (err) => this.onError(err)); @@ -24177,7 +24247,7 @@ var SimpleGit = class extends GitManager { const status2 = await this.git.status((err) => this.onError(err)); const trackingBranch = status2.tracking; const currentBranch2 = status2.current; - const remoteChangedFiles = (await this.git.diffSummary([currentBranch2, trackingBranch])).changed; + const remoteChangedFiles = (await this.git.diffSummary([currentBranch2, trackingBranch, "--"])).changed; return remoteChangedFiles !== 0; } async checkRequirements() { @@ -24472,7 +24542,7 @@ var ObsidianGitSettingsTab = class extends import_obsidian7.PluginSettingTab { plugin.settings.refreshSourceControl = value; plugin.saveSettings(); })); - new import_obsidian7.Setting(containerEl).setName("Source Control View refresh interval").setDesc("Milliseconds two wait after file change before refreshing the Source Control View").addText((toggle) => toggle.setValue(plugin.settings.refreshSourceControlTimer.toString()).setPlaceholder("7000").onChange((value) => { + new import_obsidian7.Setting(containerEl).setName("Source Control View refresh interval").setDesc("Milliseconds to wait after file change before refreshing the Source Control View").addText((toggle) => toggle.setValue(plugin.settings.refreshSourceControlTimer.toString()).setPlaceholder("7000").onChange((value) => { plugin.settings.refreshSourceControlTimer = Math.max(parseInt(value), 500); plugin.saveSettings(); plugin.setRefreshDebouncer(); @@ -26734,6 +26804,13 @@ function set_data(text2, data) { function set_input_value(input, value) { input.value = value == null ? "" : value; } +function set_style(node, key2, value, important) { + if (value === null) { + node.style.removeProperty(key2); + } else { + node.style.setProperty(key2, value, important ? "important" : ""); + } +} function toggle_class(element2, name, toggle) { element2.classList[toggle ? "add" : "remove"](name); } @@ -27037,7 +27114,9 @@ var boolean_attributes = new Set([ "disabled", "formnovalidate", "hidden", + "inert", "ismap", + "itemscope", "loop", "multiple", "muted", @@ -27054,13 +27133,13 @@ function create_component(block) { block && block.c(); } function mount_component(component, target, anchor, customElement) { - const { fragment, on_mount, on_destroy, after_update } = component.$$; + const { fragment, after_update } = component.$$; fragment && fragment.m(target, anchor); if (!customElement) { add_render_callback(() => { - const new_on_destroy = on_mount.map(run).filter(is_function); - if (on_destroy) { - on_destroy.push(...new_on_destroy); + const new_on_destroy = component.$$.on_mount.map(run).filter(is_function); + if (component.$$.on_destroy) { + component.$$.on_destroy.push(...new_on_destroy); } else { run_all(new_on_destroy); } @@ -27091,7 +27170,7 @@ function init2(component, options, instance6, create_fragment6, not_equal, props set_current_component(component); const $$ = component.$$ = { fragment: null, - ctx: null, + ctx: [], props, update: noop, not_equal, @@ -27165,6 +27244,9 @@ if (typeof HTMLElement === "function") { this.$destroy = noop; } $on(type, callback) { + if (!is_function(callback)) { + return noop; + } const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []); callbacks.push(callback); return () => { @@ -27188,6 +27270,9 @@ var SvelteComponent = class { this.$destroy = noop; } $on(type, callback) { + if (!is_function(callback)) { + return noop; + } const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []); callbacks.push(callback); return () => { @@ -27240,32 +27325,10 @@ function slide(node, { delay: delay2 = 0, duration = 400, easing = cubicOut } = }; } -// src/ui/sidebar/components/fileComponent.svelte -init_polyfill_buffer(); -var import_obsidian18 = __toModule(require("obsidian")); - -// node_modules/obsidian-community-lib/dist/index.js -init_polyfill_buffer(); - -// node_modules/obsidian-community-lib/dist/utils.js -init_polyfill_buffer(); -var feather = __toModule(require_feather()); -var import_obsidian16 = __toModule(require("obsidian")); -function hoverPreview(event, view, to) { - const targetEl = event.target; - app.workspace.trigger("hover-link", { - event, - source: view.getViewType(), - hoverParent: view, - targetEl, - linktext: to - }); -} - // src/ui/modals/discardModal.ts init_polyfill_buffer(); -var import_obsidian17 = __toModule(require("obsidian")); -var DiscardModal = class extends import_obsidian17.Modal { +var import_obsidian16 = __toModule(require("obsidian")); +var DiscardModal = class extends import_obsidian16.Modal { constructor(app2, deletion, filename) { super(app2); this.deletion = deletion; @@ -27284,14 +27347,22 @@ var DiscardModal = class extends import_obsidian17.Modal { contentEl.createEl("h4").setText(`Do you really want to ${this.deletion ? "delete" : "discard the changes of"} "${this.filename}"`); const div = contentEl.createDiv(); div.addClass("obsidian-git-center"); - div.createEl("button", { text: "Cancel" }).addEventListener("click", () => { + div.createEl("button", { + text: "Cancel", + attr: { + style: "margin: 0 10px" + } + }).addEventListener("click", () => { if (this.resolve) this.resolve(false); return this.close(); }); div.createEl("button", { cls: "mod-cta", - text: "Confirm" + text: "Confirm", + attr: { + style: "margin: 0 10px" + } }).addEventListener("click", async () => { if (this.resolve) this.resolve(true); @@ -27304,9 +27375,31 @@ var DiscardModal = class extends import_obsidian17.Modal { } }; +// src/ui/sidebar/components/fileComponent.svelte +init_polyfill_buffer(); +var import_obsidian18 = __toModule(require("obsidian")); + +// node_modules/obsidian-community-lib/dist/index.js +init_polyfill_buffer(); + +// node_modules/obsidian-community-lib/dist/utils.js +init_polyfill_buffer(); +var feather = __toModule(require_feather()); +var import_obsidian17 = __toModule(require("obsidian")); +function hoverPreview(event, view, to) { + const targetEl = event.target; + app.workspace.trigger("hover-link", { + event, + source: view.getViewType(), + hoverParent: view, + targetEl, + linktext: to + }); +} + // src/ui/sidebar/components/fileComponent.svelte function add_css(target) { - append_styles(target, "svelte-1furf50", "main.svelte-1furf50.svelte-1furf50.svelte-1furf50{cursor:pointer;background-color:var(--background-secondary);border-radius:4px;width:98%;display:flex;justify-content:space-between;font-size:0.8rem;margin-bottom:2px}main.svelte-1furf50 .path.svelte-1furf50.svelte-1furf50{color:var(--text-muted);white-space:nowrap;max-width:75%;overflow:hidden;text-overflow:ellipsis}main.svelte-1furf50:hover .path.svelte-1furf50.svelte-1furf50{color:var(--text-normal);transition:all 200ms}main.svelte-1furf50 .tools.svelte-1furf50.svelte-1furf50{display:flex;align-items:center}main.svelte-1furf50 .tools .type.svelte-1furf50.svelte-1furf50{height:16px;width:16px;margin:0;display:flex;align-items:center;justify-content:center}main.svelte-1furf50 .tools .type[data-type=M].svelte-1furf50.svelte-1furf50{color:orange}main.svelte-1furf50 .tools .type[data-type=D].svelte-1furf50.svelte-1furf50{color:red}main.svelte-1furf50 .tools .buttons.svelte-1furf50.svelte-1furf50{display:flex}main.svelte-1furf50 .tools .buttons.svelte-1furf50>.svelte-1furf50{color:var(--text-faint);height:16px;width:16px;margin:0;transition:all 0.2s;border-radius:2px;margin-right:1px}main.svelte-1furf50 .tools .buttons.svelte-1furf50>.svelte-1furf50:hover{color:var(--text-normal);background-color:var(--interactive-accent)}"); + append_styles(target, "svelte-wn85nz", "main.svelte-wn85nz .nav-file-title-content.svelte-wn85nz.svelte-wn85nz{display:flex;align-items:center}main.svelte-wn85nz .tools.svelte-wn85nz.svelte-wn85nz{display:flex;margin-left:auto}main.svelte-wn85nz .tools .type.svelte-wn85nz.svelte-wn85nz{padding-left:var(--size-2-1);width:11px;display:flex;align-items:center;justify-content:center}main.svelte-wn85nz .tools .type[data-type=M].svelte-wn85nz.svelte-wn85nz{color:orange}main.svelte-wn85nz .tools .type[data-type=D].svelte-wn85nz.svelte-wn85nz{color:red}main.svelte-wn85nz .tools .buttons.svelte-wn85nz.svelte-wn85nz{display:flex}main.svelte-wn85nz .tools .buttons.svelte-wn85nz>.svelte-wn85nz{padding:0 0;height:auto}"); } function create_if_block(ctx) { let div; @@ -27317,7 +27410,7 @@ function create_if_block(ctx) { div = element("div"); attr(div, "data-icon", "go-to-file"); attr(div, "aria-label", "Open File"); - attr(div, "class", "svelte-1furf50"); + attr(div, "class", "clickable-icon svelte-wn85nz"); }, m(target, anchor) { insert(target, div, anchor); @@ -27343,82 +27436,88 @@ function create_if_block(ctx) { function create_fragment(ctx) { var _a2; let main; - let span0; + let div6; + let div0; let t0_value = ((_a2 = ctx[0].vault_path.split("/").last()) == null ? void 0 : _a2.replace(".md", "")) + ""; let t0; - let span0_aria_label_value; let t1; + let div5; let div3; - let div2; let show_if = ctx[1].app.vault.getAbstractFileByPath(ctx[0].vault_path); let t2; - let div0; - let t3; let div1; + let t3; + let div2; let t4; - let span1; + let div4; let t5_value = ctx[0].working_dir + ""; let t5; - let span1_data_type_value; + let div4_data_type_value; + let div6_aria_label_value; let mounted; let dispose; let if_block = show_if && create_if_block(ctx); return { c() { main = element("main"); - span0 = element("span"); + div6 = element("div"); + div0 = element("div"); t0 = text(t0_value); t1 = space(); + div5 = element("div"); div3 = element("div"); - div2 = element("div"); if (if_block) if_block.c(); t2 = space(); - div0 = element("div"); - t3 = space(); div1 = element("div"); + t3 = space(); + div2 = element("div"); t4 = space(); - span1 = element("span"); + div4 = element("div"); t5 = text(t5_value); - attr(span0, "class", "path svelte-1furf50"); - attr(span0, "aria-label-position", ctx[3]); - attr(span0, "aria-label", span0_aria_label_value = ctx[0].vault_path.split("/").last() != ctx[0].vault_path ? ctx[0].vault_path : ""); - attr(div0, "data-icon", "skip-back"); - attr(div0, "aria-label", "Discard"); - attr(div0, "class", "svelte-1furf50"); - attr(div1, "data-icon", "plus"); - attr(div1, "aria-label", "Stage"); - attr(div1, "class", "svelte-1furf50"); - attr(div2, "class", "buttons svelte-1furf50"); - attr(span1, "class", "type svelte-1furf50"); - attr(span1, "data-type", span1_data_type_value = ctx[0].working_dir); - attr(div3, "class", "tools svelte-1furf50"); - attr(main, "class", "svelte-1furf50"); + attr(div0, "class", "nav-file-title-content svelte-wn85nz"); + attr(div1, "data-icon", "undo"); + attr(div1, "aria-label", "Discard"); + attr(div1, "class", "clickable-icon svelte-wn85nz"); + attr(div2, "data-icon", "plus"); + attr(div2, "aria-label", "Stage"); + attr(div2, "class", "clickable-icon svelte-wn85nz"); + attr(div3, "class", "buttons svelte-wn85nz"); + attr(div4, "class", "type svelte-wn85nz"); + attr(div4, "data-type", div4_data_type_value = ctx[0].working_dir); + attr(div5, "class", "tools svelte-wn85nz"); + attr(div6, "class", "nav-file-title"); + attr(div6, "aria-label-position", ctx[3]); + attr(div6, "aria-label", div6_aria_label_value = ctx[0].vault_path.split("/").last() != ctx[0].vault_path ? ctx[0].vault_path : ""); + attr(main, "class", "nav-file svelte-wn85nz"); }, m(target, anchor) { insert(target, main, anchor); - append2(main, span0); - append2(span0, t0); - append2(main, t1); - append2(main, div3); - append2(div3, div2); + append2(main, div6); + append2(div6, div0); + append2(div0, t0); + append2(div6, t1); + append2(div6, div5); + append2(div5, div3); if (if_block) - if_block.m(div2, null); - append2(div2, t2); - append2(div2, div0); - ctx[12](div0); - append2(div2, t3); - append2(div2, div1); - ctx[13](div1); - append2(div3, t4); - append2(div3, span1); - append2(span1, t5); + if_block.m(div3, null); + append2(div3, t2); + append2(div3, div1); + ctx[12](div1); + append2(div3, t3); + append2(div3, div2); + ctx[13](div2); + append2(div5, t4); + append2(div5, div4); + append2(div4, t5); if (!mounted) { dispose = [ - listen(span0, "click", self2(ctx[7])), - listen(span0, "auxclick", self2(ctx[7])), - listen(div0, "click", ctx[8]), - listen(div1, "click", ctx[6]), + listen(div0, "click", ctx[7]), + listen(div0, "auxclick", ctx[7]), + listen(div1, "click", ctx[8]), + listen(div2, "click", ctx[6]), + listen(div6, "click", self2(ctx[7])), + listen(div6, "auxclick", self2(ctx[7])), listen(main, "mouseover", ctx[4]), listen(main, "click", self2(ctx[7])), listen(main, "focus", ctx[10]) @@ -27430,12 +27529,6 @@ function create_fragment(ctx) { var _a3; if (dirty & 1 && t0_value !== (t0_value = ((_a3 = ctx2[0].vault_path.split("/").last()) == null ? void 0 : _a3.replace(".md", "")) + "")) set_data(t0, t0_value); - if (dirty & 8) { - attr(span0, "aria-label-position", ctx2[3]); - } - if (dirty & 1 && span0_aria_label_value !== (span0_aria_label_value = ctx2[0].vault_path.split("/").last() != ctx2[0].vault_path ? ctx2[0].vault_path : "")) { - attr(span0, "aria-label", span0_aria_label_value); - } if (dirty & 3) show_if = ctx2[1].app.vault.getAbstractFileByPath(ctx2[0].vault_path); if (show_if) { @@ -27444,7 +27537,7 @@ function create_fragment(ctx) { } else { if_block = create_if_block(ctx2); if_block.c(); - if_block.m(div2, t2); + if_block.m(div3, t2); } } else if (if_block) { if_block.d(1); @@ -27452,8 +27545,14 @@ function create_fragment(ctx) { } if (dirty & 1 && t5_value !== (t5_value = ctx2[0].working_dir + "")) set_data(t5, t5_value); - if (dirty & 1 && span1_data_type_value !== (span1_data_type_value = ctx2[0].working_dir)) { - attr(span1, "data-type", span1_data_type_value); + if (dirty & 1 && div4_data_type_value !== (div4_data_type_value = ctx2[0].working_dir)) { + attr(div4, "data-type", div4_data_type_value); + } + if (dirty & 8) { + attr(div6, "aria-label-position", ctx2[3]); + } + if (dirty & 1 && div6_aria_label_value !== (div6_aria_label_value = ctx2[0].vault_path.split("/").last() != ctx2[0].vault_path ? ctx2[0].vault_path : "")) { + attr(div6, "aria-label", div6_aria_label_value); } }, i: noop, @@ -27476,7 +27575,7 @@ function instance($$self, $$props, $$invalidate) { let { view } = $$props; let { manager } = $$props; let buttons = []; - window.setTimeout(() => buttons.forEach((b) => (0, import_obsidian18.setIcon)(b, b.getAttr("data-icon"), 16)), 0); + window.setTimeout(() => buttons.forEach((b) => (0, import_obsidian18.setIcon)(b, b.getAttr("data-icon"))), 0); function hover(event) { if (!change.path.startsWith(view.app.vault.configDir) || !change.path.startsWith(".")) { hoverPreview(event, view, change.vault_path.split("/").last().replace(".md", "")); @@ -27528,13 +27627,13 @@ function instance($$self, $$props, $$invalidate) { $$invalidate(2, buttons); }); } - function div0_binding($$value) { + function div1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { buttons[0] = $$value; $$invalidate(2, buttons); }); } - function div1_binding($$value) { + function div2_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { buttons[2] = $$value; $$invalidate(2, buttons); @@ -27567,8 +27666,8 @@ function instance($$self, $$props, $$invalidate) { manager, focus_handler, div_binding, - div0_binding, - div1_binding + div1_binding, + div2_binding ]; } var FileComponent = class extends SvelteComponent { @@ -27583,53 +27682,56 @@ var fileComponent_default = FileComponent; init_polyfill_buffer(); var import_obsidian19 = __toModule(require("obsidian")); function add_css2(target) { - append_styles(target, "svelte-1pr4yz5", "main.svelte-1pr4yz5.svelte-1pr4yz5{cursor:pointer;background-color:var(--background-secondary);border-radius:4px;width:98%;display:flex;justify-content:space-between;font-size:0.8rem;margin-bottom:2px}main.svelte-1pr4yz5 .path.svelte-1pr4yz5{color:var(--text-muted);white-space:nowrap;max-width:75%;overflow:hidden;text-overflow:ellipsis}main.svelte-1pr4yz5:hover .path.svelte-1pr4yz5{color:var(--text-normal);transition:all 200ms}main.svelte-1pr4yz5 .tools.svelte-1pr4yz5{display:flex;align-items:center}main.svelte-1pr4yz5 .tools .type.svelte-1pr4yz5{height:16px;width:16px;margin:0;display:flex;align-items:center;justify-content:center}main.svelte-1pr4yz5 .tools .type[data-type=M].svelte-1pr4yz5{color:orange}main.svelte-1pr4yz5 .tools .type[data-type=D].svelte-1pr4yz5{color:red}"); + append_styles(target, "svelte-sajhpp", "main.svelte-sajhpp .nav-file-title-content.svelte-sajhpp{display:flex;align-items:center}main.svelte-sajhpp .tools.svelte-sajhpp{display:flex;margin-left:auto}main.svelte-sajhpp .tools .type.svelte-sajhpp{padding-left:var(--size-2-1);display:flex;align-items:center;justify-content:center}main.svelte-sajhpp .tools .type[data-type=M].svelte-sajhpp{color:orange}main.svelte-sajhpp .tools .type[data-type=D].svelte-sajhpp{color:red}"); } function create_fragment2(ctx) { var _a2; let main; - let span0; + let div2; + let div0; let t0_value = ((_a2 = ctx[0].vault_path.split("/").last()) == null ? void 0 : _a2.replace(".md", "")) + ""; let t0; - let span0_aria_label_value; let t1; - let div; - let span1; + let div1; + let span; let t2_value = ctx[0].working_dir + ""; let t2; - let span1_data_type_value; + let span_data_type_value; + let div2_aria_label_value; let mounted; let dispose; return { c() { main = element("main"); - span0 = element("span"); + div2 = element("div"); + div0 = element("div"); t0 = text(t0_value); t1 = space(); - div = element("div"); - span1 = element("span"); + div1 = element("div"); + span = element("span"); t2 = text(t2_value); - attr(span0, "class", "path svelte-1pr4yz5"); - attr(span0, "aria-label-position", ctx[1]); - attr(span0, "aria-label", span0_aria_label_value = ctx[0].vault_path.split("/").last() != ctx[0].vault_path ? ctx[0].vault_path : ""); - attr(span1, "class", "type svelte-1pr4yz5"); - attr(span1, "data-type", span1_data_type_value = ctx[0].working_dir); - attr(div, "class", "tools svelte-1pr4yz5"); - attr(main, "class", "svelte-1pr4yz5"); + attr(div0, "class", "nav-file-title-content svelte-sajhpp"); + attr(span, "class", "type svelte-sajhpp"); + attr(span, "data-type", span_data_type_value = ctx[0].working_dir); + attr(div1, "class", "tools svelte-sajhpp"); + attr(div2, "class", "nav-file-title"); + attr(div2, "aria-label-position", ctx[1]); + attr(div2, "aria-label", div2_aria_label_value = ctx[0].vault_path.split("/").last() != ctx[0].vault_path ? ctx[0].vault_path : ""); + attr(main, "class", "nav-file svelte-sajhpp"); }, m(target, anchor) { insert(target, main, anchor); - append2(main, span0); - append2(span0, t0); - append2(main, t1); - append2(main, div); - append2(div, span1); - append2(span1, t2); + append2(main, div2); + append2(div2, div0); + append2(div0, t0); + append2(div2, t1); + append2(div2, div1); + append2(div1, span); + append2(span, t2); if (!mounted) { dispose = [ - listen(span0, "click", self2(ctx[3])), listen(main, "mouseover", ctx[2]), - listen(main, "click", self2(ctx[3])), + listen(main, "click", ctx[3]), listen(main, "focus", ctx[5]) ]; mounted = true; @@ -27639,16 +27741,16 @@ function create_fragment2(ctx) { var _a3; if (dirty & 1 && t0_value !== (t0_value = ((_a3 = ctx2[0].vault_path.split("/").last()) == null ? void 0 : _a3.replace(".md", "")) + "")) set_data(t0, t0_value); - if (dirty & 2) { - attr(span0, "aria-label-position", ctx2[1]); - } - if (dirty & 1 && span0_aria_label_value !== (span0_aria_label_value = ctx2[0].vault_path.split("/").last() != ctx2[0].vault_path ? ctx2[0].vault_path : "")) { - attr(span0, "aria-label", span0_aria_label_value); - } if (dirty & 1 && t2_value !== (t2_value = ctx2[0].working_dir + "")) set_data(t2, t2_value); - if (dirty & 1 && span1_data_type_value !== (span1_data_type_value = ctx2[0].working_dir)) { - attr(span1, "data-type", span1_data_type_value); + if (dirty & 1 && span_data_type_value !== (span_data_type_value = ctx2[0].working_dir)) { + attr(span, "data-type", span_data_type_value); + } + if (dirty & 2) { + attr(div2, "aria-label-position", ctx2[1]); + } + if (dirty & 1 && div2_aria_label_value !== (div2_aria_label_value = ctx2[0].vault_path.split("/").last() != ctx2[0].vault_path ? ctx2[0].vault_path : "")) { + attr(div2, "aria-label", div2_aria_label_value); } }, i: noop, @@ -27706,7 +27808,7 @@ var pulledFileComponent_default = PulledFileComponent; init_polyfill_buffer(); var import_obsidian20 = __toModule(require("obsidian")); function add_css3(target) { - append_styles(target, "svelte-15heedx", "main.svelte-15heedx.svelte-15heedx.svelte-15heedx{cursor:pointer;background-color:var(--background-secondary);border-radius:4px;width:98%;display:flex;justify-content:space-between;font-size:0.8rem;margin-bottom:2px}main.svelte-15heedx .path.svelte-15heedx.svelte-15heedx{color:var(--text-muted);white-space:nowrap;max-width:75%;overflow:hidden;text-overflow:ellipsis}main.svelte-15heedx:hover .path.svelte-15heedx.svelte-15heedx{color:var(--text-normal);transition:all 200ms}main.svelte-15heedx .tools.svelte-15heedx.svelte-15heedx{display:flex;align-items:center}main.svelte-15heedx .tools .type.svelte-15heedx.svelte-15heedx{height:16px;width:16px;margin:0;display:flex;align-items:center;justify-content:center}main.svelte-15heedx .tools .type[data-type=M].svelte-15heedx.svelte-15heedx{color:orange}main.svelte-15heedx .tools .type[data-type=D].svelte-15heedx.svelte-15heedx{color:red}main.svelte-15heedx .tools .type[data-type=A].svelte-15heedx.svelte-15heedx{color:yellowgreen}main.svelte-15heedx .tools .type[data-type=R].svelte-15heedx.svelte-15heedx{color:violet}main.svelte-15heedx .tools .buttons.svelte-15heedx.svelte-15heedx{display:flex}main.svelte-15heedx .tools .buttons.svelte-15heedx>.svelte-15heedx{color:var(--text-faint);height:16px;width:16px;margin:0;transition:all 0.2s;border-radius:2px;margin-right:1px}main.svelte-15heedx .tools .buttons.svelte-15heedx>.svelte-15heedx:hover{color:var(--text-normal);background-color:var(--interactive-accent)}"); + append_styles(target, "svelte-wn85nz", "main.svelte-wn85nz .nav-file-title-content.svelte-wn85nz.svelte-wn85nz{display:flex;align-items:center}main.svelte-wn85nz .tools.svelte-wn85nz.svelte-wn85nz{display:flex;margin-left:auto}main.svelte-wn85nz .tools .type.svelte-wn85nz.svelte-wn85nz{padding-left:var(--size-2-1);width:11px;display:flex;align-items:center;justify-content:center}main.svelte-wn85nz .tools .type[data-type=M].svelte-wn85nz.svelte-wn85nz{color:orange}main.svelte-wn85nz .tools .type[data-type=D].svelte-wn85nz.svelte-wn85nz{color:red}main.svelte-wn85nz .tools .buttons.svelte-wn85nz.svelte-wn85nz{display:flex}main.svelte-wn85nz .tools .buttons.svelte-wn85nz>.svelte-wn85nz{padding:0 0;height:auto}"); } function create_if_block2(ctx) { let div; @@ -27717,7 +27819,7 @@ function create_if_block2(ctx) { div = element("div"); attr(div, "data-icon", "go-to-file"); attr(div, "aria-label", "Open File"); - attr(div, "class", "svelte-15heedx"); + attr(div, "class", "clickable-icon svelte-wn85nz"); }, m(target, anchor) { insert(target, div, anchor); @@ -27740,70 +27842,76 @@ function create_if_block2(ctx) { function create_fragment3(ctx) { var _a2; let main; - let span0; + let div5; + let div0; let t0_value = ((_a2 = ctx[3].split("/").last()) == null ? void 0 : _a2.replace(".md", "")) + ""; let t0; - let span0_aria_label_value; let t1; + let div4; let div2; - let div1; let show_if = ctx[1].app.vault.getAbstractFileByPath(ctx[3]); let t2; - let div0; + let div1; let t3; - let span1; + let div3; let t4_value = ctx[0].index + ""; let t4; - let span1_data_type_value; + let div3_data_type_value; + let div5_aria_label_value; let mounted; let dispose; let if_block = show_if && create_if_block2(ctx); return { c() { main = element("main"); - span0 = element("span"); + div5 = element("div"); + div0 = element("div"); t0 = text(t0_value); t1 = space(); + div4 = element("div"); div2 = element("div"); - div1 = element("div"); if (if_block) if_block.c(); t2 = space(); - div0 = element("div"); + div1 = element("div"); t3 = space(); - span1 = element("span"); + div3 = element("div"); t4 = text(t4_value); - attr(span0, "class", "path svelte-15heedx"); - attr(span0, "aria-label-position", ctx[4]); - attr(span0, "aria-label", span0_aria_label_value = ctx[3].split("/").last() != ctx[3] ? ctx[3] : ""); - attr(div0, "data-icon", "minus"); - attr(div0, "aria-label", "Unstage"); - attr(div0, "class", "svelte-15heedx"); - attr(div1, "class", "buttons svelte-15heedx"); - attr(span1, "class", "type svelte-15heedx"); - attr(span1, "data-type", span1_data_type_value = ctx[0].index); - attr(div2, "class", "tools svelte-15heedx"); - attr(main, "class", "svelte-15heedx"); + attr(div0, "class", "nav-file-title-content svelte-wn85nz"); + attr(div1, "data-icon", "minus"); + attr(div1, "aria-label", "Unstage"); + attr(div1, "class", "clickable-icon svelte-wn85nz"); + attr(div2, "class", "buttons svelte-wn85nz"); + attr(div3, "class", "type svelte-wn85nz"); + attr(div3, "data-type", div3_data_type_value = ctx[0].index); + attr(div4, "class", "tools svelte-wn85nz"); + attr(div5, "class", "nav-file-title"); + attr(div5, "aria-label-position", ctx[4]); + attr(div5, "aria-label", div5_aria_label_value = ctx[3].split("/").last() != ctx[3] ? ctx[3] : ""); + attr(main, "class", "nav-file svelte-wn85nz"); }, m(target, anchor) { insert(target, main, anchor); - append2(main, span0); - append2(span0, t0); - append2(main, t1); - append2(main, div2); - append2(div2, div1); + append2(main, div5); + append2(div5, div0); + append2(div0, t0); + append2(div5, t1); + append2(div5, div4); + append2(div4, div2); if (if_block) - if_block.m(div1, null); - append2(div1, t2); - append2(div1, div0); - ctx[12](div0); - append2(div2, t3); - append2(div2, span1); - append2(span1, t4); + if_block.m(div2, null); + append2(div2, t2); + append2(div2, div1); + ctx[12](div1); + append2(div4, t3); + append2(div4, div3); + append2(div3, t4); if (!mounted) { dispose = [ - listen(span0, "click", ctx[7]), - listen(div0, "click", ctx[8]), + listen(div0, "click", ctx[7]), + listen(div0, "auxclick", ctx[7]), + listen(div1, "click", ctx[8]), + listen(div5, "click", self2(ctx[7])), listen(main, "mouseover", ctx[5]), listen(main, "focus", ctx[10]), listen(main, "click", self2(ctx[7])) @@ -27815,12 +27923,6 @@ function create_fragment3(ctx) { var _a3; if (dirty & 8 && t0_value !== (t0_value = ((_a3 = ctx2[3].split("/").last()) == null ? void 0 : _a3.replace(".md", "")) + "")) set_data(t0, t0_value); - if (dirty & 16) { - attr(span0, "aria-label-position", ctx2[4]); - } - if (dirty & 8 && span0_aria_label_value !== (span0_aria_label_value = ctx2[3].split("/").last() != ctx2[3] ? ctx2[3] : "")) { - attr(span0, "aria-label", span0_aria_label_value); - } if (dirty & 10) show_if = ctx2[1].app.vault.getAbstractFileByPath(ctx2[3]); if (show_if) { @@ -27829,7 +27931,7 @@ function create_fragment3(ctx) { } else { if_block = create_if_block2(ctx2); if_block.c(); - if_block.m(div1, t2); + if_block.m(div2, t2); } } else if (if_block) { if_block.d(1); @@ -27837,8 +27939,14 @@ function create_fragment3(ctx) { } if (dirty & 1 && t4_value !== (t4_value = ctx2[0].index + "")) set_data(t4, t4_value); - if (dirty & 1 && span1_data_type_value !== (span1_data_type_value = ctx2[0].index)) { - attr(span1, "data-type", span1_data_type_value); + if (dirty & 1 && div3_data_type_value !== (div3_data_type_value = ctx2[0].index)) { + attr(div3, "data-type", div3_data_type_value); + } + if (dirty & 16) { + attr(div5, "aria-label-position", ctx2[4]); + } + if (dirty & 8 && div5_aria_label_value !== (div5_aria_label_value = ctx2[3].split("/").last() != ctx2[3] ? ctx2[3] : "")) { + attr(div5, "aria-label", div5_aria_label_value); } }, i: noop, @@ -27896,7 +28004,7 @@ function instance3($$self, $$props, $$invalidate) { $$invalidate(2, buttons); }); } - function div0_binding($$value) { + function div1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { buttons[0] = $$value; $$invalidate(2, buttons); @@ -27933,7 +28041,7 @@ function instance3($$self, $$props, $$invalidate) { manager, focus_handler, div_binding, - div0_binding + div1_binding ]; } var StagedFileComponent = class extends SvelteComponent { @@ -27947,115 +28055,177 @@ var stagedFileComponent_default = StagedFileComponent; // src/ui/sidebar/components/treeComponent.svelte init_polyfill_buffer(); function add_css4(target) { - append_styles(target, "svelte-pgmdei", '@charset "UTF-8";main.svelte-pgmdei.svelte-pgmdei:not(.topLevel){margin-left:5px}.opener.svelte-pgmdei.svelte-pgmdei{display:flex;justify-content:space-between;align-items:center;padding:0 4px}.opener.svelte-pgmdei .collapse-icon.svelte-pgmdei::after{content:"\xA0"}.opener.svelte-pgmdei div.svelte-pgmdei{display:flex}.opener.svelte-pgmdei svg.svelte-pgmdei{transform:rotate(-90deg)}.opener.open.svelte-pgmdei svg.svelte-pgmdei{transform:rotate(0)}.opener.svelte-pgmdei span.svelte-pgmdei{font-size:0.8rem}.file-view.svelte-pgmdei.svelte-pgmdei{margin-left:5px}'); + append_styles(target, "svelte-148wteu", "main.svelte-148wteu .nav-folder-title-content.svelte-148wteu.svelte-148wteu{display:flex;align-items:center}main.svelte-148wteu .tools.svelte-148wteu.svelte-148wteu{display:flex;margin-left:auto}main.svelte-148wteu .tools .buttons.svelte-148wteu.svelte-148wteu{display:flex}main.svelte-148wteu .tools .buttons.svelte-148wteu>.svelte-148wteu{padding:0 0;height:auto}"); } function get_each_context(ctx, list, i) { const child_ctx = ctx.slice(); - child_ctx[7] = list[i]; + child_ctx[17] = list[i]; return child_ctx; } function create_else_block(ctx) { - let div2; - let div1; + let div7; + let div6; let div0; let t0; - let span; - let t1_value = ctx[7].title + ""; + let div1; let t1; + let div2; + let t2_value = ctx[17].title + ""; let t2; - let if_block_anchor; + let t3; + let div5; + let div4; + let t4; + let div3; + let div6_aria_label_value; + let t5; + let t6; let current; let mounted; let dispose; function click_handler() { - return ctx[6](ctx[7]); + return ctx[11](ctx[17]); } - let if_block = !ctx[5][ctx[7].title] && create_if_block_4(ctx); + function click_handler_1() { + return ctx[12](ctx[17]); + } + function select_block_type_2(ctx2, dirty) { + if (ctx2[3] == FileType.staged) + return create_if_block_5; + return create_else_block_1; + } + let current_block_type = select_block_type_2(ctx, -1); + let if_block0 = current_block_type(ctx); + function click_handler_5() { + return ctx[16](ctx[17]); + } + let if_block1 = !ctx[5][ctx[17].title] && create_if_block_4(ctx); return { c() { - div2 = element("div"); - div1 = element("div"); + div7 = element("div"); + div6 = element("div"); div0 = element("div"); - div0.innerHTML = ``; t0 = space(); - span = element("span"); - t1 = text(t1_value); - t2 = space(); - if (if_block) - if_block.c(); - if_block_anchor = empty(); - attr(div0, "class", "tree-item-icon collapse-icon svelte-pgmdei"); - attr(div0, "style", ""); - attr(span, "class", "svelte-pgmdei"); - attr(div1, "class", "svelte-pgmdei"); - attr(div2, "class", "opener tree-item-self is-clickable svelte-pgmdei"); - toggle_class(div2, "open", !ctx[5][ctx[7].title]); + div1 = element("div"); + div1.innerHTML = ``; + t1 = space(); + div2 = element("div"); + t2 = text(t2_value); + t3 = space(); + div5 = element("div"); + div4 = element("div"); + if_block0.c(); + t4 = space(); + div3 = element("div"); + t5 = space(); + if (if_block1) + if_block1.c(); + t6 = space(); + attr(div0, "data-icon", "folder"); + set_style(div0, "padding-right", "5px"); + set_style(div0, "display", "flex"); + attr(div1, "class", "nav-folder-collapse-indicator collapse-icon"); + attr(div2, "class", "nav-folder-title-content svelte-148wteu"); + set_style(div3, "width", "11px"); + attr(div3, "class", "svelte-148wteu"); + attr(div4, "class", "buttons svelte-148wteu"); + attr(div5, "class", "tools svelte-148wteu"); + attr(div6, "class", "nav-folder-title"); + attr(div6, "aria-label-position", ctx[6]); + attr(div6, "aria-label", div6_aria_label_value = ctx[17].vaultPath.split("/").last() != ctx[17].vaultPath ? ctx[17].vaultPath : ""); + attr(div7, "class", "nav-folder"); + toggle_class(div7, "is-collapsed", ctx[5][ctx[17].title]); }, m(target, anchor) { - insert(target, div2, anchor); - append2(div2, div1); - append2(div1, div0); - append2(div1, t0); - append2(div1, span); - append2(span, t1); - insert(target, t2, anchor); - if (if_block) - if_block.m(target, anchor); - insert(target, if_block_anchor, anchor); + insert(target, div7, anchor); + append2(div7, div6); + append2(div6, div0); + append2(div6, t0); + append2(div6, div1); + append2(div6, t1); + append2(div6, div2); + append2(div2, t2); + append2(div6, t3); + append2(div6, div5); + append2(div5, div4); + if_block0.m(div4, null); + append2(div4, t4); + append2(div4, div3); + append2(div7, t5); + if (if_block1) + if_block1.m(div7, null); + append2(div7, t6); current = true; if (!mounted) { - dispose = listen(div2, "click", click_handler); + dispose = [ + listen(div1, "click", click_handler), + listen(div2, "click", click_handler_1), + listen(div6, "click", self2(click_handler_5)) + ]; mounted = true; } }, p(new_ctx, dirty) { ctx = new_ctx; - if ((!current || dirty & 1) && t1_value !== (t1_value = ctx[7].title + "")) - set_data(t1, t1_value); - if (!current || dirty & 33) { - toggle_class(div2, "open", !ctx[5][ctx[7].title]); + if ((!current || dirty & 1) && t2_value !== (t2_value = ctx[17].title + "")) + set_data(t2, t2_value); + if (current_block_type === (current_block_type = select_block_type_2(ctx, dirty)) && if_block0) { + if_block0.p(ctx, dirty); + } else { + if_block0.d(1); + if_block0 = current_block_type(ctx); + if (if_block0) { + if_block0.c(); + if_block0.m(div4, t4); + } } - if (!ctx[5][ctx[7].title]) { - if (if_block) { - if_block.p(ctx, dirty); + if (!current || dirty & 64) { + attr(div6, "aria-label-position", ctx[6]); + } + if (!current || dirty & 1 && div6_aria_label_value !== (div6_aria_label_value = ctx[17].vaultPath.split("/").last() != ctx[17].vaultPath ? ctx[17].vaultPath : "")) { + attr(div6, "aria-label", div6_aria_label_value); + } + if (!ctx[5][ctx[17].title]) { + if (if_block1) { + if_block1.p(ctx, dirty); if (dirty & 33) { - transition_in(if_block, 1); + transition_in(if_block1, 1); } } else { - if_block = create_if_block_4(ctx); - if_block.c(); - transition_in(if_block, 1); - if_block.m(if_block_anchor.parentNode, if_block_anchor); + if_block1 = create_if_block_4(ctx); + if_block1.c(); + transition_in(if_block1, 1); + if_block1.m(div7, t6); } - } else if (if_block) { + } else if (if_block1) { group_outros(); - transition_out(if_block, 1, 1, () => { - if_block = null; + transition_out(if_block1, 1, 1, () => { + if_block1 = null; }); check_outros(); } + if (!current || dirty & 33) { + toggle_class(div7, "is-collapsed", ctx[5][ctx[17].title]); + } }, i(local) { if (current) return; - transition_in(if_block); + transition_in(if_block1); current = true; }, o(local) { - transition_out(if_block); + transition_out(if_block1); current = false; }, d(detaching) { if (detaching) - detach(div2); - if (detaching) - detach(t2); - if (if_block) - if_block.d(detaching); - if (detaching) - detach(if_block_anchor); + detach(div7); + if_block0.d(); + if (if_block1) + if_block1.d(); mounted = false; - dispose(); + run_all(dispose); } }; } @@ -28085,7 +28255,6 @@ function create_if_block3(ctx) { if (if_block) if_block.c(); t = space(); - attr(div, "class", "file-view svelte-pgmdei"); }, m(target, anchor) { insert(target, div, anchor); @@ -28144,15 +28313,100 @@ function create_if_block3(ctx) { } }; } +function create_else_block_1(ctx) { + let div0; + let t; + let div1; + let mounted; + let dispose; + function click_handler_3() { + return ctx[14](ctx[17]); + } + function click_handler_4() { + return ctx[15](ctx[17]); + } + return { + c() { + div0 = element("div"); + div0.innerHTML = ``; + t = space(); + div1 = element("div"); + div1.innerHTML = ``; + attr(div0, "data-icon", "undo"); + attr(div0, "aria-label", "Discard"); + attr(div0, "class", "clickable-icon svelte-148wteu"); + attr(div1, "data-icon", "plus"); + attr(div1, "aria-label", "Stage"); + attr(div1, "class", "clickable-icon svelte-148wteu"); + }, + m(target, anchor) { + insert(target, div0, anchor); + insert(target, t, anchor); + insert(target, div1, anchor); + if (!mounted) { + dispose = [ + listen(div0, "click", click_handler_3), + listen(div1, "click", click_handler_4) + ]; + mounted = true; + } + }, + p(new_ctx, dirty) { + ctx = new_ctx; + }, + d(detaching) { + if (detaching) + detach(div0); + if (detaching) + detach(t); + if (detaching) + detach(div1); + mounted = false; + run_all(dispose); + } + }; +} +function create_if_block_5(ctx) { + let div; + let mounted; + let dispose; + function click_handler_2() { + return ctx[13](ctx[17]); + } + return { + c() { + div = element("div"); + div.innerHTML = ``; + attr(div, "data-icon", "minus"); + attr(div, "aria-label", "Unstage"); + attr(div, "class", "clickable-icon svelte-148wteu"); + }, + m(target, anchor) { + insert(target, div, anchor); + if (!mounted) { + dispose = listen(div, "click", click_handler_2); + mounted = true; + } + }, + p(new_ctx, dirty) { + ctx = new_ctx; + }, + d(detaching) { + if (detaching) + detach(div); + mounted = false; + dispose(); + } + }; +} function create_if_block_4(ctx) { let div; let treecomponent; - let t; let div_transition; let current; treecomponent = new TreeComponent({ props: { - hierarchy: ctx[7], + hierarchy: ctx[17], plugin: ctx[1], view: ctx[2], fileType: ctx[3] @@ -28162,19 +28416,17 @@ function create_if_block_4(ctx) { c() { div = element("div"); create_component(treecomponent.$$.fragment); - t = space(); - attr(div, "class", "file-view svelte-pgmdei"); + attr(div, "class", "nav-folder-children"); }, m(target, anchor) { insert(target, div, anchor); mount_component(treecomponent, div, null); - append2(div, t); current = true; }, p(ctx2, dirty) { const treecomponent_changes = {}; if (dirty & 1) - treecomponent_changes.hierarchy = ctx2[7]; + treecomponent_changes.hierarchy = ctx2[17]; if (dirty & 2) treecomponent_changes.plugin = ctx2[1]; if (dirty & 4) @@ -28190,7 +28442,7 @@ function create_if_block_4(ctx) { if (local) { add_render_callback(() => { if (!div_transition) - div_transition = create_bidirectional_transition(div, slide, { duration: 75 }, true); + div_transition = create_bidirectional_transition(div, slide, { duration: 150 }, true); div_transition.run(1); }); } @@ -28200,7 +28452,7 @@ function create_if_block_4(ctx) { transition_out(treecomponent.$$.fragment, local); if (local) { if (!div_transition) - div_transition = create_bidirectional_transition(div, slide, { duration: 75 }, false); + div_transition = create_bidirectional_transition(div, slide, { duration: 150 }, false); div_transition.run(0); } current = false; @@ -28219,7 +28471,7 @@ function create_if_block_3(ctx) { let current; pulledfilecomponent = new pulledFileComponent_default({ props: { - change: ctx[7].statusResult, + change: ctx[17].statusResult, view: ctx[2] } }); @@ -28234,7 +28486,7 @@ function create_if_block_3(ctx) { p(ctx2, dirty) { const pulledfilecomponent_changes = {}; if (dirty & 1) - pulledfilecomponent_changes.change = ctx2[7].statusResult; + pulledfilecomponent_changes.change = ctx2[17].statusResult; if (dirty & 4) pulledfilecomponent_changes.view = ctx2[2]; pulledfilecomponent.$set(pulledfilecomponent_changes); @@ -28259,7 +28511,7 @@ function create_if_block_2(ctx) { let current; filecomponent = new fileComponent_default({ props: { - change: ctx[7].statusResult, + change: ctx[17].statusResult, manager: ctx[1].gitManager, view: ctx[2] } @@ -28275,7 +28527,7 @@ function create_if_block_2(ctx) { p(ctx2, dirty) { const filecomponent_changes = {}; if (dirty & 1) - filecomponent_changes.change = ctx2[7].statusResult; + filecomponent_changes.change = ctx2[17].statusResult; if (dirty & 2) filecomponent_changes.manager = ctx2[1].gitManager; if (dirty & 4) @@ -28302,7 +28554,7 @@ function create_if_block_1(ctx) { let current; stagedfilecomponent = new stagedFileComponent_default({ props: { - change: ctx[7].statusResult, + change: ctx[17].statusResult, manager: ctx[1].gitManager, view: ctx[2] } @@ -28318,7 +28570,7 @@ function create_if_block_1(ctx) { p(ctx2, dirty) { const stagedfilecomponent_changes = {}; if (dirty & 1) - stagedfilecomponent_changes.change = ctx2[7].statusResult; + stagedfilecomponent_changes.change = ctx2[17].statusResult; if (dirty & 2) stagedfilecomponent_changes.manager = ctx2[1].gitManager; if (dirty & 4) @@ -28348,7 +28600,7 @@ function create_each_block(ctx) { const if_block_creators = [create_if_block3, create_else_block]; const if_blocks = []; function select_block_type(ctx2, dirty) { - if (ctx2[7].statusResult) + if (ctx2[17].statusResult) return 0; return 1; } @@ -28420,7 +28672,7 @@ function create_fragment4(ctx) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } - attr(main, "class", "svelte-pgmdei"); + attr(main, "class", "svelte-148wteu"); toggle_class(main, "topLevel", ctx[4]); }, m(target, anchor) { @@ -28431,7 +28683,7 @@ function create_fragment4(ctx) { current = true; }, p(ctx2, [dirty]) { - if (dirty & 47) { + if (dirty & 2031) { each_value = ctx2[0].children; let i; for (i = 0; i < each_value.length; i += 1) { @@ -28479,15 +28731,44 @@ function create_fragment4(ctx) { }; } function instance4($$self, $$props, $$invalidate) { + let side; let { hierarchy } = $$props; let { plugin } = $$props; let { view } = $$props; let { fileType } = $$props; let { topLevel = false } = $$props; const closed = {}; - const click_handler = (entity) => { - $$invalidate(5, closed[entity.title] = !closed[entity.title], closed); - }; + function stage(path2) { + plugin.gitManager.stageAll({ dir: path2 }).finally(() => { + dispatchEvent(new CustomEvent("git-refresh")); + }); + } + function unstage(path2) { + plugin.gitManager.unstageAll({ dir: path2 }).finally(() => { + dispatchEvent(new CustomEvent("git-refresh")); + }); + } + function discard(item) { + new DiscardModal(view.app, false, item.vaultPath).myOpen().then((shouldDiscard) => { + if (shouldDiscard === true) { + plugin.gitManager.discardAll({ + dir: item.path, + status: plugin.cachedStatus + }).finally(() => { + dispatchEvent(new CustomEvent("git-refresh")); + }); + } + }); + } + function fold(item) { + $$invalidate(5, closed[item.title] = !closed[item.title], closed); + } + const click_handler = (entity) => fold(entity); + const click_handler_1 = (entity) => fold(entity); + const click_handler_2 = (entity) => unstage(entity.path); + const click_handler_3 = (entity) => discard(entity); + const click_handler_4 = (entity) => stage(entity.path); + const click_handler_5 = (entity) => fold(entity); $$self.$$set = ($$props2) => { if ("hierarchy" in $$props2) $$invalidate(0, hierarchy = $$props2.hierarchy); @@ -28500,7 +28781,31 @@ function instance4($$self, $$props, $$invalidate) { if ("topLevel" in $$props2) $$invalidate(4, topLevel = $$props2.topLevel); }; - return [hierarchy, plugin, view, fileType, topLevel, closed, click_handler]; + $$self.$$.update = () => { + if ($$self.$$.dirty & 4) { + $: + $$invalidate(6, side = view.leaf.getRoot().side == "left" ? "right" : "left"); + } + }; + return [ + hierarchy, + plugin, + view, + fileType, + topLevel, + closed, + side, + stage, + unstage, + discard, + fold, + click_handler, + click_handler_1, + click_handler_2, + click_handler_3, + click_handler_4, + click_handler_5 + ]; } var TreeComponent = class extends SvelteComponent { constructor(options) { @@ -28518,21 +28823,21 @@ var treeComponent_default = TreeComponent; // src/ui/sidebar/gitView.svelte function add_css5(target) { - append_styles(target, "svelte-1f0ksxd", '@charset "UTF-8";.commit-msg.svelte-1f0ksxd.svelte-1f0ksxd{width:100%;min-height:1.9em;height:1.9em;resize:vertical;padding:2px 5px;background-color:var(--background-modifier-form-field)}.search-input-container.svelte-1f0ksxd.svelte-1f0ksxd{width:100%}.file-view.svelte-1f0ksxd.svelte-1f0ksxd{margin-left:5px}.opener.svelte-1f0ksxd.svelte-1f0ksxd{display:flex;justify-content:space-between;align-items:center;padding:0 4px}.opener.svelte-1f0ksxd .collapse-icon.svelte-1f0ksxd::after{content:"\xA0"}.opener.svelte-1f0ksxd div.svelte-1f0ksxd{display:flex}.opener.svelte-1f0ksxd svg.svelte-1f0ksxd{transform:rotate(-90deg)}.opener.open.svelte-1f0ksxd svg.svelte-1f0ksxd{transform:rotate(0)}.git-view-body.svelte-1f0ksxd.svelte-1f0ksxd{overflow-y:auto;padding-left:10px}main.svelte-1f0ksxd.svelte-1f0ksxd{display:flex;flex-direction:column;height:100%;overflow-y:hidden}.nav-buttons-container.svelte-1f0ksxd.svelte-1f0ksxd{justify-content:space-between}.group.svelte-1f0ksxd.svelte-1f0ksxd{display:flex}'); + append_styles(target, "svelte-fnxzfa", `.commit-msg-input.svelte-fnxzfa.svelte-fnxzfa.svelte-fnxzfa{width:100%;overflow:hidden;resize:none;padding:7px 5px;background-color:var(--background-modifier-form-field)}.git-commit-msg.svelte-fnxzfa.svelte-fnxzfa.svelte-fnxzfa{position:relative;padding:0;width:calc(100% - var(--size-4-8));margin:4px auto}main.svelte-fnxzfa .tools.svelte-fnxzfa.svelte-fnxzfa{display:flex;margin-left:auto}main.svelte-fnxzfa .tools .buttons.svelte-fnxzfa.svelte-fnxzfa{display:flex}main.svelte-fnxzfa .tools .buttons.svelte-fnxzfa>.svelte-fnxzfa{padding:0 0;height:auto}main.svelte-fnxzfa .tools .files-count.svelte-fnxzfa.svelte-fnxzfa{padding-left:var(--size-2-1);width:11px;display:flex;align-items:center;justify-content:center}.git-commit-msg-clear-button.svelte-fnxzfa.svelte-fnxzfa.svelte-fnxzfa{position:absolute;background:transparent;border-radius:50%;color:var(--search-clear-button-color);cursor:var(--cursor);top:-4px;right:2px;bottom:0px;line-height:0;height:var(--input-height);width:28px;margin:auto;padding:0 0;text-align:center;display:flex;justify-content:center;align-items:center;transition:color 0.15s ease-in-out}.git-commit-msg-clear-button.svelte-fnxzfa.svelte-fnxzfa.svelte-fnxzfa:after{content:"";height:var(--search-clear-button-size);width:var(--search-clear-button-size);display:block;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,");-webkit-mask-repeat:no-repeat}.tree-item-flair.svelte-fnxzfa.svelte-fnxzfa.svelte-fnxzfa{margin-left:auto;align-items:center}`); } function get_each_context2(ctx, list, i) { const child_ctx = ctx.slice(); - child_ctx[34] = list[i]; + child_ctx[43] = list[i]; return child_ctx; } function get_each_context_1(ctx, list, i) { const child_ctx = ctx.slice(); - child_ctx[34] = list[i]; + child_ctx[43] = list[i]; return child_ctx; } function get_each_context_2(ctx, list, i) { const child_ctx = ctx.slice(); - child_ctx[39] = list[i]; + child_ctx[48] = list[i]; return child_ctx; } function create_if_block_8(ctx) { @@ -28543,13 +28848,13 @@ function create_if_block_8(ctx) { return { c() { div = element("div"); - attr(div, "class", "search-input-clear-button"); + attr(div, "class", "git-commit-msg-clear-button svelte-fnxzfa"); attr(div, "aria-label", div_aria_label_value = "Clear"); }, m(target, anchor) { insert(target, div, anchor); if (!mounted) { - dispose = listen(div, "click", ctx[29]); + dispose = listen(div, "click", ctx[31]); mounted = true; } }, @@ -28563,110 +28868,181 @@ function create_if_block_8(ctx) { }; } function create_if_block4(ctx) { - let div3; - let div2; - let div1; - let t2; - let span1; - let t3_value = ctx[5].staged.length + ""; - let t3; - let t4; - let t5; + let div18; + let div17; let div7; let div6; + let div0; + let t0; + let div1; + let t2; let div5; - let t8; - let span3; - let t9_value = ctx[5].changed.length + ""; + let div3; + let div2; + let t3; + let div4; + let t4_value = ctx[6].staged.length + ""; + let t4; + let t5; + let t6; + let div16; + let div15; + let div8; + let t7; + let div9; let t9; + let div14; + let div12; + let div10; let t10; + let div11; let t11; - let if_block2_anchor; + let div13; + let t12_value = ctx[6].changed.length + ""; + let t12; + let t13; + let t14; let current; let mounted; let dispose; let if_block0 = ctx[13] && create_if_block_6(ctx); let if_block1 = ctx[12] && create_if_block_42(ctx); - let if_block2 = ctx[6].length > 0 && create_if_block_12(ctx); + let if_block2 = ctx[7].length > 0 && create_if_block_12(ctx); return { c() { - div3 = element("div"); - div2 = element("div"); - div1 = element("div"); - div1.innerHTML = `
- Staged Changes`; - t2 = space(); - span1 = element("span"); - t3 = text(t3_value); - t4 = space(); - if (if_block0) - if_block0.c(); - t5 = space(); + div18 = element("div"); + div17 = element("div"); div7 = element("div"); div6 = element("div"); + div0 = element("div"); + div0.innerHTML = ``; + t0 = space(); + div1 = element("div"); + div1.textContent = "Staged Changes"; + t2 = space(); div5 = element("div"); - div5.innerHTML = `
- Changes`; - t8 = space(); - span3 = element("span"); - t9 = text(t9_value); + div3 = element("div"); + div2 = element("div"); + div2.innerHTML = ``; + t3 = space(); + div4 = element("div"); + t4 = text(t4_value); + t5 = space(); + if (if_block0) + if_block0.c(); + t6 = space(); + div16 = element("div"); + div15 = element("div"); + div8 = element("div"); + div8.innerHTML = ``; + t7 = space(); + div9 = element("div"); + div9.textContent = "Changes"; + t9 = space(); + div14 = element("div"); + div12 = element("div"); + div10 = element("div"); + div10.innerHTML = ``; t10 = space(); + div11 = element("div"); + div11.innerHTML = ``; + t11 = space(); + div13 = element("div"); + t12 = text(t12_value); + t13 = space(); if (if_block1) if_block1.c(); - t11 = space(); + t14 = space(); if (if_block2) if_block2.c(); - if_block2_anchor = empty(); - attr(div1, "class", "svelte-1f0ksxd"); - attr(span1, "class", "tree-item-flair"); - attr(div2, "class", "opener tree-item-self is-clickable svelte-1f0ksxd"); - toggle_class(div2, "open", ctx[13]); - attr(div3, "class", "staged"); - attr(div5, "class", "svelte-1f0ksxd"); - attr(span3, "class", "tree-item-flair"); - attr(div6, "class", "opener tree-item-self is-clickable svelte-1f0ksxd"); - toggle_class(div6, "open", ctx[12]); - attr(div7, "class", "changes"); + attr(div0, "class", "nav-folder-collapse-indicator collapse-icon"); + attr(div1, "class", "nav-folder-title-content"); + attr(div2, "data-icon", "minus"); + attr(div2, "aria-label", "Unstage"); + attr(div2, "class", "clickable-icon svelte-fnxzfa"); + attr(div3, "class", "buttons svelte-fnxzfa"); + attr(div4, "class", "files-count svelte-fnxzfa"); + attr(div5, "class", "tools svelte-fnxzfa"); + attr(div6, "class", "nav-folder-title"); + attr(div7, "class", "staged nav-folder"); + toggle_class(div7, "is-collapsed", !ctx[13]); + attr(div8, "class", "nav-folder-collapse-indicator collapse-icon"); + attr(div9, "class", "nav-folder-title-content"); + attr(div10, "data-icon", "undo"); + attr(div10, "aria-label", "Discard"); + attr(div10, "class", "clickable-icon svelte-fnxzfa"); + attr(div11, "data-icon", "plus"); + attr(div11, "aria-label", "Stage"); + attr(div11, "class", "clickable-icon svelte-fnxzfa"); + attr(div12, "class", "buttons svelte-fnxzfa"); + attr(div13, "class", "files-count svelte-fnxzfa"); + attr(div14, "class", "tools svelte-fnxzfa"); + attr(div15, "class", "nav-folder-title"); + attr(div16, "class", "changes nav-folder"); + toggle_class(div16, "is-collapsed", !ctx[12]); + attr(div17, "class", "nav-folder-children"); + attr(div18, "class", "nav-folder mod-root"); }, m(target, anchor) { - insert(target, div3, anchor); - append2(div3, div2); - append2(div2, div1); - append2(div2, t2); - append2(div2, span1); - append2(span1, t3); - append2(div3, t4); - if (if_block0) - if_block0.m(div3, null); - insert(target, t5, anchor); - insert(target, div7, anchor); + insert(target, div18, anchor); + append2(div18, div17); + append2(div17, div7); append2(div7, div6); + append2(div6, div0); + append2(div6, t0); + append2(div6, div1); + append2(div6, t2); append2(div6, div5); - append2(div6, t8); - append2(div6, span3); - append2(span3, t9); - append2(div7, t10); + append2(div5, div3); + append2(div3, div2); + ctx[34](div2); + append2(div5, t3); + append2(div5, div4); + append2(div4, t4); + append2(div7, t5); + if (if_block0) + if_block0.m(div7, null); + append2(div17, t6); + append2(div17, div16); + append2(div16, div15); + append2(div15, div8); + append2(div15, t7); + append2(div15, div9); + append2(div15, t9); + append2(div15, div14); + append2(div14, div12); + append2(div12, div10); + append2(div12, t10); + append2(div12, div11); + ctx[39](div11); + append2(div14, t11); + append2(div14, div13); + append2(div13, t12); + append2(div16, t13); if (if_block1) - if_block1.m(div7, null); - insert(target, t11, anchor); + if_block1.m(div16, null); + append2(div17, t14); if (if_block2) - if_block2.m(target, anchor); - insert(target, if_block2_anchor, anchor); + if_block2.m(div17, null); current = true; if (!mounted) { dispose = [ - listen(div2, "click", ctx[30]), - listen(div6, "click", ctx[31]) + listen(div0, "click", ctx[32]), + listen(div1, "click", ctx[33]), + listen(div2, "click", ctx[18]), + listen(div6, "click", self2(ctx[35])), + listen(div8, "click", ctx[36]), + listen(div9, "click", ctx[37]), + listen(div10, "click", ctx[38]), + listen(div11, "click", ctx[17]), + listen(div15, "click", self2(ctx[40])) ]; mounted = true; } }, p(ctx2, dirty) { - if ((!current || dirty[0] & 32) && t3_value !== (t3_value = ctx2[5].staged.length + "")) - set_data(t3, t3_value); - if (!current || dirty[0] & 8192) { - toggle_class(div2, "open", ctx2[13]); - } + if ((!current || dirty[0] & 64) && t4_value !== (t4_value = ctx2[6].staged.length + "")) + set_data(t4, t4_value); if (ctx2[13]) { if (if_block0) { if_block0.p(ctx2, dirty); @@ -28677,7 +29053,7 @@ function create_if_block4(ctx) { if_block0 = create_if_block_6(ctx2); if_block0.c(); transition_in(if_block0, 1); - if_block0.m(div3, null); + if_block0.m(div7, null); } } else if (if_block0) { group_outros(); @@ -28686,11 +29062,11 @@ function create_if_block4(ctx) { }); check_outros(); } - if ((!current || dirty[0] & 32) && t9_value !== (t9_value = ctx2[5].changed.length + "")) - set_data(t9, t9_value); - if (!current || dirty[0] & 4096) { - toggle_class(div6, "open", ctx2[12]); + if (!current || dirty[0] & 8192) { + toggle_class(div7, "is-collapsed", !ctx2[13]); } + if ((!current || dirty[0] & 64) && t12_value !== (t12_value = ctx2[6].changed.length + "")) + set_data(t12, t12_value); if (ctx2[12]) { if (if_block1) { if_block1.p(ctx2, dirty); @@ -28701,7 +29077,7 @@ function create_if_block4(ctx) { if_block1 = create_if_block_42(ctx2); if_block1.c(); transition_in(if_block1, 1); - if_block1.m(div7, null); + if_block1.m(div16, null); } } else if (if_block1) { group_outros(); @@ -28710,17 +29086,20 @@ function create_if_block4(ctx) { }); check_outros(); } - if (ctx2[6].length > 0) { + if (!current || dirty[0] & 4096) { + toggle_class(div16, "is-collapsed", !ctx2[12]); + } + if (ctx2[7].length > 0) { if (if_block2) { if_block2.p(ctx2, dirty); - if (dirty[0] & 64) { + if (dirty[0] & 128) { transition_in(if_block2, 1); } } else { if_block2 = create_if_block_12(ctx2); if_block2.c(); transition_in(if_block2, 1); - if_block2.m(if_block2_anchor.parentNode, if_block2_anchor); + if_block2.m(div17, null); } } else if (if_block2) { group_outros(); @@ -28746,21 +29125,15 @@ function create_if_block4(ctx) { }, d(detaching) { if (detaching) - detach(div3); + detach(div18); + ctx[34](null); if (if_block0) if_block0.d(); - if (detaching) - detach(t5); - if (detaching) - detach(div7); + ctx[39](null); if (if_block1) if_block1.d(); - if (detaching) - detach(t11); if (if_block2) - if_block2.d(detaching); - if (detaching) - detach(if_block2_anchor); + if_block2.d(); mounted = false; run_all(dispose); } @@ -28775,7 +29148,7 @@ function create_if_block_6(ctx) { const if_block_creators = [create_if_block_7, create_else_block_2]; const if_blocks = []; function select_block_type(ctx2, dirty) { - if (ctx2[2]) + if (ctx2[3]) return 0; return 1; } @@ -28785,7 +29158,7 @@ function create_if_block_6(ctx) { c() { div = element("div"); if_block.c(); - attr(div, "class", "file-view svelte-1f0ksxd"); + attr(div, "class", "nav-folder-children"); }, m(target, anchor) { insert(target, div, anchor); @@ -28848,7 +29221,7 @@ function create_if_block_6(ctx) { function create_else_block_2(ctx) { let each_1_anchor; let current; - let each_value_2 = ctx[5].staged; + let each_value_2 = ctx[6].staged; let each_blocks = []; for (let i = 0; i < each_value_2.length; i += 1) { each_blocks[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i)); @@ -28871,8 +29244,8 @@ function create_else_block_2(ctx) { current = true; }, p(ctx2, dirty) { - if (dirty[0] & 35) { - each_value_2 = ctx2[5].staged; + if (dirty[0] & 67) { + each_value_2 = ctx2[6].staged; let i; for (i = 0; i < each_value_2.length; i += 1) { const child_ctx = get_each_context_2(ctx2, each_value_2, i); @@ -28965,7 +29338,7 @@ function create_each_block_2(ctx) { let current; stagedfilecomponent = new stagedFileComponent_default({ props: { - change: ctx[39], + change: ctx[48], view: ctx[1], manager: ctx[0].gitManager } @@ -28980,8 +29353,8 @@ function create_each_block_2(ctx) { }, p(ctx2, dirty) { const stagedfilecomponent_changes = {}; - if (dirty[0] & 32) - stagedfilecomponent_changes.change = ctx2[39]; + if (dirty[0] & 64) + stagedfilecomponent_changes.change = ctx2[48]; if (dirty[0] & 2) stagedfilecomponent_changes.view = ctx2[1]; if (dirty[0] & 1) @@ -29009,10 +29382,10 @@ function create_if_block_42(ctx) { let if_block; let div_transition; let current; - const if_block_creators = [create_if_block_5, create_else_block_1]; + const if_block_creators = [create_if_block_52, create_else_block_12]; const if_blocks = []; function select_block_type_1(ctx2, dirty) { - if (ctx2[2]) + if (ctx2[3]) return 0; return 1; } @@ -29022,7 +29395,7 @@ function create_if_block_42(ctx) { c() { div = element("div"); if_block.c(); - attr(div, "class", "file-view svelte-1f0ksxd"); + attr(div, "class", "nav-folder-children"); }, m(target, anchor) { insert(target, div, anchor); @@ -29082,10 +29455,10 @@ function create_if_block_42(ctx) { } }; } -function create_else_block_1(ctx) { +function create_else_block_12(ctx) { let each_1_anchor; let current; - let each_value_1 = ctx[5].changed; + let each_value_1 = ctx[6].changed; let each_blocks = []; for (let i = 0; i < each_value_1.length; i += 1) { each_blocks[i] = create_each_block_1(get_each_context_1(ctx, each_value_1, i)); @@ -29108,8 +29481,8 @@ function create_else_block_1(ctx) { current = true; }, p(ctx2, dirty) { - if (dirty[0] & 35) { - each_value_1 = ctx2[5].changed; + if (dirty[0] & 67) { + each_value_1 = ctx2[6].changed; let i; for (i = 0; i < each_value_1.length; i += 1) { const child_ctx = get_each_context_1(ctx2, each_value_1, i); @@ -29152,7 +29525,7 @@ function create_else_block_1(ctx) { } }; } -function create_if_block_5(ctx) { +function create_if_block_52(ctx) { let treecomponent; let current; treecomponent = new treeComponent_default({ @@ -29202,7 +29575,7 @@ function create_each_block_1(ctx) { let current; filecomponent = new fileComponent_default({ props: { - change: ctx[34], + change: ctx[43], view: ctx[1], manager: ctx[0].gitManager } @@ -29218,8 +29591,8 @@ function create_each_block_1(ctx) { }, p(ctx2, dirty) { const filecomponent_changes = {}; - if (dirty[0] & 32) - filecomponent_changes.change = ctx2[34]; + if (dirty[0] & 64) + filecomponent_changes.change = ctx2[43]; if (dirty[0] & 2) filecomponent_changes.view = ctx2[1]; if (dirty[0] & 1) @@ -29244,10 +29617,12 @@ function create_each_block_1(ctx) { function create_if_block_12(ctx) { let div3; let div2; + let div0; + let t0; let div1; let t2; - let span1; - let t3_value = ctx[6].length + ""; + let span; + let t3_value = ctx[7].length + ""; let t3; let t4; let current; @@ -29258,43 +29633,45 @@ function create_if_block_12(ctx) { c() { div3 = element("div"); div2 = element("div"); + div0 = element("div"); + div0.innerHTML = ``; + t0 = space(); div1 = element("div"); - div1.innerHTML = `
- Recently Pulled Changes`; + div1.textContent = "Recently Pulled Files"; t2 = space(); - span1 = element("span"); + span = element("span"); t3 = text(t3_value); t4 = space(); if (if_block) if_block.c(); - attr(div1, "class", "svelte-1f0ksxd"); - attr(span1, "class", "tree-item-flair"); - attr(div2, "class", "opener tree-item-self is-clickable svelte-1f0ksxd"); - toggle_class(div2, "open", ctx[14]); - attr(div3, "class", "pulled"); + attr(div0, "class", "nav-folder-collapse-indicator collapse-icon"); + attr(div1, "class", "nav-folder-title-content"); + attr(span, "class", "tree-item-flair svelte-fnxzfa"); + attr(div2, "class", "nav-folder-title"); + attr(div3, "class", "pulled nav-folder"); + toggle_class(div3, "is-collapsed", !ctx[14]); }, m(target, anchor) { insert(target, div3, anchor); append2(div3, div2); + append2(div2, div0); + append2(div2, t0); append2(div2, div1); append2(div2, t2); - append2(div2, span1); - append2(span1, t3); + append2(div2, span); + append2(span, t3); append2(div3, t4); if (if_block) if_block.m(div3, null); current = true; if (!mounted) { - dispose = listen(div2, "click", ctx[32]); + dispose = listen(div2, "click", ctx[41]); mounted = true; } }, p(ctx2, dirty) { - if ((!current || dirty[0] & 64) && t3_value !== (t3_value = ctx2[6].length + "")) + if ((!current || dirty[0] & 128) && t3_value !== (t3_value = ctx2[7].length + "")) set_data(t3, t3_value); - if (!current || dirty[0] & 16384) { - toggle_class(div2, "open", ctx2[14]); - } if (ctx2[14]) { if (if_block) { if_block.p(ctx2, dirty); @@ -29314,6 +29691,9 @@ function create_if_block_12(ctx) { }); check_outros(); } + if (!current || dirty[0] & 16384) { + toggle_class(div3, "is-collapsed", !ctx2[14]); + } }, i(local) { if (current) @@ -29344,7 +29724,7 @@ function create_if_block_22(ctx) { const if_block_creators = [create_if_block_32, create_else_block2]; const if_blocks = []; function select_block_type_2(ctx2, dirty) { - if (ctx2[2]) + if (ctx2[3]) return 0; return 1; } @@ -29354,7 +29734,7 @@ function create_if_block_22(ctx) { c() { div = element("div"); if_block.c(); - attr(div, "class", "file-view svelte-1f0ksxd"); + attr(div, "class", "nav-folder-children"); }, m(target, anchor) { insert(target, div, anchor); @@ -29417,7 +29797,7 @@ function create_if_block_22(ctx) { function create_else_block2(ctx) { let each_1_anchor; let current; - let each_value = ctx[6]; + let each_value = ctx[7]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block2(get_each_context2(ctx, each_value, i)); @@ -29440,8 +29820,8 @@ function create_else_block2(ctx) { current = true; }, p(ctx2, dirty) { - if (dirty[0] & 66) { - each_value = ctx2[6]; + if (dirty[0] & 130) { + each_value = ctx2[7]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context2(ctx2, each_value, i); @@ -29534,7 +29914,7 @@ function create_each_block2(ctx) { let current; pulledfilecomponent = new pulledFileComponent_default({ props: { - change: ctx[34], + change: ctx[43], view: ctx[1] } }); @@ -29549,8 +29929,8 @@ function create_each_block2(ctx) { }, p(ctx2, dirty) { const pulledfilecomponent_changes = {}; - if (dirty[0] & 64) - pulledfilecomponent_changes.change = ctx2[34]; + if (dirty[0] & 128) + pulledfilecomponent_changes.change = ctx2[43]; if (dirty[0] & 2) pulledfilecomponent_changes.view = ctx2[1]; pulledfilecomponent.$set(pulledfilecomponent_changes); @@ -29572,8 +29952,8 @@ function create_each_block2(ctx) { } function create_fragment5(ctx) { let main; - let div9; - let div6; + let div8; + let div7; let div0; let t0; let div1; @@ -29586,9 +29966,9 @@ function create_fragment5(ctx) { let t4; let div5; let t5; - let div7; + let div6; let t6; - let div8; + let div9; let textarea; let t7; let t8; @@ -29596,13 +29976,13 @@ function create_fragment5(ctx) { let current; let mounted; let dispose; - let if_block0 = ctx[7] && create_if_block_8(ctx); - let if_block1 = ctx[5] && ctx[10] && ctx[9] && create_if_block4(ctx); + let if_block0 = ctx[2] && create_if_block_8(ctx); + let if_block1 = ctx[6] && ctx[10] && ctx[9] && create_if_block4(ctx); return { c() { main = element("main"); - div9 = element("div"); - div6 = element("div"); + div8 = element("div"); + div7 = element("div"); div0 = element("div"); t0 = space(); div1 = element("div"); @@ -29615,9 +29995,9 @@ function create_fragment5(ctx) { t4 = space(); div5 = element("div"); t5 = space(); - div7 = element("div"); + div6 = element("div"); t6 = space(); - div8 = element("div"); + div9 = element("div"); textarea = element("textarea"); t7 = space(); if (if_block0) @@ -29628,73 +30008,76 @@ function create_fragment5(ctx) { if_block1.c(); attr(div0, "id", "commit-btn"); attr(div0, "data-icon", "check"); - attr(div0, "class", "nav-action-button"); + attr(div0, "class", "clickable-icon nav-action-button"); attr(div0, "aria-label", "Commit"); attr(div1, "id", "stage-all"); - attr(div1, "class", "nav-action-button"); + attr(div1, "class", "clickable-icon nav-action-button"); attr(div1, "data-icon", "plus-circle"); attr(div1, "aria-label", "Stage all"); attr(div2, "id", "unstage-all"); - attr(div2, "class", "nav-action-button"); + attr(div2, "class", "clickable-icon nav-action-button"); attr(div2, "data-icon", "minus-circle"); attr(div2, "aria-label", "Unstage all"); attr(div3, "id", "push"); - attr(div3, "class", "nav-action-button"); + attr(div3, "class", "clickable-icon nav-action-button"); attr(div3, "data-icon", "upload"); attr(div3, "aria-label", "Push"); attr(div4, "id", "pull"); - attr(div4, "class", "nav-action-button"); + attr(div4, "class", "clickable-icon nav-action-button"); attr(div4, "data-icon", "download"); attr(div4, "aria-label", "Pull"); attr(div5, "id", "layoutChange"); - attr(div5, "class", "nav-action-button"); + attr(div5, "class", "clickable-icon nav-action-button"); attr(div5, "aria-label", "Change Layout"); - attr(div6, "class", "group svelte-1f0ksxd"); - attr(div7, "id", "refresh"); - attr(div7, "class", "nav-action-button"); - attr(div7, "data-icon", "refresh-cw"); - attr(div7, "aria-label", "Refresh"); - toggle_class(div7, "loading", ctx[4]); - attr(textarea, "class", "commit-msg svelte-1f0ksxd"); + attr(div6, "id", "refresh"); + attr(div6, "class", "clickable-icon nav-action-button"); + attr(div6, "data-icon", "refresh-cw"); + attr(div6, "aria-label", "Refresh"); + set_style(div6, "margin", "1px"); + toggle_class(div6, "loading", ctx[5]); + attr(div7, "class", "nav-buttons-container"); + attr(div8, "class", "nav-header"); + attr(textarea, "rows", ctx[15]); + attr(textarea, "class", "commit-msg-input svelte-fnxzfa"); attr(textarea, "type", "text"); attr(textarea, "spellcheck", "true"); attr(textarea, "placeholder", "Commit Message"); - attr(div8, "class", "search-input-container svelte-1f0ksxd"); - attr(div9, "class", "nav-buttons-container svelte-1f0ksxd"); - attr(div10, "class", "git-view-body svelte-1f0ksxd"); - attr(main, "class", "svelte-1f0ksxd"); + attr(div9, "class", "git-commit-msg svelte-fnxzfa"); + attr(div10, "class", "nav-files-container"); + set_style(div10, "position", "relative"); + attr(main, "class", "svelte-fnxzfa"); }, m(target, anchor) { insert(target, main, anchor); + append2(main, div8); + append2(div8, div7); + append2(div7, div0); + ctx[22](div0); + append2(div7, t0); + append2(div7, div1); + ctx[23](div1); + append2(div7, t1); + append2(div7, div2); + ctx[24](div2); + append2(div7, t2); + append2(div7, div3); + ctx[25](div3); + append2(div7, t3); + append2(div7, div4); + ctx[26](div4); + append2(div7, t4); + append2(div7, div5); + ctx[27](div5); + append2(div7, t5); + append2(div7, div6); + ctx[29](div6); + append2(main, t6); append2(main, div9); - append2(div9, div6); - append2(div6, div0); - ctx[20](div0); - append2(div6, t0); - append2(div6, div1); - ctx[21](div1); - append2(div6, t1); - append2(div6, div2); - ctx[22](div2); - append2(div6, t2); - append2(div6, div3); - ctx[23](div3); - append2(div6, t3); - append2(div6, div4); - ctx[24](div4); - append2(div6, t4); - append2(div6, div5); - ctx[25](div5); - append2(div9, t5); - append2(div9, div7); - ctx[27](div7); - append2(div9, t6); - append2(div9, div8); - append2(div8, textarea); - set_input_value(textarea, ctx[7]); - append2(div8, t7); + append2(div9, textarea); + set_input_value(textarea, ctx[2]); + append2(div9, t7); if (if_block0) - if_block0.m(div8, null); + if_block0.m(div9, null); append2(main, t8); append2(main, div10); if (if_block1) @@ -29702,41 +30085,44 @@ function create_fragment5(ctx) { current = true; if (!mounted) { dispose = [ - listen(div0, "click", ctx[15]), - listen(div1, "click", ctx[16]), - listen(div2, "click", ctx[17]), - listen(div3, "click", ctx[18]), - listen(div4, "click", ctx[19]), - listen(div5, "click", ctx[26]), - listen(div7, "click", triggerRefresh), - listen(textarea, "input", ctx[28]) + listen(div0, "click", ctx[16]), + listen(div1, "click", ctx[17]), + listen(div2, "click", ctx[18]), + listen(div3, "click", ctx[19]), + listen(div4, "click", ctx[20]), + listen(div5, "click", ctx[28]), + listen(div6, "click", triggerRefresh), + listen(textarea, "input", ctx[30]) ]; mounted = true; } }, p(ctx2, dirty) { - if (!current || dirty[0] & 16) { - toggle_class(div7, "loading", ctx2[4]); + if (!current || dirty[0] & 32) { + toggle_class(div6, "loading", ctx2[5]); } - if (dirty[0] & 128) { - set_input_value(textarea, ctx2[7]); + if (!current || dirty[0] & 32768) { + attr(textarea, "rows", ctx2[15]); } - if (ctx2[7]) { + if (dirty[0] & 4) { + set_input_value(textarea, ctx2[2]); + } + if (ctx2[2]) { if (if_block0) { if_block0.p(ctx2, dirty); } else { if_block0 = create_if_block_8(ctx2); if_block0.c(); - if_block0.m(div8, null); + if_block0.m(div9, null); } } else if (if_block0) { if_block0.d(1); if_block0 = null; } - if (ctx2[5] && ctx2[10] && ctx2[9]) { + if (ctx2[6] && ctx2[10] && ctx2[9]) { if (if_block1) { if_block1.p(ctx2, dirty); - if (dirty[0] & 1568) { + if (dirty[0] & 1600) { transition_in(if_block1, 1); } } else { @@ -29766,13 +30152,13 @@ function create_fragment5(ctx) { d(detaching) { if (detaching) detach(main); - ctx[20](null); - ctx[21](null); ctx[22](null); ctx[23](null); ctx[24](null); ctx[25](null); + ctx[26](null); ctx[27](null); + ctx[29](null); if (if_block0) if_block0.d(); if (if_block1) @@ -29786,6 +30172,7 @@ function triggerRefresh() { dispatchEvent(new CustomEvent("git-refresh")); } function instance5($$self, $$props, $$invalidate) { + let rows; let { plugin } = $$props; let { view } = $$props; let loading; @@ -29812,7 +30199,7 @@ function instance5($$self, $$props, $$invalidate) { removeEventListener("git-view-refresh", refresh); }); async function commit2() { - $$invalidate(4, loading = true); + $$invalidate(5, loading = true); if (status2) { if (await plugin.hasTooBigFiles(status2.staged)) { plugin.setState(PluginState.idle); @@ -29820,37 +30207,48 @@ function instance5($$self, $$props, $$invalidate) { } plugin.gitManager.commit(commitMessage).then(() => { if (commitMessage !== plugin.settings.commitMessage) { - $$invalidate(7, commitMessage = ""); + $$invalidate(2, commitMessage = ""); } }).finally(triggerRefresh); } } async function refresh() { if (!plugin.gitReady) { - $$invalidate(5, status2 = void 0); + $$invalidate(6, status2 = void 0); return; } - $$invalidate(5, status2 = plugin.cachedStatus); + $$invalidate(6, status2 = plugin.cachedStatus); if (plugin.lastPulledFiles && plugin.lastPulledFiles != lastPulledFiles) { - $$invalidate(6, lastPulledFiles = plugin.lastPulledFiles); + $$invalidate(7, lastPulledFiles = plugin.lastPulledFiles); $$invalidate(11, lastPulledFilesHierarchy = { title: "", + path: "", + vaultPath: "", children: plugin.gitManager.getTreeStructure(lastPulledFiles) }); } if (status2) { + const sort = (a, b) => { + return a.vault_path.split("/").last().localeCompare(b.vault_path.split("/").last()); + }; + status2.changed.sort(sort); + status2.staged.sort(sort); if (status2.changed.length + status2.staged.length > 500) { - $$invalidate(5, status2 = void 0); + $$invalidate(6, status2 = void 0); if (!plugin.loading) { plugin.displayError("Too many changes to display"); } } else { $$invalidate(9, changeHierarchy = { title: "", + path: "", + vaultPath: "", children: plugin.gitManager.getTreeStructure(status2.changed) }); $$invalidate(10, stagedHierarchy = { title: "", + path: "", + vaultPath: "", children: plugin.gitManager.getTreeStructure(status2.staged) }); } @@ -29858,24 +30256,33 @@ function instance5($$self, $$props, $$invalidate) { $$invalidate(9, changeHierarchy = void 0); $$invalidate(10, stagedHierarchy = void 0); } - $$invalidate(4, loading = plugin.loading); + $$invalidate(5, loading = plugin.loading); } function stageAll() { - $$invalidate(4, loading = true); + $$invalidate(5, loading = true); plugin.gitManager.stageAll({ status: status2 }).finally(triggerRefresh); } function unstageAll() { - $$invalidate(4, loading = true); + $$invalidate(5, loading = true); plugin.gitManager.unstageAll({ status: status2 }).finally(triggerRefresh); } function push2() { - $$invalidate(4, loading = true); + $$invalidate(5, loading = true); plugin.push().finally(triggerRefresh); } function pull2() { - $$invalidate(4, loading = true); + $$invalidate(5, loading = true); plugin.pullChangesFromRemote().finally(triggerRefresh); } + function discard() { + new DiscardModal(view.app, false, plugin.gitManager.getVaultPath("/")).myOpen().then((shouldDiscard) => { + if (shouldDiscard === true) { + plugin.gitManager.discardAll({ status: plugin.cachedStatus }).finally(() => { + dispatchEvent(new CustomEvent("git-refresh")); + }); + } + }); + } function div0_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { buttons[0] = $$value; @@ -29909,15 +30316,15 @@ function instance5($$self, $$props, $$invalidate) { function div5_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { layoutBtn = $$value; - $$invalidate(3, layoutBtn); + $$invalidate(4, layoutBtn); }); } const click_handler = () => { - $$invalidate(2, showTree = !showTree); + $$invalidate(3, showTree = !showTree); $$invalidate(0, plugin.settings.treeStructure = showTree, plugin); plugin.saveSettings(); }; - function div7_binding($$value) { + function div6_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { buttons[6] = $$value; $$invalidate(8, buttons); @@ -29925,12 +30332,29 @@ function instance5($$self, $$props, $$invalidate) { } function textarea_input_handler() { commitMessage = this.value; - $$invalidate(7, commitMessage); + $$invalidate(2, commitMessage); } - const click_handler_1 = () => $$invalidate(7, commitMessage = ""); + const click_handler_1 = () => $$invalidate(2, commitMessage = ""); const click_handler_2 = () => $$invalidate(13, stagedOpen = !stagedOpen); - const click_handler_3 = () => $$invalidate(12, changesOpen = !changesOpen); - const click_handler_4 = () => $$invalidate(14, lastPulledFilesOpen = !lastPulledFilesOpen); + const click_handler_3 = () => $$invalidate(13, stagedOpen = !stagedOpen); + function div2_binding_1($$value) { + binding_callbacks[$$value ? "unshift" : "push"](() => { + buttons[8] = $$value; + $$invalidate(8, buttons); + }); + } + const click_handler_4 = () => $$invalidate(13, stagedOpen = !stagedOpen); + const click_handler_5 = () => $$invalidate(12, changesOpen = !changesOpen); + const click_handler_6 = () => $$invalidate(12, changesOpen = !changesOpen); + const click_handler_7 = () => discard(); + function div11_binding($$value) { + binding_callbacks[$$value ? "unshift" : "push"](() => { + buttons[9] = $$value; + $$invalidate(8, buttons); + }); + } + const click_handler_8 = () => $$invalidate(12, changesOpen = !changesOpen); + const click_handler_9 = () => $$invalidate(14, lastPulledFilesOpen = !lastPulledFilesOpen); $$self.$$set = ($$props2) => { if ("plugin" in $$props2) $$invalidate(0, plugin = $$props2.plugin); @@ -29938,7 +30362,7 @@ function instance5($$self, $$props, $$invalidate) { $$invalidate(1, view = $$props2.view); }; $$self.$$.update = () => { - if ($$self.$$.dirty[0] & 12) { + if ($$self.$$.dirty[0] & 24) { $: { if (layoutBtn) { layoutBtn.empty(); @@ -29946,16 +30370,20 @@ function instance5($$self, $$props, $$invalidate) { } } } + if ($$self.$$.dirty[0] & 4) { + $: + $$invalidate(15, rows = (commitMessage.match(/\n/g) || []).length + 1 || 1); + } }; return [ plugin, view, + commitMessage, showTree, layoutBtn, loading, status2, lastPulledFiles, - commitMessage, buttons, changeHierarchy, stagedHierarchy, @@ -29963,11 +30391,13 @@ function instance5($$self, $$props, $$invalidate) { changesOpen, stagedOpen, lastPulledFilesOpen, + rows, commit2, stageAll, unstageAll, push2, pull2, + discard, div0_binding, div1_binding, div2_binding, @@ -29975,12 +30405,19 @@ function instance5($$self, $$props, $$invalidate) { div4_binding, div5_binding, click_handler, - div7_binding, + div6_binding, textarea_input_handler, click_handler_1, click_handler_2, click_handler_3, - click_handler_4 + div2_binding_1, + click_handler_4, + click_handler_5, + click_handler_6, + click_handler_7, + div11_binding, + click_handler_8, + click_handler_9 ]; } var GitView = class extends SvelteComponent { @@ -30104,11 +30541,15 @@ var ObsidianGit = class extends import_obsidian23.Plugin { id: "edit-gitignore", name: "Edit .gitignore", callback: async () => { - const content = await this.app.vault.adapter.read(this.gitManager.getVaultPath(".gitignore")); + const path2 = this.gitManager.getVaultPath(".gitignore"); + if (!await this.app.vault.adapter.exists(path2)) { + this.app.vault.adapter.write(path2, ""); + } + const content = await this.app.vault.adapter.read(path2); const modal = new IgnoreModal(this.app, content); const res = await modal.open(); if (res !== void 0) { - await this.app.vault.adapter.write(this.gitManager.getVaultPath(".gitignore"), res); + await this.app.vault.adapter.write(path2, res); this.refresh(); } } @@ -30118,12 +30559,16 @@ var ObsidianGit = class extends import_obsidian23.Plugin { name: "Open source control view", callback: async () => { const leafs = this.app.workspace.getLeavesOfType(GIT_VIEW_CONFIG.type); + let leaf; if (leafs.length === 0) { - await this.app.workspace.getRightLeaf(false).setViewState({ + leaf = this.app.workspace.getRightLeaf(false); + await leaf.setViewState({ type: GIT_VIEW_CONFIG.type }); + } else { + leaf = leafs.first(); } - this.app.workspace.revealLeaf(leafs.first()); + this.app.workspace.revealLeaf(leaf); dispatchEvent(new CustomEvent("git-refresh")); } }); diff --git a/.obsidian/plugins/obsidian-git/manifest.json b/.obsidian/plugins/obsidian-git/manifest.json index a9c6f9f..949c98e 100644 --- a/.obsidian/plugins/obsidian-git/manifest.json +++ b/.obsidian/plugins/obsidian-git/manifest.json @@ -4,5 +4,5 @@ "description": "Backup your vault with Git.", "isDesktopOnly": false, "js": "main.js", - "version": "2.4.1" + "version": "2.8.0" } diff --git a/.obsidian/plugins/obsidian-git/styles.css b/.obsidian/plugins/obsidian-git/styles.css index ee88d4c..e496c17 100644 --- a/.obsidian/plugins/obsidian-git/styles.css +++ b/.obsidian/plugins/obsidian-git/styles.css @@ -1,469 +1,474 @@ @keyframes loading { - 0% { - transform: rotate(0deg); - } + 0% { + transform: rotate(0deg); + } - 100% { - transform: rotate(360deg); - } + 100% { + transform: rotate(360deg); + } +} + +.workspace-leaf-content[data-type='git-view'] .view-content { + padding: 0; } .loading>svg { - animation: 2s linear infinite loading; - transform-origin: 50% 50%; - display: inline-block; + animation: 2s linear infinite loading; + transform-origin: 50% 50%; + display: inline-block; } .obsidian-git-center { - margin: auto; - width: 50%; + margin: auto; + text-align: center; + width: 50%; } .obsidian-git-textarea { - display: block; - margin-left: auto; - margin-right: auto; + display: block; + margin-left: auto; + margin-right: auto; } .obsidian-git-center-button { - display: block; - margin: 20px auto; + display: block; + margin: 20px auto; } .tooltip.mod-left { - overflow-wrap: break-word; + overflow-wrap: break-word; } .tooltip.mod-right { - overflow-wrap: break-word; + overflow-wrap: break-word; } .obsidian-git-shortcuts { - margin: 10px; + margin: 10px; } .diff-err { - height: 100%; - display: flex; - justify-content: center; - flex-direction: column; - align-items: center; + height: 100%; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; } .diff-err-sign { - font-size: 2em; + font-size: 2em; } .workspace-leaf-content[data-type="diff-view"] .d2h-d-none { - display: none; + display: none; } .workspace-leaf-content[data-type="diff-view"] .d2h-wrapper { - text-align: left; + text-align: left; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-header { - background-color: var(--background-primary); - border-bottom: 1px solid var(--interactive-accent); - font-family: var(--font-monospace); - height: 35px; - padding: 5px 10px; + background-color: var(--background-primary); + border-bottom: 1px solid var(--interactive-accent); + font-family: var(--font-monospace); + height: 35px; + padding: 5px 10px; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-header, .workspace-leaf-content[data-type="diff-view"] .d2h-file-stats { - display: -webkit-box; - display: -ms-flexbox; - display: flex; + display: -webkit-box; + display: -ms-flexbox; + display: flex; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-stats { - font-size: 14px; - margin-left: auto; + font-size: 14px; + margin-left: auto; } .workspace-leaf-content[data-type="diff-view"] .d2h-lines-added { - border: 1px solid #b4e2b4; - border-radius: 5px 0 0 5px; - color: #399839; - padding: 2px; - text-align: right; - vertical-align: middle; + border: 1px solid #b4e2b4; + border-radius: 5px 0 0 5px; + color: #399839; + padding: 2px; + text-align: right; + vertical-align: middle; } .workspace-leaf-content[data-type="diff-view"] .d2h-lines-deleted { - border: 1px solid #e9aeae; - border-radius: 0 5px 5px 0; - color: #c33; - margin-left: 1px; - padding: 2px; - text-align: left; - vertical-align: middle; + border: 1px solid #e9aeae; + border-radius: 0 5px 5px 0; + color: #c33; + margin-left: 1px; + padding: 2px; + text-align: left; + vertical-align: middle; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-name-wrapper { - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - font-size: 15px; - width: 100%; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + font-size: 15px; + width: 100%; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-name { - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-wrapper { - border: 1px solid var(--background-modifier-border); - border-radius: 3px; - margin-bottom: 1em; + border: 1px solid var(--background-modifier-border); + border-radius: 3px; + margin-bottom: 1em; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse { - -webkit-box-pack: end; - -ms-flex-pack: end; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - border: 1px solid var(--background-modifier-border); - border-radius: 3px; - cursor: pointer; - display: none; - font-size: 12px; - justify-content: flex-end; - padding: 4px 8px; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + border: 1px solid var(--background-modifier-border); + border-radius: 3px; + cursor: pointer; + display: none; + font-size: 12px; + justify-content: flex-end; + padding: 4px 8px; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse.d2h-selected { - background-color: #c8e1ff; + background-color: #c8e1ff; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse-input { - margin: 0 4px 0 0; + margin: 0 4px 0 0; } .workspace-leaf-content[data-type="diff-view"] .d2h-diff-table { - border-collapse: collapse; - font-family: Menlo, Consolas, monospace; - font-size: 13px; - width: 100%; + border-collapse: collapse; + font-family: Menlo, Consolas, monospace; + font-size: 13px; + width: 100%; } .workspace-leaf-content[data-type="diff-view"] .d2h-files-diff { - width: 100%; + width: 100%; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-diff { - overflow-y: hidden; + overflow-y: hidden; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-side-diff { - display: inline-block; - margin-bottom: -8px; - margin-right: -4px; - overflow-x: scroll; - overflow-y: hidden; - width: 50%; + display: inline-block; + margin-bottom: -8px; + margin-right: -4px; + overflow-x: scroll; + overflow-y: hidden; + width: 50%; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-line { - padding: 0 8em; + padding: 0 8em; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-line, .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line { - display: inline-block; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - white-space: nowrap; - width: 100%; + display: inline-block; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + white-space: nowrap; + width: 100%; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line { - padding: 0 4.5em; + padding: 0 4.5em; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-line-ctn { - word-wrap: normal; - background: none; - display: inline-block; - padding: 0; - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; - vertical-align: middle; - white-space: pre; - width: 100%; + word-wrap: normal; + background: none; + display: inline-block; + padding: 0; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + vertical-align: middle; + white-space: pre; + width: 100%; } .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del, .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line del { - background-color: #ffb6ba; + background-color: #ffb6ba; } .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del, .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line del { - background-color: #8d232881; + background-color: #8d232881; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del, .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins, .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line del, .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line ins { - border-radius: 0.2em; - display: inline-block; - margin-top: -1px; - text-decoration: none; - vertical-align: middle; + border-radius: 0.2em; + display: inline-block; + margin-top: -1px; + text-decoration: none; + vertical-align: middle; } .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins, .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line ins { - background-color: #97f295; - text-align: left; + background-color: #97f295; + text-align: left; } .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins, .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line ins { - background-color: #1d921996; - text-align: left; + background-color: #1d921996; + text-align: left; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-line-prefix { - word-wrap: normal; - background: none; - display: inline; - padding: 0; - white-space: pre; + word-wrap: normal; + background: none; + display: inline; + padding: 0; + white-space: pre; } .workspace-leaf-content[data-type="diff-view"] .line-num1 { - float: left; + float: left; } .workspace-leaf-content[data-type="diff-view"] .line-num1, .workspace-leaf-content[data-type="diff-view"] .line-num2 { - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - padding: 0 0.5em; - text-overflow: ellipsis; - width: 3.5em; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + padding: 0 0.5em; + text-overflow: ellipsis; + width: 3.5em; } .workspace-leaf-content[data-type="diff-view"] .line-num2 { - float: right; + float: right; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber { - background-color: var(--background-primary); - border: solid var(--background-modifier-border); - border-width: 0 1px; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: var(--text-muted); - cursor: pointer; - display: inline-block; - position: absolute; - text-align: right; - width: 7.5em; + background-color: var(--background-primary); + border: solid var(--background-modifier-border); + border-width: 0 1px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: var(--text-muted); + cursor: pointer; + display: inline-block; + position: absolute; + text-align: right; + width: 7.5em; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber:after { - content: "\200b"; + content: "\200b"; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber { - background-color: var(--background-primary); - border: solid var(--background-modifier-border); - border-width: 0 1px; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: var(--text-muted); - cursor: pointer; - display: inline-block; - overflow: hidden; - padding: 0 0.5em; - position: absolute; - text-align: right; - text-overflow: ellipsis; - width: 4em; + background-color: var(--background-primary); + border: solid var(--background-modifier-border); + border-width: 0 1px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: var(--text-muted); + cursor: pointer; + display: inline-block; + overflow: hidden; + padding: 0 0.5em; + position: absolute; + text-align: right; + text-overflow: ellipsis; + width: 4em; } .workspace-leaf-content[data-type="diff-view"] .d2h-diff-tbody tr { - position: relative; + position: relative; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber:after { - content: "\200b"; + content: "\200b"; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-emptyplaceholder, .workspace-leaf-content[data-type="diff-view"] .d2h-emptyplaceholder { - background-color: var(--background-primary); - border-color: var(--background-modifier-border); + background-color: var(--background-primary); + border-color: var(--background-modifier-border); } .workspace-leaf-content[data-type="diff-view"] .d2h-code-line-prefix, .workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber, .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber, .workspace-leaf-content[data-type="diff-view"] .d2h-emptyplaceholder { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber, .workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber { - direction: rtl; + direction: rtl; } .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-del { - background-color: #fee8e9; - border-color: #e9aeae; + background-color: #fee8e9; + border-color: #e9aeae; } .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-ins { - background-color: #dfd; - border-color: #b4e2b4; + background-color: #dfd; + border-color: #b4e2b4; } .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-del { - background-color: #521b1d83; - border-color: #691d1d73; + background-color: #521b1d83; + border-color: #691d1d73; } .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-ins { - background-color: rgba(30, 71, 30, 0.5); - border-color: #13501381; + background-color: rgba(30, 71, 30, 0.5); + border-color: #13501381; } .workspace-leaf-content[data-type="diff-view"] .d2h-info { - background-color: var(--background-primary); - border-color: var(--background-modifier-border); - color: var(--text-normal); + background-color: var(--background-primary); + border-color: var(--background-modifier-border); + color: var(--text-normal); } .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-file-diff .d2h-del.d2h-change { - background-color: #fdf2d0; + background-color: #fdf2d0; } .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-file-diff .d2h-del.d2h-change { - background-color: #55492480; + background-color: #55492480; } .theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-file-diff .d2h-ins.d2h-change { - background-color: #ded; + background-color: #ded; } .theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-file-diff .d2h-ins.d2h-change { - background-color: rgba(37, 78, 37, 0.418); + background-color: rgba(37, 78, 37, 0.418); } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper { - margin-bottom: 10px; + margin-bottom: 10px; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper a { - color: #3572b0; - text-decoration: none; + color: #3572b0; + text-decoration: none; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper a:visited { - color: #3572b0; + color: #3572b0; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list-header { - text-align: left; + text-align: left; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list-title { - font-weight: 700; + font-weight: 700; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list-line { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - text-align: left; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + text-align: left; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list { - display: block; - list-style: none; - margin: 0; - padding: 0; + display: block; + list-style: none; + margin: 0; + padding: 0; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list>li { - border-bottom: 1px solid var(--background-modifier-border); - margin: 0; - padding: 5px 10px; + border-bottom: 1px solid var(--background-modifier-border); + margin: 0; + padding: 5px 10px; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-list>li:last-child { - border-bottom: none; + border-bottom: none; } .workspace-leaf-content[data-type="diff-view"] .d2h-file-switch { - cursor: pointer; - display: none; - font-size: 10px; + cursor: pointer; + display: none; + font-size: 10px; } .workspace-leaf-content[data-type="diff-view"] .d2h-icon { - fill: currentColor; - margin-right: 10px; - vertical-align: middle; + fill: currentColor; + margin-right: 10px; + vertical-align: middle; } .workspace-leaf-content[data-type="diff-view"] .d2h-deleted { - color: #c33; + color: #c33; } .workspace-leaf-content[data-type="diff-view"] .d2h-added { - color: #399839; + color: #399839; } .workspace-leaf-content[data-type="diff-view"] .d2h-changed { - color: #d0b44c; + color: #d0b44c; } .workspace-leaf-content[data-type="diff-view"] .d2h-moved { - color: #3572b0; + color: #3572b0; } .workspace-leaf-content[data-type="diff-view"] .d2h-tag { - background-color: var(--background-primary); - display: -webkit-box; - display: -ms-flexbox; - display: flex; - font-size: 10px; - margin-left: 5px; - padding: 0 2px; + background-color: var(--background-primary); + display: -webkit-box; + display: -ms-flexbox; + display: flex; + font-size: 10px; + margin-left: 5px; + padding: 0 2px; } .workspace-leaf-content[data-type="diff-view"] .d2h-deleted-tag { - border: 2px solid #c33; + border: 2px solid #c33; } .workspace-leaf-content[data-type="diff-view"] .d2h-added-tag { - border: 1px solid #399839; + border: 1px solid #399839; } .workspace-leaf-content[data-type="diff-view"] .d2h-changed-tag { - border: 1px solid #d0b44c; + border: 1px solid #d0b44c; } .workspace-leaf-content[data-type="diff-view"] .d2h-moved-tag { - border: 1px solid #3572b0; + border: 1px solid #3572b0; } \ No newline at end of file diff --git a/.obsidian/snippets/obsidian.css b/.obsidian/snippets/obsidian.css index 124e005..8adecf5 100644 --- a/.obsidian/snippets/obsidian.css +++ b/.obsidian/snippets/obsidian.css @@ -194,7 +194,7 @@ a.external-link { } .nav-folder-title::before { - content: '💼'; + content: '📁'; margin-right: 0.3em; } @@ -207,8 +207,7 @@ a.external-link { } .nav-file-title::before { - /*content: '📄';*/ - content: '\E801'; + content: '📄'; margin-right: 0.3em; } diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 050c3bf..a4a43bd 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,24 +13,50 @@ "state": { "type": "markdown", "state": { - "file": "00. Inbox/00. Inbox/猴子都會的AI繪圖軟體安裝教學.md", + "file": "05. 資料收集/Tool Setup/Software/Windows 10 Setup.md", "mode": "source", "source": true } } }, { - "id": "67d6babe83bb7f0b", + "id": "d6d91c541006ee95", "type": "leaf", "state": { - "type": "kanban", + "type": "markdown", "state": { - "file": "00. Inbox/02. Kanban TODO - Me.md" + "file": "00. Inbox/01. TODO.md", + "mode": "source", + "source": true + } + } + }, + { + "id": "1b1f377d09410f18", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "01. 個人/01. Daily/2021/01/2021-01-17(Sun).md", + "mode": "preview", + "source": true + } + } + }, + { + "id": "7457240a4e7a5f8a", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "01. 個人/01. Daily/2022-10-18(週二).md", + "mode": "source", + "source": true } } } ], - "currentTab": 1 + "currentTab": 3 } ], "direction": "vertical" @@ -88,7 +114,7 @@ "state": { "type": "backlink", "state": { - "file": "00. Inbox/02. Kanban TODO - Me.md", + "file": "01. 個人/01. Daily/2022-10-18(週二).md", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -105,7 +131,7 @@ "state": { "type": "outline", "state": { - "file": "00. Inbox/02. Kanban TODO - Me.md" + "file": "01. 個人/01. Daily/2022-10-18(週二).md" } } } @@ -131,22 +157,22 @@ } ], "direction": "horizontal", - "width": 243 + "width": 319.5 }, "ribbon": { "mostRecentAction": "" }, - "active": "67d6babe83bb7f0b", + "active": "7457240a4e7a5f8a", "lastOpenFiles": [ - "00. Inbox/02. Kanban TODO - Logitech.md", - "00. Inbox/02. Kanban TODO - Me.md", - "04. Programming/演算法.md", - "00. Inbox/00. Inbox/猴子都會的AI繪圖軟體安裝教學.md", - "04. Programming/FFMPEG/FFMpeg.md", - "04. Programming/FFMPEG/01. Setup.md", - "04. Programming/FFMPEG/00. Introduction.md", - "00. Inbox/01. TODO.md", - "03. 專注Study/C++/lambda.md", - "04. Programming/OpenCV.md" + "00. Inbox/00. Inbox/Coding style.md", + "01. 個人/01. Daily/2021/01/2021-01-17(Sun).md", + "03. 專注Study/Android/Android programming.md", + "01. 個人/01. Daily/2021/05/2021-05-22(週六).md", + "01. 個人/01. Daily/2019/2019-09-12(週四).md", + "01. 個人/01. Daily/2020/05/2020-05-16(週六).md", + "01. 個人/01. Daily/2021/09/2021-09-02(週四).md", + "01. 個人/01. Daily/2020/05/2020-05-19(週二).md", + "01. 個人/01. Daily/2021/09/2021-09-09(週四).md", + "02. 工作/01. Logitech/TestCam.md" ] } \ No newline at end of file diff --git a/00. Inbox/00. Inbox/Coding style.md b/00. Inbox/00. Inbox/Coding style.md new file mode 100644 index 0000000..08b60a4 --- /dev/null +++ b/00. Inbox/00. Inbox/Coding style.md @@ -0,0 +1,8 @@ +## Common Rule +### LowerCamel case +Variables and functions start with lower camel case. e.g. +```cpp +int myName = 0; +void doSomething(int argNum1); +``` + diff --git a/01. 個人/01. Daily/2022-10-17(週一).md b/01. 個人/01. Daily/2022-10-17(週一).md new file mode 100644 index 0000000..8a023e2 --- /dev/null +++ b/01. 個人/01. Daily/2022-10-17(週一).md @@ -0,0 +1,61 @@ +時間:20:13:42 + +### TAG + + +### All TODOs +```tasks +not done +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` + +--- + +### Doing TODOs +```tasks +not done +has start date +happens before tomorrow +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` + +--- + +### 新增TODO +#### 私事 + +#### 公事 + +### 今日回顧 +#職場 +今天終於聽到台灣的裁員消息,是CP&G那邊的人,也挺資深的,少徐大幾年而已。依照徐大的說法,他跟錯老闆與做事沒有價值。進一步詢問,他做的是FPGA的工作,也就是用FPGA來驗證鍵盤滑鼠,我說這看起來不像是沒有價值的事情阿,徐大說有價值,但是現階段沒有必須性。恩,好吧。總之,他可以在一個月內找其他部門依靠,不然就只能離職了。 + +不得不抱怨一下和徐大講話真的是有點累,徐大在看訊息好像都看最後一行似的,永遠都只會回最後一行的問題,其他問題都會被忽略。要是多個問題就要一個一個問,速度就會變得很慢。另外,徐大野很喜歡先說一聲Hi,然後等你回應再說他的需求,這變成你沒法在第一時間去處理事情,又多了一見懸宕的事,而且可能在你回應之後,他也無法馬上回應,一來一往就耗掉很多時間。 + +#自律 +今天上班不算很認真,一直拖到下午才開始寫FFMPEG的encode class,明天要正常一點。 + +律定一下自己,把以下這些事習慣化: +- 讀一篇summit +- 背單字 +- 看書 +- 寫日記 +先四個就好! \ No newline at end of file diff --git a/01. 個人/01. Daily/2022-10-18(週二).md b/01. 個人/01. Daily/2022-10-18(週二).md new file mode 100644 index 0000000..ccb0a73 --- /dev/null +++ b/01. 個人/01. Daily/2022-10-18(週二).md @@ -0,0 +1,58 @@ +時間:21:38:53 + +### TAG + + +### All TODOs +```tasks +not done +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` + +--- + +### Doing TODOs +```tasks +not done +has start date +happens before tomorrow +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` + +--- + +### 新增TODO +#### 私事 + +#### 公事 + +### 今日回顧 +睿睿忘記帶聯絡簿去學校。 + +下午跟徐大開會,他要把CameraView的一部分code抽出去變成library。這部份在工程上是很合理的。但我的心裡上卻隱約有點不太舒服,說不上來為什麼。 +想一想,或許是因為......我覺得......我沒有受重視的感覺吧。雖然徐大會口頭上說:你精心刻的東西怎麼樣阿,這樣的好東西我們應該讓它可以被看到阿,之類的。 +但我總覺的徐大會有意無意的忽略我做的東西,也許是還沒發現其用處,不知道該怎麼用它處理他吧,我不知道,就是一種感覺,不是很正面的。 +另外一點還是關於部門的定位讓我很沒有安全感吧,應該說,我對於我在Karthik面前沒有曝光度這點讓我感到沒有安全感,我不覺的Karthik把我看作他的人馬,我目前只是依附在徐大之下而已。是不是應該大膽一點做些什麼來增加曝光度呢?會不會引起反效果呢? #職場 + +媽媽打電話來問罷罷的保單,爸爸手部的脂肪瘤要開刀,有比較有效的自費凝膠可以用,所以醫生問說爸爸是不是有實支實付的保單可以請領,很可惜爸爸沒有保這個。 +明天要記得問一下爸爸的開刀時間。 #家庭 + +這兩天都有寫日記,但是都沒有聽summit,明天開始要聽summit,然後背單字。 #自律 \ No newline at end of file diff --git a/04. Programming/Python/Poetry.md b/04. Programming/Python/Poetry.md new file mode 100644 index 0000000..d550583 --- /dev/null +++ b/04. Programming/Python/Poetry.md @@ -0,0 +1,1030 @@ +來源:[再見了 pip!最佳 Python 套件管理器——Poetry 完全入門指南](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8) + +--- + +# 再見了 pip!最佳 Python 套件管理器——Poetry 完全入門指南 + + +[![by Feifei Ruan on Behance](https://i.imgur.com/3QW32TX.jpg)] + +前陣子工作上的專案從原先的 pip 改用 Poetry 管理 Python 套件,由於採用 Poetry 正是我的提議,所以得身先士卒,研究 Poetry 使用上的重點與學習成本,並評估是否真有所值——講白了就是至少要利大於弊,不然會徒增團隊適應上的負擔。 + +拜這個機會所賜,我對 Poetry 總算有了一個較為全面的理解。 + +習慣後,現在我所有的個人開發也都改用 Poetry 來管理套件及虛擬環境,對於 Poetry 這個略嫌複雜的工具(相較 pip),上手的同時我也感受到它確實存在一些**學習門檻**,間接促使了本文的誕生。 + +### 本文定位:獻給 Poetry 新手的使用說明書 + +有鑑於 Poetry 真的有點複雜,如果要推薦別人使用,我想還是有必要好好介紹一下。換句話說,這會是一篇**完整的入門教學**。 + +本文除了講解如何使用 Poetry,還會先**不厭其煩地闡述它所解決的痛點**,如果對此興趣有限,可以直接跳到「[從零開始使用 Poetry](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B%E4%BD%BF%E7%94%A8-Poetry)」章節,但看完前導部分,相信能更加體會 Poetry 的**必要性與強大**之處。 + +為了讓你**無痛上手**,這將會是一篇超過 8000 字的長文,還請多多擔待。🙏 + +### 本文目錄 + +方便快速跳轉到有興趣的部分,桌面版用戶可和右下角的「回到最上方」搭配使用: + +1. [Poetry 是什麼?](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#Poetry-%E6%98%AF%E4%BB%80%E9%BA%BC%EF%BC%9F) +2. [名詞解釋:虛擬環境管理、套件管理、相依性管理](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%90%8D%E8%A9%9E%E8%A7%A3%E9%87%8B%EF%BC%9A%E8%99%9B%E6%93%AC%E7%92%B0%E5%A2%83%E7%AE%A1%E7%90%86%E3%80%81%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E3%80%81%E7%9B%B8%E4%BE%9D%E6%80%A7%E7%AE%A1%E7%90%86) +3. [pip 的最大不足](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#pip-%E7%9A%84%E6%9C%80%E5%A4%A7%E4%B8%8D%E8%B6%B3) +4. [pip 替代方案選擇——Pipenv vs Poetry](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#pip-%E6%9B%BF%E4%BB%A3%E6%96%B9%E6%A1%88%E9%81%B8%E6%93%87%E2%80%94%E2%80%94Pipenv-vs-Poetry) +5. [選擇 Poetry 的兩個理由](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E9%81%B8%E6%93%87-Poetry-%E7%9A%84%E5%85%A9%E5%80%8B%E7%90%86%E7%94%B1) +6. [從零開始使用 Poetry](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B%E4%BD%BF%E7%94%A8-Poetry) +7. [安裝 Poetry](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%AE%89%E8%A3%9D-Poetry) +8. [初始化 Poetry 專案](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%88%9D%E5%A7%8B%E5%8C%96-Poetry-%E5%B0%88%E6%A1%88) +9. [管理 Poetry 虛擬環境](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E7%AE%A1%E7%90%86-Poetry-%E8%99%9B%E6%93%AC%E7%92%B0%E5%A2%83) +10. [Poetry 常用指令](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#Poetry-%E5%B8%B8%E7%94%A8%E6%8C%87%E4%BB%A4) +11. [Poetry 常見使用情境與操作 QA](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#Poetry-%E5%B8%B8%E8%A6%8B%E4%BD%BF%E7%94%A8%E6%83%85%E5%A2%83%E8%88%87%E6%93%8D%E4%BD%9C-QA) +12. [結語:井然有序的複雜](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E7%B5%90%E8%AA%9E%EF%BC%9A%E4%BA%95%E7%84%B6%E6%9C%89%E5%BA%8F%E7%9A%84%E8%A4%87%E9%9B%9C) + +--- + +## Poetry 是什麼? + +要了解 Poetry 大致的作用與功能,參考 [Poetry GitHub](https://github.com/python-poetry/poetry#poetry-dependency-management-for-python) 說明是一個不錯的開始: + +> **Poetry: Dependency Management for Python** +> Poetry helps you declare, manage and install dependencies of Python projects + +而 [Poetry 官網](https://python-poetry.org/)的 slogan 則更加簡潔有力: + +![python-poetry.org](https://i.imgur.com/xnYx0FB.png) + +簡單來說,**Poetry 類似 pip,能協助你進行套件管理(dependency management),但又比 pip 強大得多,因為它還包含了 pip 所未有的下列功能:** + +- 虛擬環境管理 +- 套件相依性管理 +- 套件的打包與發布 + +其中**最為關鍵**的是「**套件的相依性管理**」,也是本文的重點,而「套件的打包與發布」與主題較無關係,所以不會提及。 + +## 名詞解釋:虛擬環境管理、套件管理、相依性管理 + +開始前,要先大致說明標題中這三者的區別,才不易混淆文中的內容。這裡的定義可能不盡準確,但至少對理解文中的表達能有所幫助。 + +### 虛擬環境管理 + +指的是使用內建的 venv 或 virtualenv 套件來建立及管理 Python 的虛擬環境,不同的虛擬環境間各自獨立,也就是對應的路徑各不相同。 + +### 套件管理、依賴管理(dependency management) + +指的是使用 pip 這類的套件管理器來管理 Python 環境(未必是虛擬環境),即管理環境中所安裝的全部套件(package、dependency)及其版本。 + +在這個**語境**下,dependency 基本上就是指你安裝的 package。 + +### 「套件的」相依性管理、依賴解析 + +這個有點難定義,它並不是一個非常通俗且有共識的名詞,我在英文中也還難找到對應的單字。本文使用它時,主要指的是**套件與套件之間的依賴關係及版本衝突管理**,也就是套件的「**相依性管理**」。在下文提及的 Podcast 中,又稱為「**依賴解析**」。 + +所謂套件的「**版本衝突**」指的是單一套件被兩個以上的套件所依賴,但不同的套件對依賴的套件有著不同的**最低或最高版本要求**,若兩者要求的範圍「**沒有交集**」,則會發生衝突而導致套件**失效**或**無法安裝**。 + +## pip 的最大不足 + +大概在 2 年前就聽過 Poetry 的大名,不過那時我還沒有套件相依性管理的強烈需求,加上看起來需要一些學習成本(確實如此),所以就一直擱在一旁,直到真正體會到了 pip 的不足。 + +pip 是 Python 內建的套件管理工具,而它的最大罩門,就是對於「套件間的相依性管理」能力不足。尤其是在「**移除**」套件時的依賴解析——可以說沒有。這也是我提議改用 Poetry 的根本原因。 + +怎麼說?看完下面的例子就能明白。 + +### `pip uninstall`的困境:以 Flask 為例 + +假設現在你的工作專案中有開發 API 的需求,經過一番研究與討論,決定使用 [Flask](https://github.com/pallets/flask) 網頁框架來進行開發。 + +我們知道,很多套件都有依賴的套件,也就是使用「別人已經造好的輪子」來構成套件功能的一部分。 + +安裝主套件時,這些依賴套件也**必須一併安裝,主套件才能正常運作**,這裡的 Flask 就是如此。安裝 Flask 時,不會只安裝單一個`flask`套件,還會安裝所有 Flask 的必要構成部分——也就是依賴套件,結果如下: + +``` +❯ pip install flask +Collecting flask +Downloading Flask-2.1.1-py3-none-any.whl (95 kB) +|████████████████████████████████| 95 kB 993 kB/s +Collecting importlib-metadata>=3.6.0 +Using cached importlib_metadata-4.11.3-py3-none-any.whl (18 kB) +Collecting itsdangerous>=2.0 +Downloading itsdangerous-2.1.2-py3-none-any.whl (15 kB) +Collecting Werkzeug>=2.0 +Downloading Werkzeug-2.1.1-py3-none-any.whl (224 kB) +|████████████████████████████████| 224 kB 2.8 MB/s +Collecting click>=8.0 +Downloading click-8.1.2-py3-none-any.whl (96 kB) +|████████████████████████████████| 96 kB 1.9 MB/s +Collecting Jinja2>=3.0 +Downloading Jinja2-3.1.1-py3-none-any.whl (132 kB) +|████████████████████████████████| 132 kB 3.7 MB/s +Collecting zipp>=0.5 +Using cached zipp-3.7.0-py3-none-any.whl (5.3 kB) +Collecting MarkupSafe>=2.0 +Downloading MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl (13 kB) +Installing collected packages: zipp, MarkupSafe, Werkzeug, Jinja2, itsdangerous, importlib-metadata, click, flask +Successfully installed Jinja2-3.1.1 MarkupSafe-2.1.1 Werkzeug-2.1.1 click-8.1.2 flask-2.1.1 importlib-metadata-4.11.3 itsdangerous-2.1.2 zipp-3.7.0 +``` + +從上可知,`pip install flask`還會一併安裝`importlib-metadata`、`itsdangerous`等 7 個依賴套件,實際上總共安裝了 8 個套件! + +可以說,pip 在「安裝」套件時的相依性管理還是可以的,這並不難,因為套件的依賴要求都寫在安裝檔裡了,根本不需要「管理」。 + +--- + +附帶一提,這 8 個套件包括`flask`,除了`importlib-metadata`和`zipp`外,其餘 6 個實際上都是由 [Flask 團隊自行開發](https://palletsprojects.com/p/)。 + +但並非只有 Flask 框架會使用(依賴)這些套件。 + +比如其中的 [Click](https://palletsprojects.com/p/click/) 就是一個被廣泛使用的命令列製作工具。套件官網是這麼介紹的: + +> Click is a Python package for **creating beautiful command line interfaces** in a composable way with as little code as necessary. + +別的套件也可能依賴`click`來提供命令列的功能,換句話說,主套件的依賴套件也可能被其他第三方套件所依賴、使用。**這就產生了「衝突」的可能。** + +--- + +好,一切都很美好,就這樣一年過去,團隊決定改用火紅的 FastAPI 取代 Flask 來實作專案的 API,作為 API 的主要開發人員,你對新技術充滿了期待(或排斥),興高采列地安裝了 FastAPI,更新了所有程式碼,最後要移除 Flask,這時問題就來了。 + +安裝 Flask 的時候,我們只需要`pip install flask`,pip 就會幫你一併安裝所有依賴套件。現在要移除它,也只要`pip uninstall flask`就可以了嗎? + +> **很遺憾,答案是否定的**。 + +### pip 的致命缺陷:缺乏移除套件時的依賴解析(相依性管理) + +僅執行`pip uninstall flask`的話,pip 就**真的只會**幫你移除`flask`這個套件**本身**而已。那剩下的、再也用不到的套件怎麼辦?你只能一個一個手動移除! + +但你千萬不要真的嘗試手動移除依賴套件!——因為你無法確定這些依賴套件**是否同時被別的套件所依賴**。 + +### pip 手動移除依賴套件的潛在風險:以 Flask + Black 為例 + +繼續以 Flask 為例,還記得其中一個依賴套件是`click`,如前所述,它是一個協助製作命令列界面的工具。 + +假設專案中已同時安裝了 [Black](https://github.com/psf/black) 這個 Python code formatter 來統一程式碼排版(沒錯!我現在個人開發也都改用 Black 取代 [yapf](https://github.com/google/yapf) 了),Black 是一個可以透過 CLI 指令執行的格式化工具,剛好,它也是使用`click`來實作命令列功能。 + +> **Black formatter** 相關文章: +> +> - [試用從 VS Code Python extension 拆分的 Black、isort 套件](https://blog.kyomind.tw/black-and-isort/) +> - [pyproject.toml 介紹 + VS Code 整合 Black、yapf、isort 教學](https://blog.kyomind.tw/pyproject-toml/) + +我們可以藉由 Poetry 指令(後續介紹)來查看,目前這唯二安裝套件的**依賴關係樹**: + +``` +black 22.3.0 The uncompromising code formatter. +├── click >=8.0.0 +│ └── colorama * +├── mypy-extensions >=0.4.3 +├── pathspec >=0.9.0 +├── platformdirs >=2 +├── tomli >=1.1.0 +└── typing-extensions >=3.10.0.0 +flask 2.1.2 A simple framework for building complex web applications. +├── click >=8.0 +│ └── colorama * +├── importlib-metadata >=3.6.0 +│ └── zipp >=0.5 +├── itsdangerous >=2.0 +├── jinja2 >=3.0 +│ └── markupsafe >=2.0 +└── werkzeug >=2.0 +``` + +可以明顯看出,**兩者都依賴了`click`套件**。可想而知,移除 Flask 時,如果你同時把`click`也**跟著一併移除**,會發生什麼樣的悲劇——**你的 Black 壞了**——因為它缺少了必要的依賴套件`click`。 + +簡言之,直接 pip 手動移除依賴套件存在下列兩大疑慮,不建議輕易嘗試: + +#### 一、無法確定想移除的套件還有多少依賴套件 + +正常而言,你不會去注意安裝時總共一併安裝了多少依賴套件。雖然有`pip show`這類的指令可以大概知曉套件的依賴,但這指令只會顯示「**直接**依賴套件」而不會顯示「依賴套件的依賴」,所以列出來的結果未必準確: + +```bash +❯ pip show flask +Name: Flask +Version: 2.1.1 +Summary: A simple framework for building complex web applications. +Home-page: https://palletsprojects.com/p/flask +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +License: BSD-3-Clause +Location: /Users/kyo/.pyenv/versions/3.8.12/envs/test/lib/python3.8/site-packages +Requires: importlib-metadata, Werkzeug, click, Jinja2, itsdangerous +Required-by:**** +``` + +可以看到,`Requires:`只顯示了 5 個依賴套件,因為剩下的 2 個(`zipp`、`markupsafe`)是「**依賴的依賴**」,在更下層,並未顯示。 + +#### 二、即使確定所有依賴套件,也無法確定這些套件是否還被其他套件所依賴 + +好繞口啊!上述的`click`例子就是解釋這個困境。 + +### 小結:pip 只適合小型專案或「只新增不移除」套件的專案 + +以前我的個人或工作上的專案往往規模不大,pip 就真的只負責新增,鮮少需要考慮移除套件的情況,所以缺少移除套件時的依賴解析,似乎也沒什麼大問題。 + +但稍具模規的專案往往就需要考慮套件的退場,以維持開發及部署環境的簡潔,尤其在使用容器化部署時,**過多不必要的套件會徒增 image 肥大,產生額外的成本與資源浪費不說,同時也提升了「套件之間發生衝突」的可能。** + +然而透過上述的例子可知,僅靠 pip 想要**乾淨移除**過時的套件,且不影響既有的套件,簡直是**不可能的任務!**所以我們需要擁有「**完整套件相依性管理功能**」的套件管理器。 + +--- + +## pip 替代方案選擇——Pipenv vs Poetry + +關於 pip 的前世今生、它的歷史包袱,以及為何它難以演化成理想的、可以完美管理套件相依性的模樣,可以參考〈[告別 Anaconda:在 macOS 上使用 pyenv 建立 Python 開發環境](https://blog.kyomind.tw/pyenv-setup/)〉中推薦過的單集 Podcast: + +- [《捕蛇者說》Ep 15. 和 PyPA 的成員聊聊 Python 開發工作流](https://pythonhunter.org/episodes/ep15) + +從 Podcast 官方頁面中「時間節點」目錄中可知,該集對 Python 的虛擬環境與套件管理機制及相關工具,有著非常廣泛的討論,十分精彩,強力推薦!(為了寫這篇又聽了第 3 次) + +![pythonhunter.org/episodes/ep15](https://i.imgur.com/gzcAU7e.png) + +### Pipenv vs Poetry + +因為 pip 存在這樣的致命弱點,所以很早就有相關的方案提出想要解決它,最知名的莫過於 [Pipenv](https://pipenv.pypa.io/en/latest/)! + +而講到需要有充分「套件相依性管理」功能的套件管理器,你基本上也只能從 Pipenv 和 Poetry 兩者之中二擇一了。 + +如果是在兩年前,這道選擇題恐怕不容易回答,且 Pipenv 可能會有較大的機率勝出,**但兩年後的今天,我會建議你毫不猶豫地選擇 Poetry。** + +## 選擇 Poetry 的兩個理由 + +怎麼說呢?以下是我的理由。 + +### 理由一:Pipenv 的不足 + +當你搜尋「python poetry」關鍵字的時候,那些教你怎麼使用 Poetry 的文章,往往也會一併提及**為何不選擇 Pipenv。** + +以下兩篇有著較為完整的說明,容我直接引用。 + +〈[Python - 取代 Pipenv 的新套件管理器 Poetry](https://note.koko.guru/posts/using-poetry-manage-python-package-environments)〉: + +> Pipenv 雖然強大,卻也暴露出了一些問題如 Lock 過慢、Windows 支援性差、對 PyPI 套件打包的友善度差…等更多其他問題,甚至有越來越多人表明 [不要使用 Pipenv](http://greyli.com/do-not-use-pipenv/) 或 [pipenv 的凋零與替代方案 poetry](https://blog.gslin.org/archives/2019/12/21/9347/pipenv-%E7%9A%84%E5%87%8B%E9%9B%B6%E8%88%87%E6%9B%BF%E4%BB%A3%E6%96%B9%E6%A1%88-poetry/) 等。 + +> 同時 Pipenv 的社群維護狀況也越來越差,有許多的 PR 都沒有被 Release,導致許多貢獻者抱怨,甚至有人發出了該篇 [If this project is dead, just tell us](https://github.com/pypa/pipenv/issues/4058) issue 想知道是否專案已經不在維護。 + +〈[相比 Pipenv,Poetry 是一個更好的選擇](https://greyli.com/poetry-a-better-choice-than-pipenv/)〉(本文作者[李輝](https://greyli.com/about/)為 Flask 團隊成員): + +> Pipenv 描繪了一個美夢,讓我們以為 Python 也有了其他語言那樣完善的包管理器,不過這一切卻在後來者 Poetry 這裡得到了更好的實現。 + +> 這幾年 Pipenv 收獲了很多用戶,但是也暴露了很多問題。雖然 Lock 太慢、Windows 支持不好和 bug 太多的問題都已經改進了很多,但對我來說,仍然不能接受隨時更新鎖定依賴的設定,在上一篇文章《[不要用 Pipenv](http://greyli.com/do-not-use-pipenv/)》裡也吐槽了很多相關的問題。 + +兩篇的引述內容總結就是一句話:不要用 Pipenv。 + +目前 Pipenv 已經由 [PyPA](https://github.com/pypa)(同時也維護 pip 及 virtualenv)接手,上述「**擺爛**」的情況相信已有所改善,不過我似乎還沒看到有什麼文章大力鼓吹或宣告 Pipenv 已經「great again」,所以個人對它的未來發展還是持保留態度。 + +### 理由二:pyproject.toml + +pyproject.toml 是 [PEP 518](https://peps.python.org/pep-0518/) 所提出的新標準: + +> The build system dependencies will be stored in a file named `pyproject.toml` that is written in the TOML format. + +原意是作為套件打包設定的標準格式,後來又有了 [PEP 621](https://peps.python.org/pep-0621/),將其**擴充定性**為 **Python 生態系工具的共同設定檔標準**,現在已經被愈來愈多套件所支援,詳細可參考[這個清單](https://github.com/carlosperate/awesome-pyproject)及頁面中的說明: + +> `pyproject.toml` is a new configuration file defined in [PEP 518](https://www.python.org/dev/peps/pep-0518/) and expanded in [PEP 621](https://www.python.org/dev/peps/pep-0621/). It is design to store build system requirements, **but it can also store any tool configuration for your Python project**, possibly replacing the need for `setup.cfg` or other tool-specific files. + +作為規範控,我很願意追隨這個標準。 + +並且,Poetry 使用`pyproject.toml`,可遠遠不止是設定檔的程度,更是**不可或缺**的一部分。相當於 Pipenv 的`Pipfile`或 npm 的`package.json`。 + +換句話說,少了`pyproject.toml`,Poetry 便無法運作。 + +> 更多關於 **pyproject.toml** 的介紹與實踐,可參考:[pyproject.toml 介紹 + VS Code 整合 Black、yapf、isort 教學](https://blog.kyomind.tw/pyproject-toml/) + +--- + +好,漫長的前言到此結束,讓我們進入正題,開始上手學習 Poetry。 + +## 從零開始使用 Poetry + +本文所有的參考資料會放在文末的「參考」一欄中,不過在此還是要特別提及主要的參考對象,總共有二: + +- [Poetry 官方文件](https://python-poetry.org/docs/) +- [Dependency Management With Python Poetry](https://realpython.com/dependency-management-python-poetry/) + +如果在本文找不到你需要的內容,以上二處可能會有,所以主動列出。 + +另外本文主要以 macOS 和 Linux(Ubuntu)環境來進行安裝及教學,Windows 用戶如果有無法順利安裝的情況,建議參考官方文件內容修正。不過,即使有問題,應該也是集中在安裝與設定階段,本文其餘部分仍可適用。 + +## 安裝 Poetry + +Poetry 和 pip、git、pyenv 等工具一樣,都是典型的**命令列工具**,需要先安裝才能下達指令——`poetry`。 + +### 安裝方式選擇 + +Poetry 主要提供了[兩種安裝方式](https://python-poetry.org/docs/#installation): + +1. **全域安裝**至使用者的家目錄。 +2. **pip 安裝**至專案使用的 Python(虛擬)環境,即`pip install poetry`。 + +**個人推薦使用全域安裝**,官方文件也表示[不推薦使用 pip 安裝](https://python-poetry.org/docs/#alternative-installation-methods-not-recommended)。 + +因為 pip 安裝是直接安裝到「**專案所屬的 Python 虛擬環境**」裡,而 Poetry 所依賴的套件非常多,**總計超過 30 個,會嚴重影響專案虛擬環境的整潔度**。文件中也警告這些依賴套件可能和專案本身的套件**發生衝突**: + +> Be aware that it will also install Poetry’s dependencies which **might cause conflicts with other packages.** + +### 全域安裝 Poetry 至家目錄 + +所以我們就使用全域安裝吧! + +#### macOS / Linux / WSL(Windows Subsystem for Linux) + +``` +curl -sSL https://install.python-poetry.org | python3 - +``` + +或 + +``` +curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - +``` + +#### Windows + +``` +(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python - +``` + +Poetry 實際安裝路徑如下: + +> The installer installs the `poetry` tool to Poetry’s `bin` directory. This location depends on your system: + +- `$HOME/.local/bin` for Unix +- `%APPDATA%\Python\Scripts` on Windows + +以 macOS 為例,如果要下`poetry`指令,就需要打完整路徑`$HOME/.local/bin/poetry`,顯然不太方便,所以我們需要設定 PATH。 + +### 設定 PATH + +新增`poetry`指令執行檔所在的路徑至 PATH。 + +在`.zshrc`或`.bashrc`或`.bash_profile`新增: + +``` +export PATH=$PATH:$HOME/.local/bin +``` + +存檔後重啟 shell 即可使用。直接在命令列打上`poetry`指令測試: + +``` +❯ poetry +Poetry version 1.1.13 + +USAGE + poetry [-h] [-q] [-v [<...>]] [-V] [--ansi] [--no-ansi] [-n] [] ... [] + +... +``` + +### 設定 alias + +比起`pip`,`poetry`這個指令實在太冗長了!我們還是給它一個 alias 吧! + +基於它是我極為常用的指令,我願意賦與它**「單字母」的 alias 特權**,我使用`p`: + +``` +alias p='poetry' +``` + +測試結果: + +``` +❯ p +Poetry version 1.1.13 + +USAGE + poetry [-h] [-q] [-v [<...>]] [-V] [--ansi] [--no-ansi] [-n] [] ... [] +``` + +alias 是方便自己使用,但本文基於表達清晰考量,下面的解說除了圖片外,原則上並不會使用 alias 表示。 + +--- + +## 初始化 Poetry 專案 + +為了方便解說,我們先建立一個全新的專案,名為`poetry-demo`。 + +指令都很簡單,但還是建議可以一步一步跟著操作。 + +就像 git 專案需要初始化,Poetry 也需要,因為每一個使用了 Poetry 的專案中一定要有一個`pyproject.toml`作為它的**設定檔**。否則直接使用`poetry`相關指令就會出現下列錯誤訊息: + +> Poetry could not find a pyproject.toml file in {cwd} or its parents + +所以一定先初始化,使用`poetry init`: + +``` +mkdir poetry-demo +cd poetry-demo +poetry init +``` + +此時會跳出一連串的互動對話,協助你建立專案的資料,大部分可以直接`enter`跳過: + +``` +This command will guide you through creating your pyproject.toml config. + +Package name [poetry-demo]: +Version [0.1.0]: +Description []: +Author [kyo , n to skip]: +License []: +Compatible Python versions [^3.8]: + +Would you like to define your main dependencies interactively? (yes/no) [yes] +``` + +直到出現「`Would you like to define your main dependencies interactively? (yes/no) [yes]`」,我們先選擇「**no**」後,會讓你確認本次產生的`toml`檔內容: + +``` +Would you like to define your development dependencies interactively? (yes/no) [yes] no +Generated file + +[tool.poetry] +name = "poetry-demo" +version = "0.1.0" +description = "" +authors = ["kyo "] + +[tool.poetry.dependencies] +python = "^3.8" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" +``` + +並詢問你「`Do you confirm generation? (yes/no) [yes]`」,按`enter`使用預設選項「yes」或直接回答「yes」,則`pyproject.toml`建立完成。 + +此時專案目錄結構如下: + +``` +poetry-demo +└── pyproject.toml + +0 directories, 1 file +``` + +## 管理 Poetry 虛擬環境 + +我覺得學習 Poetry 的**第一道關卡**,就是它對於虛擬環境的管理。 + +### 「強制」虛擬環境 + +Poetry 預設上(可透過`poetry config`修改)會強制套件都要安裝在虛擬環境中,以免污染全域,所以它整合了`virtualenv`。 + +所以在執行`poetry add、install`等指令時,Poetry 都會自動檢查**當下是否正在使用虛擬環境:** + +- 如果**是**,則會直接安裝套件至**當前**的虛擬環境。 +- 如果**否**,則會自動幫你建立一個**新的**虛擬環境,再進行套件安裝。 + +### 容易混淆的虛擬環境 + +Poetry 主動納入虛擬環境管理算是立意良善,相當於把`pip`+`venv`兩者的功能直接整合在一起,**但也帶來一定的複雜度**,尤其在你已經自行使用了`venv`、`virtualenv`或 `pyenv-virtualenv`或`conda`等工具來管理虛擬環境的情況下! + +**沒錯,Python 的虛擬環境管理就是這麼麻煩!** + +個人建議,對新手而言,於 Poetry 的專案中,**一律使用 Poetry** 來管理虛擬環境即可。我目前也是這樣,省得麻煩。 + +### 以指令建立虛擬環境 + +使用指令`poetry env use python`: + +``` +❯ poetry env use python +Creating virtualenv poetry-demo-IEWSZKSE-py3.8 in /Users/kyo/Library/Caches/pypoetry/virtualenvs +Using virtualenv: /Users/kyo/Library/Caches/pypoetry/virtualenvs/poetry-demo-IEWSZKSE-py3.8 +``` + +可以看出 Poetry 為我們建立了名為`poetry-demo-IEWSZKSE-py3.8`的虛擬環境。 + +### 重點說明 + +- `poetry env use python`建立虛擬環境所使用的 Python 版本,取決於`python`指令在你的 PATH 是連結到哪個版本。同理,你也可以將指令明示為`use python3`或`use python3.8`,只要這些指令確實存在 PATH 中。 +- 預設上,Poetry 會統一將虛擬環境建立在「**特定目錄**」裡,比如本例中存放的路徑是`/Users/kyo/Library/Caches/pypoetry/virtualenvs`。 +- 虛擬環境的**命名模式為`專案名稱-亂數-Python版本`。** + +老實說我個人不是很喜歡這樣的做法,因為這意味著單一專案允許建立複數個虛擬環境(比如 Python 3.7、3.8、3.9 可以各來一個),**彈性之餘也增加了混亂的可能**,而且這命名模式我也不太欣賞,顯得過於僵化且冗長。 + +既然 Python 的虛擬環境理論上都是**高度綁定專案本身**的,我更偏好`venv`式的做法,也就是**把虛擬環境放到專案目錄內**,而非統一放在獨立的目錄,讓虛擬環境與專案呈現**直觀的一對一關係**。 + +所幸,Poetry 具備這樣的選項。 + +--- + +### 修改`config`,建立專案內的`.venv`虛擬環境 + +我們先使用`poetry config`指令來查看 Poetry 目前幾個主要的設定,需要`--list`這個參數: + +``` +❯ poetry config --list +cache-dir = "/Users/kyo/Library/Caches/pypoetry" +experimental.new-installer = true +installer.parallel = true +virtualenvs.create = true +virtualenvs.in-project = false +virtualenvs.path = "{cache-dir}/virtualenvs" +``` + +其中`virtualenvs.create = true`若改成`false`,則可以停止 Poetry 在「偵測不到虛擬環境時會自行建立」的行為模式,但建議還是不要更動。 + +而`virtualenvs.in-project = false`就是我們要修改的目標,使用指令: + +``` +poetry config virtualenvs.in-project true +``` + +好,我們先把之前建立的虛擬環境刪除: + +``` +❯ poetry env remove python +Deleted virtualenv: /Users/kyo/Library/Caches/pypoetry/virtualenvs/poetry-demo-IEWSZKSE-py3.8 +``` + +重新建立,看看行為有何差異: + +``` +❯ poetry env use python +Creating virtualenv poetry-demo in /Users/kyo/Documents/code/poetry-demo/.venv +Using virtualenv: /Users/kyo/Documents/code/poetry-demo/.venv +``` + +可以看出: + +- 虛擬環境的路徑改為「**專案的根目錄**」。 +- 名稱固定為`.venv`。 + +我覺得這樣的設定更加簡潔。 + +### 啟動與退出虛擬環境 + +啟動虛擬環境,需移至專案目錄底下,使用指令`poetry shell`: + +1 +2 +3 +``` +❯ poetry shell +Spawning shell within /Users/kyo/Documents/code/poetry-demo/.venv +❯ . /Users/kyo/Documents/code/poetry-demo/.venv/bin/activate +``` + +`poetry shell`指令會偵測當前目錄或所屬上層目錄是否存在`pyproject.toml`來確定所要啟動的虛擬環境,所以如果不移至專案目錄,則會出現下列錯誤: + +``` +❯ poetry shell + + RuntimeError + + Poetry could not find a pyproject.toml file in /Users/kyo/Documents/code or its parents + + at ~/Library/Application Support/pypoetry/venv/lib/python3.8/site-packages/poetry/core/factory.py:369 in locate + 365│ if poetry_file.exists(): + 366│ return poetry_file + 367│ + 368│ else: + → 369│ raise RuntimeError( + 370│ "Poetry could not find a pyproject.toml file in {} or its parents".format( + 371│ cwd + 372│ ) + 373│ ) +``` + +可以看到,Poetry 的錯誤訊息非常清楚,讓你很容易知曉修正的方向,這是作為一個優秀命令列工具的必要條件。 + +退出就簡單多了,只需要`exit`即可。 + +--- + +## Poetry 常用指令 + +Poetry 是一個獨立的命令列工具,就像 pyenv,它有自己的指令,需要花費額外的心力學習,且較 pip 更加複雜,這可能是使用 Poetry 的**第二道關卡**。好在常用的指令,其實也不超過 10 個,下面就來一一介紹。 + +在此我們繼續使用前面提過的 Flask 和 Black 套件,來示範並說明 Poetry 的優勢以及它和 pip 的不同之處。 + +### Poetry 新增套件 + +使用指令: + +``` +poetry add +``` + +相當於`pip install`,我們來試著安裝 Flask 看看會有什麼變化: + +![](https://i.imgur.com/H7pPtsk.png) + +圖中可以看出 Poetry 漂亮的命令列資訊呈現,會清楚告知總共新增了幾個套件。 + +此時專案中的`pyproject.toml`也會發生變化: + +``` +... +[tool.poetry.dependencies] +python = "^3.8" +Flask = "^2.1.1" # 新增部分 + +[tool.poetry.dev-dependencies] + +[build-system] +... +``` + +這裡要說明,安裝 Flask,則`pyproject.toml`就只會新增記載`Flask = "^2.1.1"`這個 **top-level** 的 package 項目,其餘的依賴套件**不會**直接記錄在`toml`檔中。 + +我覺得這是一大優點,方便區分哪些是你**主動安裝**的主要套件,而哪些又是基於套件的依賴關係而一併安裝的依賴套件。 + +### poetry.lock 與更新順序 + +除了更新`pyproject.toml`,此時專案中還會新增一個檔案,名為`poetry.lock`,它實際上就相當於 pip 的`requirements.txt`,詳細記載了所有安裝的套件與版本。 + +當你使用`poetry add`指令時,Poetry 會**自動依序**幫你做完這三件事: + +1. 更新`pyproject.toml`。 +2. 依照`pyproject.toml`的內容,更新`poetry.lock`。 +3. 依照`poetry.lock`的內容,更新虛擬環境。 + +由此可見,`poetry.lock`的內容是取決於`pyproject.toml`,但兩者並不會自己連動,一定要基於特定指令才會進行同步與更新,`poetry add`就是一個典型案例。 + +此時專案目錄結構如下: + +``` +poetry-demo +├── poetry.lock +└── pyproject.toml + +0 directories, 2 files +``` + +### 更新 poetry.lock + +當你自行修改了`pyproject.toml`內容,比如變更特定套件的版本(這是有可能的,尤其在手動處理版本衝突的時候),此時`poetry.lock`的內容與`pyproject.toml`出現了「**脫鉤**」,必須讓它依照新的`pyproject.toml`內容更新、同步,使用指令: + +``` +poetry lock +``` + +如此一來,才能確保手動修改的內容,也更新到`poetry.lock`中,畢竟虛擬環境如果要重新建立,是基於`poetry.lock`的內容來安裝套件,而非`pyproject.toml`。 + +還是那句話:`poetry.lock`相當於 Poetry 的`requirements.txt`。 + +--- + +### 列出全部套件清單 + +類似`pip list`,這裡要使用`poetry show`: + +``` +❯ poetry show +black 22.3.0 The uncompromising code formatter. +click 8.1.3 Composable command line interface toolkit +flask 2.1.2 A simple framework for building complex web applications. +importlib-metadata 4.11.4 Read metadata from Python packages +itsdangerous 2.1.2 Safely pass data to untrusted environments and back. +jinja2 3.1.2 A very fast and expressive template engine. +markupsafe 2.1.1 Safely add untrusted strings to HTML/XML markup. +mypy-extensions 0.4.3 Experimental type system extensions for programs checked... +pathspec 0.9.0 Utility library for gitignore style pattern matching of ... +platformdirs 2.5.2 A small Python module for determining appropriate platfo... +... +``` + +特別提醒的是,這裡的清單內容**並不是來自於虛擬環境**,這點和 pip 不同,而是來自於`poetry.lock`的內容。 + +你可能會想,來自於`poetry.lock`或虛擬環境,有差嗎?兩者不是應該要一致? + +沒錯,理論上是,但也有不一致的時候,比如你使用了`pip install`指令安裝套件,就不會記載在`poetry.lock`中,那`poetry show`自然也不會顯示。 + +### 「樹狀」顯示套件依賴層級 + +Poetry 最為人津津樂道的就是它的樹狀顯示——`poetry show --tree`。 + +``` +❯ poetry show --tree +flask 2.1.1 A simple framework for building complex web applications. +├── click >=8.0 +│ └── colorama * +├── importlib-metadata >=3.6.0 +│ └── zipp >=0.5 +├── itsdangerous >=2.0 +├── jinja2 >=3.0 +│ └── markupsafe >=2.0 +└── werkzeug >=2.0 +black 22.3.0 The uncompromising code formatter. +├── click >=8.0.0 +│ └── colorama * +├── mypy-extensions >=0.4.3 +├── pathspec >=0.9.0 +├── platformdirs >=2 +├── tomli >=1.1.0 +└── typing-extensions >=3.10.0.0 +``` + +讓主要套件與其依賴套件的**關係與層次,一目了然**。 + +而且很貼心的是,它也可以**只顯示「指定套件」**的依賴層級,以`celery`為例: + +``` +❯ poetry show celery --tree +celery 4.4.0 Distributed Task Queue. +├── billiard >=3.6.1,<4.0 +├── kombu >=4.6.7,<4.7 +│ ├── amqp >=2.6.0,<2.7 +│ │ └── vine >=1.1.3,<5.0.0a1 +│ └── importlib-metadata >=0.18 +│ ├── typing-extensions >=3.6.4 +│ └── zipp >=0.5 +├── pytz >0.0-dev +└── vine 1.3.0 +``` + +### 安裝套件至 dev-dependencies + +有些套件,比如`pytest`、`flake8`等等,**只會在開發環境中使用**,產品的**部署環境**並不需要安裝。 + +Poetry 允許你區分這兩者,將上述的套件安裝至`dev-dependencies`區塊,方便讓你**輕鬆建立一份不包含 dev 套件的安裝清單**。 + +在此以 Black 為例,安裝方式如下: + +``` +poetry add black -D +``` + +或 + +``` +poetry add black --dev +``` + +結果的區別顯示在`pyproject.toml`裡: + +``` +... +[tool.poetry.dependencies] +python = "^3.8" +Flask = "^2.1.1" + +[tool.poetry.dev-dependencies] +black = "^22.3.0" +... +``` + +可以看到`black`被列在不同區塊:`tool.poetry.dev-dependencies`。 + +#### 建議:明確區分 dev-dependencies + +明確區分出**開發環境專用**的套件,並從部署環境中分離,我認為很有必要。 + +因為這些套件常常屬於「檢測型」工具,相關的**依賴套件**著實都不少!比如`flake8`,它依賴了`pycodestyle`、`pyflakes`、`mccabe`等等,這些套件都是開發環境才需要,而且它們的版本也不一定要與產品的版本一致。 + +還有`black`、`pre-commit`等,依賴的套件數量也都很可觀,如果不加以區分一律安裝到`dependencies`區塊,部署環境就會顯得過於**臃腫**。 + +常見的`dev-dependencies`區塊項目例示如下: + +```toml +[tool.poetry.dev-dependencies] +flake8 = "4.0.1" +yapf = "0.32.0" +pytest = "7.1.2" +pytest-django = "4.5.2" +pytest-cov = "3.0.0" +pytest-env = "0.6.2" +pytest-sugar = "0.9.4" +pre-commit = "2.20.0" +``` + +--- + +### Poetry 移除套件 + +使用`poetry remove`指令。和`poetry add`一樣,可以加上`-D`參數來移除置於開發區的套件。 + +而移除套件時的「**依賴解析(相依性管理)**」能力,正是 Poetry 遠優於 pip 的主要環節,因為 pip 沒有嘛!也是我提議改用 Poetry 的關鍵理由——**為了順利移除套件**。 + +前面已經提過,pip 的`pip uninstall`只會移除你所指定的套件,而不會連同依賴套件一起移除。 + +這是基於安裝考量,因為 pip 沒有「依賴解析」功能。如果貿然移除所有「安裝時一併安裝」的依賴套件,可能會造成巨大災難,讓別的套件失去效用。 + +前面也舉了 Flask 和 Black 都共同依賴`click`這個套件的例子,在手動移除套件的情況下,你可能未曾注意 Black 也依賴了`click`,結果為了「徹底移除」Flask 的所有相關套件,不小心把`click`也移除掉了。 + +當然,我知道,絕大部分的真實情況是——你根本不會去移除一段時間前安裝但已不再使用的套件。 + +--- + +好,解釋了很多,接下來就是 Poetry 的表演了,它會幫你處理這些棘手的「套件相依性」難題,讓你輕鬆移除 Flask 而不影響 Black: + +![](https://i.imgur.com/79TycuL.png) + +可以對比上面安裝 Flask 時的截圖,那時總共安裝了 8 個套件,但現在移除的卻只有 7 個——沒錯,因為有依賴解析,**Poetry 知道 Black 還需要**`click`!所以不能移除: + +``` +❯ poetry show --tree +black 22.3.0 The uncompromising code formatter. +├── click >=8.0.0 +│ └── colorama * +├── mypy-extensions >=0.4.3 +├── pathspec >=0.9.0 +├── platformdirs >=2 +├── tomli >=1.1.0 +└── typing-extensions >=3.10.0.0 +``` + +一個套件直到環境中的**其餘套件都不再依賴它**,Poetry 才會安心讓它被移除。 + +### 輸出 Poetry 虛擬環境的 requirements.txt + +理論上,全面改用 Poetry 後,專案中是不需要存在`requirements.txt`,因為它的角色已經完全被`poetry.lock`所取代。 + +但事實是,你可能還需要它,甚至還需要它隨著`poetry.lock`的內容更新!至少對我而言就是如此,我在 Docker 部署環境中並不使用 Poetry,所以我需要一份完全等價於`poetry.lock`的`requirements.txt`,用於 Docker 部署。 + +你可能想說,那我就在 Poetry 的虛擬環境下,使用以往熟悉的指令`pip freeze > requirements.txt`來產生一份就可以了吧?我本來也是這麼想,但實際的產出卻是如此:(提醒:目前 poetry-demo 專案中**僅剩下 Black 和它的依賴套件**) + +``` +black @ file:///Users/kyo/Library/Caches/pypoetry/artifacts/11/4c/fc/cd6d885e9f5be135b161e365b11312cff5920d7574c8446833d7a9b1a3/black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl +click @ file:///Users/kyo/Library/Caches/pypoetry/artifacts/f0/23/09/b13d61d1fa8b3cd7c26f67505638d55002e7105849de4c4432c28e1c0d/click-8.1.2-py3-none-any.whl +mypy-extensions @ file:///Users/kyo/Library/Caches/pypoetry/artifacts/b6/a0/b0/a5dc9acd6fd12aba308634f21bb7cf0571448f20848797d7ecb327aa12/mypy_extensions-0.4.3-py2.py3-none-any.whl +... +``` + +這呈現好像不是我們以前熟悉的那樣: + +``` +black==22.3.0 +click==8.1.2 +mypy_extensions==0.4.3 +... +``` + +沒錯,只要是使用`poetry add`安裝的套件,在`pip freeze`就會變成這樣。此時想輸出類似`requirements.txt`的格式,需要使用`poetry export`。 + +預設的輸出結果會有 hash 值,很干擾閱讀。不想納入 hash 則要**加上參數**去除。**以下就是我固定用來輸出`requirements.txt`的指令與參數:** + +``` +poetry export -f requirements.txt -o requirements.txt --without-hashes +``` + +`2022/08/24`補充:網友提醒,**hash 有其價值,並建議保留**,詳見[留言區](https://github.com/kyomind/blog-reply/issues/5#issuecomment-1195904820)。 + +我們再看一下輸出結果,雖然不盡相同,但也相去不遠了……嗎?等等,怎麼是空白? + +--- + +因為`poetry export`預設只會輸出`toml`中的`[tool.poetry.dependencies]`區塊的套件!還記得上面我們把 Black 安裝到`[tool.poetry.dev-dependencies]`了嗎? + +顯然 Poetry 認為你的 export 需求基本上就為了部署,並不需要開發區的套件。這倒是沒錯,不過基於演示需求,我們必須輸出`[tool.poetry.dev-dependencies]`的套件,才能看到 Black。 + +加上`--dev`參數即可: + +``` +poetry export -f requirements.txt -o requirements.txt --without-hashes --dev +``` + +輸出的`requirements.txt`內容: + +``` +black==22.3.0; python_full_version >= "3.6.2" +click==8.1.2; python_version >= "3.7" and python_full_version >= "3.6.2" +colorama==0.4.4; python_version >= "3.7" and python_full_version >= "3.6.2" and platform_system == "Windows" +... +``` + +雖然長得有點不一樣,但這個檔案確實是可以`pip install`的。 + +從這裡也可以看出前面提及的「區分套件安裝區塊」的價值——有些時候並不需要輸出開發專用套件。 + +`poetry export`所有參數用法與說明,請參考[文件](https://python-poetry.org/docs/cli/#export)。 + +此時專案目錄結構如下: + +``` +poetry-demo +├── poetry.lock +├── pyproject.toml +└── requirements.txt + +0 directories, 3 files +``` + +### 小結:Poetry 常用指令清單 + +算來算去,Poetry 的常用指令主要有下面幾個: + +- `poetry add` +- `poetry remove` +- `poetry export` +- `poetry env use` +- `poetry shell` +- `poetry show` +- `poetry init` +- `poetry install` + +其中一半,單一專案可能只會用個一兩次而已,比如`init`、`install`和`env use`,實際上需要學習的指令並不多。 + +那麼,只要知曉這些指令,就可以順利運用 Poetry 了嗎?可能是,也可能否,所以我下面還會再補充 Poetry 的常見使用情境與操作方式,讓你接納 Poetry 的阻力可以進一步下降! + +--- + +## Poetry 常見使用情境與操作 QA + +這部分會以「**使用場景**」的角度切入,介紹 Poetry 應用情境與操作說明,還包括一些自問自答: + +1. [新增專案並使用 Poetry](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E4%B8%80%E3%80%81%E6%96%B0%E5%A2%9E%E5%B0%88%E6%A1%88%E4%B8%A6%E4%BD%BF%E7%94%A8-Poetry) +2. [現有專案改用 Poetry](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E4%BA%8C%E3%80%81%E7%8F%BE%E6%9C%89%E5%B0%88%E6%A1%88%E6%94%B9%E7%94%A8-Poetry) +3. [在別台主機回復專案狀態](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E4%B8%89%E3%80%81%E5%9C%A8%E5%88%A5%E5%8F%B0%E4%B8%BB%E6%A9%9F%E5%9B%9E%E5%BE%A9%E5%B0%88%E6%A1%88%E7%8B%80%E6%85%8B) +4. [我想要重建虛擬環境](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%9B%9B%E3%80%81%E6%88%91%E6%83%B3%E8%A6%81%E9%87%8D%E5%BB%BA%E8%99%9B%E6%93%AC%E7%92%B0%E5%A2%83) +5. [為什麼我不在 Docker 環境中使用 Poetry?](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E4%BA%94%E3%80%81%E7%82%BA%E4%BB%80%E9%BA%BC%E6%88%91%E4%B8%8D%E5%9C%A8-Docker-%E7%92%B0%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Poetry%EF%BC%9F) +6. [我可以使用自己習慣的 virtualenv 嗎?](https://blog.kyomind.tw/python-poetry/?fbclid=IwAR0PsmydXgpKzpbAWzJeK2ze7o0uXgGHn24xUvTCjo9fQQGGhcpnkPBG9n8#%E5%85%AD%E3%80%81%E6%88%91%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8%E8%87%AA%E5%B7%B1%E7%BF%92%E6%85%A3%E7%9A%84-virtualenv-%E5%97%8E%EF%BC%9F) + +### 一、新增專案並使用 Poetry + +這是最理想的狀態,沒有過去的「包袱」,可謂是最能輕鬆採用 Poetry 的情境。 + +使用順序不外乎是: + +1. `poetry init`:初始化,建立`pyproject.toml`。 +2. `poetry env use python`:建立專案虛擬環境並使用。 +3. `poetry shell`:進入專案但虛擬環境還未啟動,以這個指令啟動。如果使用本指令時虛擬環境**尚未建立或已移除**,則會**直接自動幫你建立虛擬環境**並使用。 +4. `poetry add`:新增套件並寫入虛擬環境。必要時使用`-D`參數新增至 dev 區塊。 +5. `poetry remove`:移除套件,若是移除 dev 區塊的套件,需要加上`-D`參數。 + +這部分和前面內容沒有差別,因為前面內容就是以全新專案作為基礎。 + +### 二、現有專案改用 Poetry + +極為常見的需求,但並沒有很正式的做法,因為不存在`poetry import`之類的指令。 + +首先要考量的就是:要怎麼把`requirements.txt`的所有項目加到`pyproject.toml`中呢?經過一番 Google,基本上[只能土法煉鋼](https://stackoverflow.com/questions/62764148/how-to-import-requirements-txt-from-an-existing-project-using-poetry): + +``` +cat requirements.txt | xargs poetry add +``` + +然而這樣做是有可能遇到一些問題的,因為 Poetry **對套件的版本衝突比較敏感**,所以即便用`pip install -r requirements.txt`都能正常安裝,透過上述指令的遷移過程卻仍有機會出現錯誤。 + +那怎麼辦?只能照著錯誤訊息手動修正`requirements.txt`中的套件版本。 + +只能說這個「**手動 import**」做法實在是不得已,因為我們最早介紹`pyproject.toml`時有提到,`poetry add`只會在`pyproject.toml`中寫入「主套件」,但這樣的 import 方式相當於把`requirements.txt`中的**所有套件,都當作主套件**來`add`了! + +畢竟在`requirements.txt`中**無從區分**主套件與依賴套件,都是「一視同仁」地列出。 + +但如此做法也讓專案的套件**失去主從之分**,這樣會有什麼**壞處**?日後要移除主套件時,**需要花額外的心力去區分主從**(因為僅僅移除依賴套件**並不會有移除效果**),比如使用`poetry show --tree`去一個一個檢視,終究是件麻煩事。 + +完成轉換後,為保險起見,建議透過新的`pyproject.toml`來重建一個虛擬環境。 + +### 三、在別台主機上重現專案的 Poetry 虛擬環境 + +這也是非常常見的需求。 + +第一步當然是`git clone`專案,此時專案中已經有 Poetry 所需的必要資訊了——也就是`pyproject.toml`和`poetry.lock`。 + +你還缺少的僅僅是虛擬環境。如果是全新的主機,則還得先安裝、設定好 Poetry。 + +確定 Poetry 可正常使用後,移至專案目錄底下,依序執行指令: + +1. `poetry env use python`:建立專案虛擬環境並使用。如果你懶得打這麼長的指令,直接`poetry shell`也是可以。此時我們會有一個「**空的**」虛擬環境。 +2. `poetry install`:因為是舊專案,不需要`init`,會直接依`poetry.lock`記載的套件版本安裝到虛擬環境中!類似`npm install`。 + +### 四、我想要重建虛擬環境 + +在使用專案內虛擬環境方案,也就是`.venv`的前提下,想要刪除這個虛擬環境並加以重建,也不需要使用`poetry env remove python`指令了,因為會出錯。 + +還有更簡單暴力的方式,是什麼呢?——直接刪除`.venv`資料夾即可。 + +然後再`poetry env use python`或`poetry shell`建一個新的就好。 + +### 五、為什麼我不在 Docker 環境中使用 Poetry? + +因為啟動容器後需要先安裝 Poetry 到全域,或打包一個帶有 Poetry 的 image,兩者都會**增加新的耦合與依賴**,我覺得並不妥當。 + +所幸 Poetry 依舊可以輸出`requirements.txt`,Docker 部署環境就繼續使用這個舊方案即可,而且 Poetry 本來主要就是用於「開發」時的套件管理,對部署差別不大。 + +### 六、我可以使用自己習慣的 virtualenv 嗎? + +當然可以。 + +不過我本來也繼續使用`pyenv`的`virtualenv`,但兩者有時候也是會小小打架,後來還是索性用 Poetry 的虛擬環境就好。 + +一個專案對應一個虛擬環境,應該還是比較簡潔的做法,我的觀察啦!😎 + +--- + +## 結語:井然有序的複雜 + +總的來說,Poetry 是一款優秀的套件管理工具,但並不像 pip 那般簡單、好上手。 + +使用 Poetry 來管理專案的套件與虛擬環境,需要一定的學習成本,但帶來的效益還是相當可觀的,尤其在你希望能夠乾淨且安心地移除套件之際,可謂莫它莫屬。 + +所以,別再猶豫,從今天起,加入 Poetry 的行列吧! + +### 參考 + +- [https://python-poetry.org/docs/](https://python-poetry.org/docs/) +- [https://github.com/python-poetry/poetry](https://github.com/python-poetry/poetry) +- [https://github.com/python-poetry/poetry/issues/3248](https://github.com/python-poetry/poetry/issues/3248) +- [https://github.com/python-poetry/poetry/issues/5185](https://github.com/python-poetry/poetry/issues/5185) +- [Python - 取代 Pipenv 的新套件管理器 Poetry](https://note.koko.guru/posts/using-poetry-manage-python-package-environments) +- [相比 Pipenv,Poetry 是一個更好的選擇](https://greyli.com/poetry-a-better-choice-than-pipenv/) +- [pip, pipenv 和 poetry 的選擇](https://shazi.info/pip-pipenv-%E5%92%8C-poetry-%E7%9A%84%E9%81%B8%E6%93%87/) +- [Dependency Management With Python Poetry](https://realpython.com/dependency-management-python-poetry/) +- [Ep 15. 和 PyPA 的成員聊聊 Python 開發工作流](https://pythonhunter.org/episodes/ep15) +- [Python - Poetry](https://blog.jihongo.com/posts/2022/06/04/python-poetry/) + +**相關文章** + +- ### [告別 Anaconda:在 macOS 上使用 pyenv 建立 Python 開發環境](https://blog.kyomind.tw/pyenv-setup/ "告別 Anaconda:在 macOS 上使用 pyenv 建立 Python 開發環境") +- ### [Ubuntu 安裝使用 pyenv + pyenv-virtualenv](https://blog.kyomind.tw/ubuntu-pyenv/ "Ubuntu 安裝使用 pyenv + pyenv-virtualenv") +- ### [pyproject.toml 介紹 + VS Code 整合 Black、yapf、isort 教學](https://blog.kyomind.tw/pyproject-toml/ "pyproject.toml 介紹 + VS Code 整合 Black、yapf、isort 教學") \ No newline at end of file diff --git a/05. 資料收集/Tool Setup/Software/Visual Studio Code.md b/05. 資料收集/Tool Setup/Software/Visual Studio Code.md index c0b8372..564f0b6 100644 --- a/05. 資料收集/Tool Setup/Software/Visual Studio Code.md +++ b/05. 資料收集/Tool Setup/Software/Visual Studio Code.md @@ -325,4 +325,7 @@ }, } -``` \ No newline at end of file +``` + +## 參考 +- [VSCode 是什么](https://geek-docs.com/vscode/vscode-tutorials/what-is-vscode.html) \ No newline at end of file diff --git a/05. 資料收集/Tool Setup/Software/Windows Setup.md b/05. 資料收集/Tool Setup/Software/Windows 10 Setup.md similarity index 100% rename from 05. 資料收集/Tool Setup/Software/Windows Setup.md rename to 05. 資料收集/Tool Setup/Software/Windows 10 Setup.md diff --git a/05. 資料收集/Tool Setup/Software/Windows 11 Setup.md b/05. 資料收集/Tool Setup/Software/Windows 11 Setup.md new file mode 100644 index 0000000..2313707 --- /dev/null +++ b/05. 資料收集/Tool Setup/Software/Windows 11 Setup.md @@ -0,0 +1,191 @@ +### Install tools +#### 先安裝 +- 手動安裝[Google Drive](https://www.google.com/drive/download/),以取得本檔案。 + +#### 自動安裝 +1. 安裝[Chocolatey](https://chocolatey.org/),用Administrator身份打開powershell,輸入下列指令: +``` +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) +``` +2. 用安裝常用的工具 +``` +choco install 7zip vscode hxd sublimetext4 androidstudio intellijidea-community git winmerge freefilesync freedownloadmanager gsudo firacode cascadiacode sourcecodepro delta --yes +``` + +列表: +- 7zip +- vscode +- hxd +- [sublimetext4](https://community.chocolatey.org/packages/sublimetext4/4.0.0.412100) +- androidstudio +- [intellijidea-community](https://chocolatey.org/packages/intellijidea-community) +- git +- winmerge +- [freefilesync](https://chocolatey.org/packages/freefilesync) +- [freedownloadmanager](https://chocolatey.org/packages/FreeDownloadManager) +- [gsudo](https://community.chocolatey.org/packages/gsudo) +- [firacode](https://community.chocolatey.org/packages/FiraCode) +- [cascadiacode](https://community.chocolatey.org/packages/cascadiacode) +- [sourcecodepro](https://community.chocolatey.org/packages/SourceCodePro) +- [delta](https://community.chocolatey.org/packages/delta) + +#### 手動安裝 +1. Google drive +2. Google drive(Logitech) +3. Python 3.6.3 +4. Python 3.9 +6. Visual Studio 2017 +7. Visual Studio 2019 +8. Office 365 +9. Lightroom +10. [Enpass](https://www.enpass.io/) +11. [ShareX](https://getsharex.com/) +12. [win32diskimager](https://sourceforge.net/projects/win32diskimager/) +13. [卡巴斯基](https://www.kaspersky.com.tw/) +14. 字型 + - [Caskaydia](\\diskstation\share\Tools\字型\Caskaydia Cove Nerd Font\) + +#### Portable App +1. Aegisub portable +2. Audacity 2.3.3 +3. Cheat Engine 7.0 +4. cmder v1.3.12 +5. ConEmu +6. ConvertZZ.1.0.0.3 +7. CrystalDiskMark 6.0.1 x64 +8. EzMeta +9. ffmpeg-2020-09-20-full_build +10. FileZillaPortable +11. Geek Uninstaller 1.4.7 +12. HxDPortable +13. ImgBurnPortable +14. IntelliJ IDEA +15. JDownloader 2.0 +16. PhraseExpress +17. Process Explorer 16.21 +18. Q-Dir 9.01 +19. Rufus +20. Sandboxie +21. [Speccy](https://www.ccleaner.com/speccy) +22. [ThunderbirdPortable](https://portableapps.com/apps/internet/thunderbird_portable) +23. [WindowGrid 1.3.11](http://windowgrid.net/) +24. [wiztree_3_35_portable](https://wiztreefree.com/download) + +### Upgrade +#### Upgrade by Chocolately +``` +choco upgrade all -y +``` + +### Setup +#### Setup doskey in **Command Prompt** +1. 切換到`Document`資料夾。 +2. 建立`cmdinit.cmd`,內容如下: +``` +@echo off +doskey sl="C:\Program Files\Sublime Text 3\sublime_text.exe" +doskey ll=dir +doskey rrp="cd C:\Python363\lib\site-packages\RobotRun" $T C: +doskey rra=cd "G:\My Drive\codes\Projects\RobotRunApplications" $T G: +doskey gpull=git pull origin master +doskey gpush=git push origin master +doskey gs=git status +doskey gd=git diff +doskey e.=explorer.exe . +``` + +#### Setup bashrc in **Git bash** +1. 打開`~/.bashrc`。 +2. 內容如下: +```bash +export PATH="/c/Users/ahuang11/AppData/Local/Android/Sdk/platform-tools:$PATH" + +##----- Android ----- +alias adb="/c/EasyAVEngine/Tools/Android/ADB/adb.exe" +alias ad='adb devices' +alias fastboot='/c/EasyAVEngine/Tools/Android/ADB/fastboot.exe' +alias fd='fastboot devices' + +##----- Logitech coding ----- +alias rrp='cd /c/Python363/lib/site-packages/RobotRun' +alias rra='cd /c/RobotRun' +alias rrd='cd /g/My\ Drive/codes/Projects/RobotRunDocs' +alias rro='cd /c/RobotRun/Output' +alias prj='cd /c/Users/awinh/OneDrive/codes/Logitech/project' +alias coderrp='cd "/c/Python363/lib/site-packages/RobotRun" ; code "/c/Python363/lib/site-packages/RobotRun"' +alias coderra='cd "/c/RobotRun"; code "/c/RobotRun"' +alias coderras='code "/d/GoogleDriveLogi/codes/Projects/RobotRunAutoServer"' + +##----- Awin coding ----- +alias codes='cd /c/Users/awinh/OneDrive/codes' +alias ctest='code "/g/My Drive/codes/test"' +alias jn='C:/Python310/Scripts/jupyter notebook' +alias ipy='C:/Python310/Scripts/ipython' + +##----- ELF ----- +alias hugo='~/OneDrive/文件/BLOG/Hugo/bin/0.98_extend/hugo.exe' + +##----- MISC ----- +alias sl="/c/Program\ Files/Sublime\ Text/subl.exe" +alias e.='explorer.exe .' +alias blog="cd ~/OneDrive/文件/BLOG/Hugo/Sites/blog.awin.one" +alias ffmpeg="/c/Users/awinh/OneDrive/codes/CommonLib/RobotRunCommonLib/ffmpeg-n5.0-latest-win64-lgpl-shared-5.0/bin/ffmpeg.exe" +alias ob="cd ~/OneDrive/文件/Obsidian/Main" + +##----- Connection ----- +alias gods='ssh awin@192.168.1.11' +alias gorp320='ssh pi@192.168.1.20' +alias gopve='ssh root@192.168.1.21' + +##----- Git ----- +alias gs="git status" +alias gd="git diff" +alias gpull='git pull origin master' +alias gpush='git push origin master' +alias gpushmain='git push origin main' +alias gc='git clone' +alias gclogi='git clone --config user.name="Awin Huang" --config user.email=ahuang11@logitech.com $@' + +##----- Python enviroment swich ----- +alias pyv='echo PY_PYTHON=$PY_PYTHON' + +function set_py() { + echo "Original Python verison is \"$PY_PYTHON\"" + export PY_PYTHON=$1 + echo " New Python verison is \"$PY_PYTHON\"" + + if [ ! -z "$2" ] + then + py "${@:2}" + fi +} + +function py36() { + set_py "3.6.3" "$@" +} + +function py310() { + set_py "3.10" "$@" +} + +``` + +#### Setup Windows Terminal + +1. 開啟Windows Terminal。 +2. 按`ctrl + ,`打開設定,之後參考[[Windows Terminal]]。 + +#### 恢復右鍵選單 +- 以admin身份打開PowerShell,執行 `reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve` +- 要恢復Windows 11的右鍵選單則是執行:`reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f` + +##### 來源 +- [How to open the full right-click menu by default on Windows 11](https://www.xda-developers.com/how-to-open-full-right-click-menu-by-default-windows-11/) + +#### Setup WSL2 +- [[安裝筆記] Windows 10 安裝 Linux 子系統 (WSL2) | Kenmingの鮮思維](http://www.kenming.idv.tw/note_window10_install_wsl2/) +- [[安裝筆記] Windows 10 WSL 2 安裝 Docker Desktop (含更改 Docker Image 路徑) | Kenmingの鮮思維](http://www.kenming.idv.tw/win10_wsl2_install_docker-desktop/) + +#### 其他細節 +- [【教學】Windows 11 系統優化](https://ofeyhong.pixnet.net/blog/post/225581177) +- \ No newline at end of file diff --git a/05. 資料收集/Tool Setup/Software/Windows Terminal.md b/05. 資料收集/Tool Setup/Software/Windows Terminal.md index b0d0ff6..ce10ef5 100644 --- a/05. 資料收集/Tool Setup/Software/Windows Terminal.md +++ b/05. 資料收集/Tool Setup/Software/Windows Terminal.md @@ -27,133 +27,346 @@ unset THEME ## Settings.json ```json -// This file was initially generated by Windows Terminal 1.6.10571.0 -// It should still be usable in newer versions, but newer versions might have additional -// settings, help text, or changes that you will not see unless you clear this file -// and let us generate a new one for you. - -// To view the default settings, hold "alt" while clicking on the "Settings" button. -// For documentation on these settings, see: https://aka.ms/terminal-documentation -// This file was initially generated by Windows Terminal 1.2.2381.0 -// It should still be usable in newer versions, but newer versions might have additional -// settings, help text, or changes that you will not see unless you clear this file -// and let us generate a new one for you. - -// To view the default settings, hold "alt" while clicking on the "Settings" button. -// For documentation on these settings, see: https://aka.ms/terminal-documentation { + "$help": "https://aka.ms/terminal-documentation", "$schema": "https://aka.ms/terminal-profiles-schema", - - "defaultProfile": "{00000000-0000-0000-ba54-000000000002}", - - // You can add more global application settings here. - // To learn more about global settings, visit https://aka.ms/terminal-global-settings - - // If enabled, selections are automatically copied to your clipboard. - "copyOnSelect": false, - - // If enabled, formatted data is also copied to your clipboard - "copyFormatting": false, - - // Start position - "initialCols": 205, - "initialRows": 30, - "initialPosition": "15,400", // x,y - - // A profile specifies a command to execute paired with information about how it should look and feel. - // Each one of them will appear in the 'New Tab' dropdown, - // and can be invoked from the commandline with `wt.exe -p xxx` - // To learn more about profiles, visit https://aka.ms/terminal-profile-settings - "profiles": - { - "defaults": + "actions": + [ { - // Put settings here that you want to apply to all profiles. + "command": + { + "action": "prevTab" + }, + "keys": "ctrl+pgup" }, - "list": + { + "command": + { + "action": "copy", + "singleLine": false + }, + "keys": "ctrl+c" + }, + { + "command": + { + "action": "splitPane", + "split": "right" + }, + "keys": "ctrl+shift+e" + }, + { + "command": "find", + "keys": "ctrl+shift+f" + }, + { + "command": "paste", + "keys": "ctrl+v" + }, + { + "command": + { + "action": "splitPane", + "split": "auto", + "splitMode": "duplicate" + }, + "keys": "alt+shift+d" + }, + { + "command": "toggleFullscreen", + "keys": "alt+x" + }, + { + "command": + { + "action": "newTab" + }, + "keys": "ctrl+t" + }, + { + "command": "closePane", + "keys": "ctrl+w" + }, + { + "command": + { + "action": "splitPane", + "split": "down" + }, + "keys": "ctrl+shift+o" + }, + { + "command": + { + "action": "nextTab" + }, + "keys": "ctrl+pgdn" + } + ], + "copyFormatting": "none", + "copyOnSelect": false, + // Start position + "initialCols": 234, + "initialRows": 30, + "initialPosition": "16,950", // x,y + "defaultProfile": "{00000000-0000-0000-ba54-000000000002}", + "profiles": + { + "defaults": {}, + "list": [ { - // Make changes here to the powershell.exe profile. "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", - "name": "Windows PowerShell", - "commandline": "powershell.exe", - "hidden": false - }, - { - "guid": "{41dd7a51-f0e1-4420-a2ec-1a7130b7e950}", - "name": "Windows PowerShell(Administrator)", - "commandline": "gsudo.exe powershell.exe", "hidden": false, - "colorScheme": "Solarized Dark", - "fontFace": "Fira Code", - "icon" : "C:\\Users\\awinh\\OneDrive\\圖片\\icon\\console_red.png" + "name": "Windows PowerShell" + }, + { + "colorScheme": "Solarized Dark", + "commandline": "gsudo.exe powershell.exe", + "font": + { + "face": "Fira Code" + }, + "guid": "{41dd7a51-f0e1-4420-a2ec-1a7130b7e950}", + "hidden": false, + "icon": "C:\\Users\\awinh\\OneDrive\\\u5716\u7247\\icon\\console_red.png", + "name": "Windows PowerShell(Administrator)" }, { - // Make changes here to the cmd.exe profile. "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}", - "name": "Command Prompt", - "commandline": "cmd.exe", - "hidden": false + "hidden": false, + "name": "Command Prompt" }, { "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}", - "name": "Azure Cloud Shell", - "source": "Windows.Terminal.Azure", "hidden": false, + "name": "Azure Cloud Shell", + "source": "Windows.Terminal.Azure" }, { - "guid": "{00000000-0000-0000-ba54-000000000002}", - "name" : "Bash", "commandline": "%PROGRAMFILES%/git/usr/bin/bash.exe -i -l", - "icon": "%PROGRAMFILES%/Git/mingw64/share/git/git-for-windows.ico", - "startingDirectory" : "D:\\GoogleDrive\\codes", "font": { "face": "CaskaydiaCove NF", "size": 12 }, - "historySize" : 9000, - }, + "guid": "{00000000-0000-0000-ba54-000000000002}", + "historySize": 9000, + "icon": "%PROGRAMFILES%/Git/mingw64/share/git/git-for-windows.ico", + "name": "Bash", + "startingDirectory": "C:\\Users\\awinh\\OneDrive\\codes" + } ] }, - - // Add custom color schemes to this array. - // To learn more about color schemes, visit https://aka.ms/terminal-color-schemes - "schemes": [], - - // Add custom keybindings to this array. - // To unbind a key combination from your defaults.json, set the command to "unbound". - // To learn more about keybindings, visit https://aka.ms/terminal-keybindings - "keybindings": + "schemes": [ - // Copy and paste are bound to Ctrl+Shift+C and Ctrl+Shift+V in your defaults.json. - // These two lines additionally bind them to Ctrl+C and Ctrl+V. - // To learn more about selection, visit https://aka.ms/terminal-selection - { "command": {"action": "copy", "singleLine": false }, "keys": "ctrl+c" }, - { "command": "paste", "keys": "ctrl+v" }, - - // Press Ctrl+Shift+F to open the search box - { "command": "find", "keys": "ctrl+shift+f" }, - - // Press Alt+Shift+D to open a new pane. - // - "split": "auto" makes this pane open in the direction that provides the most surface area. - // - "splitMode": "duplicate" makes the new pane use the focused pane's profile. - // To learn more about panes, visit https://aka.ms/terminal-panes - { "command": { "action": "splitPane", "split": "auto", "splitMode": "duplicate" }, "keys": "alt+shift+d" }, - // Full screen - { "command": "toggleFullscreen", "keys": "alt+x" }, - // Open new default tab - { "command": "newTab", "keys": "ctrl+t" }, - // Close current pane - { "command": "closePane", "keys": "ctrl+w" }, - // Split pane in vertical - { "command": { "action": "splitPane", "split": "vertical"}, "keys": "ctrl+shift+E" }, - // Split pane in horizontal - { "command": { "action": "splitPane", "split": "horizontal"}, "keys": "ctrl+shift+O" }, - // Goto next tab - { "command": "nextTab", "keys": "ctrl+pagedown" }, - // Goto previous tab - { "command": "prevTab", "keys": "ctrl+pageup" }, + { + "background": "#0C0C0C", + "black": "#0C0C0C", + "blue": "#0037DA", + "brightBlack": "#767676", + "brightBlue": "#3B78FF", + "brightCyan": "#61D6D6", + "brightGreen": "#16C60C", + "brightPurple": "#B4009E", + "brightRed": "#E74856", + "brightWhite": "#F2F2F2", + "brightYellow": "#F9F1A5", + "cursorColor": "#FFFFFF", + "cyan": "#3A96DD", + "foreground": "#CCCCCC", + "green": "#13A10E", + "name": "Campbell", + "purple": "#881798", + "red": "#C50F1F", + "selectionBackground": "#FFFFFF", + "white": "#CCCCCC", + "yellow": "#C19C00" + }, + { + "background": "#012456", + "black": "#0C0C0C", + "blue": "#0037DA", + "brightBlack": "#767676", + "brightBlue": "#3B78FF", + "brightCyan": "#61D6D6", + "brightGreen": "#16C60C", + "brightPurple": "#B4009E", + "brightRed": "#E74856", + "brightWhite": "#F2F2F2", + "brightYellow": "#F9F1A5", + "cursorColor": "#FFFFFF", + "cyan": "#3A96DD", + "foreground": "#CCCCCC", + "green": "#13A10E", + "name": "Campbell Powershell", + "purple": "#881798", + "red": "#C50F1F", + "selectionBackground": "#FFFFFF", + "white": "#CCCCCC", + "yellow": "#C19C00" + }, + { + "background": "#282C34", + "black": "#282C34", + "blue": "#61AFEF", + "brightBlack": "#5A6374", + "brightBlue": "#61AFEF", + "brightCyan": "#56B6C2", + "brightGreen": "#98C379", + "brightPurple": "#C678DD", + "brightRed": "#E06C75", + "brightWhite": "#DCDFE4", + "brightYellow": "#E5C07B", + "cursorColor": "#FFFFFF", + "cyan": "#56B6C2", + "foreground": "#DCDFE4", + "green": "#98C379", + "name": "One Half Dark", + "purple": "#C678DD", + "red": "#E06C75", + "selectionBackground": "#FFFFFF", + "white": "#DCDFE4", + "yellow": "#E5C07B" + }, + { + "background": "#FAFAFA", + "black": "#383A42", + "blue": "#0184BC", + "brightBlack": "#4F525D", + "brightBlue": "#61AFEF", + "brightCyan": "#56B5C1", + "brightGreen": "#98C379", + "brightPurple": "#C577DD", + "brightRed": "#DF6C75", + "brightWhite": "#FFFFFF", + "brightYellow": "#E4C07A", + "cursorColor": "#4F525D", + "cyan": "#0997B3", + "foreground": "#383A42", + "green": "#50A14F", + "name": "One Half Light", + "purple": "#A626A4", + "red": "#E45649", + "selectionBackground": "#FFFFFF", + "white": "#FAFAFA", + "yellow": "#C18301" + }, + { + "background": "#002B36", + "black": "#002B36", + "blue": "#268BD2", + "brightBlack": "#073642", + "brightBlue": "#839496", + "brightCyan": "#93A1A1", + "brightGreen": "#586E75", + "brightPurple": "#6C71C4", + "brightRed": "#CB4B16", + "brightWhite": "#FDF6E3", + "brightYellow": "#657B83", + "cursorColor": "#FFFFFF", + "cyan": "#2AA198", + "foreground": "#839496", + "green": "#859900", + "name": "Solarized Dark", + "purple": "#D33682", + "red": "#DC322F", + "selectionBackground": "#FFFFFF", + "white": "#EEE8D5", + "yellow": "#B58900" + }, + { + "background": "#FDF6E3", + "black": "#002B36", + "blue": "#268BD2", + "brightBlack": "#073642", + "brightBlue": "#839496", + "brightCyan": "#93A1A1", + "brightGreen": "#586E75", + "brightPurple": "#6C71C4", + "brightRed": "#CB4B16", + "brightWhite": "#FDF6E3", + "brightYellow": "#657B83", + "cursorColor": "#002B36", + "cyan": "#2AA198", + "foreground": "#657B83", + "green": "#859900", + "name": "Solarized Light", + "purple": "#D33682", + "red": "#DC322F", + "selectionBackground": "#FFFFFF", + "white": "#EEE8D5", + "yellow": "#B58900" + }, + { + "background": "#000000", + "black": "#000000", + "blue": "#3465A4", + "brightBlack": "#555753", + "brightBlue": "#729FCF", + "brightCyan": "#34E2E2", + "brightGreen": "#8AE234", + "brightPurple": "#AD7FA8", + "brightRed": "#EF2929", + "brightWhite": "#EEEEEC", + "brightYellow": "#FCE94F", + "cursorColor": "#FFFFFF", + "cyan": "#06989A", + "foreground": "#D3D7CF", + "green": "#4E9A06", + "name": "Tango Dark", + "purple": "#75507B", + "red": "#CC0000", + "selectionBackground": "#FFFFFF", + "white": "#D3D7CF", + "yellow": "#C4A000" + }, + { + "background": "#FFFFFF", + "black": "#000000", + "blue": "#3465A4", + "brightBlack": "#555753", + "brightBlue": "#729FCF", + "brightCyan": "#34E2E2", + "brightGreen": "#8AE234", + "brightPurple": "#AD7FA8", + "brightRed": "#EF2929", + "brightWhite": "#EEEEEC", + "brightYellow": "#FCE94F", + "cursorColor": "#000000", + "cyan": "#06989A", + "foreground": "#555753", + "green": "#4E9A06", + "name": "Tango Light", + "purple": "#75507B", + "red": "#CC0000", + "selectionBackground": "#FFFFFF", + "white": "#D3D7CF", + "yellow": "#C4A000" + }, + { + "background": "#000000", + "black": "#000000", + "blue": "#000080", + "brightBlack": "#808080", + "brightBlue": "#0000FF", + "brightCyan": "#00FFFF", + "brightGreen": "#00FF00", + "brightPurple": "#FF00FF", + "brightRed": "#FF0000", + "brightWhite": "#FFFFFF", + "brightYellow": "#FFFF00", + "cursorColor": "#FFFFFF", + "cyan": "#008080", + "foreground": "#C0C0C0", + "green": "#008000", + "name": "Vintage", + "purple": "#800080", + "red": "#800000", + "selectionBackground": "#FFFFFF", + "white": "#C0C0C0", + "yellow": "#808000" + } ] } ```