From 97dd06e296747f41d3662613c1dce07d91080039 Mon Sep 17 00:00:00 2001 From: Zane Meyers Date: Tue, 6 Jan 2026 17:28:27 -0500 Subject: [PATCH] vault backup: 2026-01-06 17:28:27 --- .obsidian/community-plugins.json | 3 +- .../plugins/spellcheck-toggler/data.json | 26 + .obsidian/plugins/spellcheck-toggler/main.js | 591 ++++++++++++++++++ .../plugins/spellcheck-toggler/manifest.json | 10 + .../plugins/spellcheck-toggler/styles.css | 38 ++ 2026-01-06.md | 87 ++- distribution-designations.md | 124 ++++ electrical-takeoff.md | 2 +- fixture-designations.md | 10 +- fixtures-takeoff.md | 2 +- fooled-by-randomness.md | 36 +- holt_2023_estimating.md | 13 + ...ustrated-guide-to-electrical-estimating.md | 73 ++- switchgear-takeoff.md | 97 +-- 14 files changed, 987 insertions(+), 125 deletions(-) create mode 100644 .obsidian/plugins/spellcheck-toggler/data.json create mode 100644 .obsidian/plugins/spellcheck-toggler/main.js create mode 100644 .obsidian/plugins/spellcheck-toggler/manifest.json create mode 100644 .obsidian/plugins/spellcheck-toggler/styles.css create mode 100644 distribution-designations.md create mode 100644 holt_2023_estimating.md diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index f9d7ec2..342aff5 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -18,5 +18,6 @@ "easy-copy", "anchor-display-text", "calendar", - "periodic-notes" + "periodic-notes", + "spellcheck-toggler" ] \ No newline at end of file diff --git a/.obsidian/plugins/spellcheck-toggler/data.json b/.obsidian/plugins/spellcheck-toggler/data.json new file mode 100644 index 0000000..2639088 --- /dev/null +++ b/.obsidian/plugins/spellcheck-toggler/data.json @@ -0,0 +1,26 @@ +{ + "externalLinks": { + "behaviour": "global" + }, + "internalLinks": { + "behaviour": "global" + }, + "bareLinks": { + "behaviour": "global" + }, + "htmlComments": { + "behaviour": "default" + }, + "anyNode": { + "behaviour": "default" + }, + "emphasis": { + "behaviour": "default" + }, + "strong": { + "behaviour": "default" + }, + "blockquote": { + "behaviour": "default" + } +} \ No newline at end of file diff --git a/.obsidian/plugins/spellcheck-toggler/main.js b/.obsidian/plugins/spellcheck-toggler/main.js new file mode 100644 index 0000000..e047e2d --- /dev/null +++ b/.obsidian/plugins/spellcheck-toggler/main.js @@ -0,0 +1,591 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// main.ts +var main_exports = {}; +__export(main_exports, { + default: () => SpellcheckTogglerPlugin +}); +module.exports = __toCommonJS(main_exports); + +// src/plugin.ts +var import_obsidian2 = require("obsidian"); + +// src/settings.ts +var import_obsidian = require("obsidian"); +var SpellcheckBehaviourOptionDisplay = { + ["default" /* DEFAULT */]: "Always spellcheck", + ["frontmatter" /* FRONTMATTER */]: "Use frontmatter", + ["global" /* GLOBAL */]: "Never spellcheck" +}; +var defaultSettings = { + externalLinks: { behaviour: "global" /* GLOBAL */ }, + internalLinks: { behaviour: "global" /* GLOBAL */ }, + bareLinks: { behaviour: "global" /* GLOBAL */ }, + htmlComments: { behaviour: "default" /* DEFAULT */ }, + anyNode: { behaviour: "default" /* DEFAULT */ }, + emphasis: { behaviour: "default" /* DEFAULT */ }, + strong: { behaviour: "default" /* DEFAULT */ }, + blockquote: { behaviour: "default" /* DEFAULT */ } +}; +var SpellcheckTogglerSettingTab = class extends import_obsidian.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + display() { + let { containerEl } = this; + containerEl.empty(); + const createSpellcheckOptionDisplay = (optionKey, name, description, targetFormat) => { + const settingContainer = containerEl.createDiv({ + cls: "settings-container" + }); + const headingSetting = new import_obsidian.Setting(settingContainer).setHeading().setName(name).setDesc(description); + if (targetFormat) { + const span = headingSetting.controlEl.createEl("span", { + text: `Targeted format: `, + cls: "setting-item-target-label" + }); + span.createEl("code", { + text: targetFormat, + cls: "setting-item-target-label" + }); + } + const frontmatterDrawer = settingContainer.createDiv({ + cls: "frontmatter-drawer" + }); + new import_obsidian.Setting(frontmatterDrawer).setName("Frontmatter override property").setDesc( + "Define the (boolean) frontmatter property which controls the spellcheck behaviour for a file." + ).addText( + (text) => { + var _a; + return text.setValue( + (_a = this.plugin.settings[optionKey].frontmatterOverride) != null ? _a : "" + ).onChange( + (frontmatterOverride) => this.plugin.saveSettings({ + [optionKey]: { + ...this.plugin.settings[optionKey], + frontmatterOverride: frontmatterOverride.length ? frontmatterOverride : void 0 + } + }) + ); + } + ); + new import_obsidian.Setting(frontmatterDrawer).setName("Fallback behavior").setDesc( + "Select the spellcheck fallback behavior for the node in files where the frontmatter property is absent." + ).addDropdown((dropdown) => { + var _a; + const { frontmatter, ...filteredOptions } = SpellcheckBehaviourOptionDisplay; + dropdown.addOptions({ ...filteredOptions }).onChange((frontmatterFallback) => { + this.plugin.saveSettings({ + [optionKey]: { + ...this.plugin.settings[optionKey], + frontmatterFallback + } + }); + }).setValue( + (_a = this.plugin.settings[optionKey].frontmatterFallback) != null ? _a : "default" /* DEFAULT */ + ); + }); + frontmatterDrawer.toggleClass( + "hidden", + [ + "default" /* DEFAULT */, + "global" /* GLOBAL */ + ].includes(this.plugin.settings[optionKey].behaviour) + ); + new import_obsidian.Setting(settingContainer).setName("Spellchecking behaviour").addDropdown( + (dropdown) => dropdown.addOptions(SpellcheckBehaviourOptionDisplay).onChange((behaviour) => { + this.plugin.saveSettings({ + [optionKey]: { + ...this.plugin.settings[optionKey], + behaviour + } + }); + frontmatterDrawer.toggleClass( + "hidden", + [ + "default" /* DEFAULT */, + "global" /* GLOBAL */ + ].includes( + this.plugin.settings[optionKey].behaviour + ) + ); + }).setValue(this.plugin.settings[optionKey].behaviour) + ); + settingContainer.appendChild(frontmatterDrawer); + }; + const legendContainer = containerEl.createDiv({ + cls: "settings-container" + }); + new import_obsidian.Setting(legendContainer).setHeading().setName("Spellcheck toggler settings").setDesc( + "Configure spellchecking options. The behaviour of a spellcheck option is defined as follows:" + ); + const list = legendContainer.createEl("ul"); + list.createEl("li", { + text: ` "Always spellcheck": the editor default behaviour will be applied (the plugin is not active for the option).`, + cls: "setting-item-target-label" + }); + list.createEl("li", { + text: ` "Use frontmatter": use the defined frontmatter property to toggle spellchecking per file.`, + cls: "setting-item-target-label" + }); + list.createEl("li", { + text: ` "Never spellcheck": do not use spellcheck in any file for the option.`, + cls: "setting-item-target-label" + }); + createSpellcheckOptionDisplay( + "externalLinks", + "Markdown links option", + "Toggle spellcheck underline for link text in any markdown link.", + "![text](link)" + ); + createSpellcheckOptionDisplay( + "internalLinks", + "Wikilinks option", + "Toggle spellcheck underline for link text in any wikilink.", + "[[ link text ]]" + ); + createSpellcheckOptionDisplay( + "bareLinks", + "Bare links option", + "Toggle spellcheck underline for text in any bare link.", + "[text]" + ); + createSpellcheckOptionDisplay( + "htmlComments", + "Html comment option", + "Toggle spellcheck underline for any text inside an html comment block.", + "<-- text -->" + ); + createSpellcheckOptionDisplay( + "blockquote", + "Blockquote node option", + "Toggle spellcheck for text inside block quotes.", + ">text" + ); + createSpellcheckOptionDisplay( + "emphasis", + "Italics (emphasis) node option", + "Toggle spellcheck for italics (emphasis) text.", + "*text*" + ); + createSpellcheckOptionDisplay( + "strong", + "Bolded (strong) node option", + "Toggle spellcheck for bolded (strong) text.", + "**text**" + ); + createSpellcheckOptionDisplay( + "anyNode", + "Per file option", + 'Toggle spellcheck for an entire file. With "Never spellcheck", spellchecking is disabled for all files.', + "*" + ); + } +}; + +// src/spellchecks/html-comments.ts +var import_view2 = require("@codemirror/view"); + +// src/spellchecks/apply-spellcheck-plugin.ts +var import_view = require("@codemirror/view"); +var import_state = require("@codemirror/state"); +var import_language = require("@codemirror/language"); + +// src/context.ts +var spellcheckContext = { + file: null, + frontmatter: null, + settings: defaultSettings, + uniqueOverrideKeys: [] +}; +var updateSpellcheckContext = (partialContext) => { + if ("frontmatter" in partialContext) { + updateFrontmatterWithDifference(partialContext.frontmatter); + delete partialContext["frontmatter"]; + } + if ("settings" in partialContext) { + spellcheckContext.uniqueOverrideKeys = Object.values( + partialContext.settings + ).reduce((set, option) => { + const override = option.frontmatterOverride; + if (override !== void 0) + set.push(override); + return set; + }, []); + } + spellcheckContext = { ...spellcheckContext, ...partialContext }; +}; +var updateFrontmatterWithDifference = (frontmatter) => { + if (frontmatter === null) { + spellcheckContext.frontmatter = null; + return true; + } + const frontmatterSpellcheckKeys = Object.keys(frontmatter).filter( + (k) => spellcheckContext.uniqueOverrideKeys.includes(k) + ); + const filteredFrontmatter = frontmatterSpellcheckKeys.reduce((ffm, k) => { + ffm[k] = frontmatter[k]; + return ffm; + }, {}); + if (spellcheckContext.frontmatter === null || Object.keys(spellcheckContext.frontmatter).length !== frontmatterSpellcheckKeys.length) { + spellcheckContext.frontmatter = filteredFrontmatter; + return true; + } + for (const key of frontmatterSpellcheckKeys) { + if (key in spellcheckContext.frontmatter && spellcheckContext.frontmatter[key] === frontmatter[key]) + continue; + spellcheckContext.frontmatter = filteredFrontmatter; + return true; + } + return false; +}; +var getSpellcheckContextProperty = (key) => spellcheckContext[key]; + +// src/spellchecks/apply-spellcheck-plugin.ts +var ApplySpellcheckAttributePluginValue = class { + constructor(view) { + this.decorations = this.buildDecorations(view); + } + update(update) { + if (!update.docChanged && !update.viewportChanged) + return; + this.decorations = this.buildDecorations(update.view); + } + buildDecorations(view) { + const builder = new import_state.RangeSetBuilder(); + const markSpellcheckFalse = import_view.Decoration.mark({ + attributes: { spellcheck: "false" } + }); + const adds = []; + const enter = (node) => { + var _a; + if ((_a = this.isNodeEligible) == null ? void 0 : _a.call(this, node)) + adds.push({ from: node.from, to: node.to }); + }; + for (let { from, to } of view.visibleRanges) + (0, import_language.syntaxTree)(view.state).iterate({ + from, + to, + enter + }); + adds.sort((a, b) => a.from - b.from); + for (const { from, to } of adds) + builder.add(from, to, markSpellcheckFalse); + return builder.finish(); + } +}; +var checkNodeEligibility = (settingsKey) => { + const frontmatter = getSpellcheckContextProperty("frontmatter"); + switch (getSpellcheckContextProperty("settings")[settingsKey].behaviour) { + case "default" /* DEFAULT */: + return false; + case "global" /* GLOBAL */: + return true; + case "frontmatter" /* FRONTMATTER */: + const override = getSpellcheckContextProperty("settings")[settingsKey].frontmatterOverride; + const isOverrideInFrontmatter = frontmatter !== null && override !== void 0 && override in frontmatter; + const fallback = getSpellcheckContextProperty("settings")[settingsKey].frontmatterFallback; + const isOverrideFalse = isOverrideInFrontmatter && frontmatter[override] === false; + const isFallbackDisable = !isOverrideInFrontmatter && fallback === "global" /* GLOBAL */; + return isOverrideFalse || isFallbackDisable; + } + return false; +}; + +// src/spellchecks/html-comments.ts +var HtmlCommentSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (!node.type.name.startsWith("comment")) + return false; + return checkNodeEligibility("htmlComments"); + } +}; +var htmlCommentSpellcheckPluginValue = import_view2.ViewPlugin.fromClass( + HtmlCommentSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/spellchecks/markdown-links.ts +var import_view3 = require("@codemirror/view"); +var MarkdownLinkSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (node.type.name.match(/^link|.*_link$/) === null || node.type.name.contains("hmd-barelink")) + return false; + return checkNodeEligibility("externalLinks"); + } +}; +var markdownLinkSpellcheckViewPlugin = import_view3.ViewPlugin.fromClass( + MarkdownLinkSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/spellchecks/wiki-links.ts +var import_view4 = require("@codemirror/view"); +var WikiLinkSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (!node.type.name.contains("hmd-internal-link")) + return false; + return checkNodeEligibility("internalLinks"); + } +}; +var wikiLinkSpellcheckViewPlugin = import_view4.ViewPlugin.fromClass( + WikiLinkSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/spellchecks/emphasis.ts +var import_view5 = require("@codemirror/view"); +var EmphasisSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (!node.type.name.startsWith("em")) + return false; + return checkNodeEligibility("emphasis"); + } +}; +var emphasisSpellcheckPluginValue = import_view5.ViewPlugin.fromClass( + EmphasisSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/spellchecks/strong.ts +var import_view6 = require("@codemirror/view"); +var StrongSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (!node.type.name.startsWith("strong")) + return false; + return checkNodeEligibility("strong"); + } +}; +var strongSpellcheckPluginValue = import_view6.ViewPlugin.fromClass( + StrongSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/migration/index.ts +var validateAndMigrateSettings = (settings) => { + if (settings === null) + return {}; + const keys1_1_0ToKeys1_2_0 = { + spellcheckExternalLinks: "externalLinks", + spellcheckInternalLinks: "internalLinks", + spellcheckHtmlComments: "htmlComments" + }; + for (const [srcKey, destKey] of Object.entries(keys1_1_0ToKeys1_2_0)) { + if (srcKey in settings && typeof settings[srcKey] === "boolean") { + settings[destKey] = { + behaviour: settings[srcKey] ? "default" /* DEFAULT */ : "global" /* GLOBAL */ + }; + delete settings[srcKey]; + } + } + for (const [spellcheckKey, spellcheckOption] of Object.entries(settings)) { + const behaviour = spellcheckOption["behaviour"]; + if (["opt-in", "opt-out"].includes(behaviour)) { + settings[spellcheckKey] = { + behaviour: "frontmatter" /* FRONTMATTER */, + frontmatterOverride: spellcheckOption["frontmatterOverride"], + frontmatterFallback: behaviour === "opt-in" ? "default" /* DEFAULT */ : "global" /* GLOBAL */ + }; + } + } + return { ...settings }; +}; + +// src/spellchecks/blockquote.ts +var import_view7 = require("@codemirror/view"); +var BlockQuoteSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (!node.type.name.startsWith("quote")) + return false; + return checkNodeEligibility("blockquote"); + } +}; +var blockQuoteSpellcheckPluginValue = import_view7.ViewPlugin.fromClass( + BlockQuoteSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/spellchecks/barelink.ts +var import_view8 = require("@codemirror/view"); +var BareLinkSpellcheckPluginValue = class extends ApplySpellcheckAttributePluginValue { + isNodeEligible(node) { + if (!node.type.name.contains("hmd-barelink_link")) + return false; + return checkNodeEligibility("bareLinks"); + } +}; +var bareLinkSpellcheckPluginValue = import_view8.ViewPlugin.fromClass( + BareLinkSpellcheckPluginValue, + { + decorations: (pluginValue) => pluginValue.decorations + } +); + +// src/plugin.ts +var SpellcheckTogglerPlugin = class extends import_obsidian2.Plugin { + constructor() { + super(...arguments); + this.editorExtensions = []; + } + async loadSettings() { + const userSettings = validateAndMigrateSettings(await this.loadData()); + this.settings = { ...defaultSettings, ...userSettings }; + this.saveData(this.settings); + updateSpellcheckContext({ settings: this.settings }); + } + async saveSettings(settings) { + this.settings = { ...this.settings, ...settings }; + await this.saveData(this.settings); + updateSpellcheckContext({ settings: this.settings }); + this.refreshExtensions(); + } + buildExtensions() { + this.editorExtensions.length = 0; + if (this.settings.internalLinks.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(wikiLinkSpellcheckViewPlugin); + if (this.settings.externalLinks.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(markdownLinkSpellcheckViewPlugin); + if (this.settings.bareLinks.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(bareLinkSpellcheckPluginValue); + if (this.settings.htmlComments.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(htmlCommentSpellcheckPluginValue); + if (this.settings.blockquote.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(blockQuoteSpellcheckPluginValue); + if (this.settings.emphasis.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(emphasisSpellcheckPluginValue); + if (this.settings.strong.behaviour !== "default" /* DEFAULT */) + this.editorExtensions.push(strongSpellcheckPluginValue); + } + refreshExtensions() { + var _a, _b; + this.buildExtensions(); + this.app.workspace.updateOptions(); + (_b = (_a = this.app.workspace.activeEditor) == null ? void 0 : _a.editor) == null ? void 0 : _b.refresh(); + } + handleSpellcheckAttribute() { + let globalSpellcheckFlag = true; + const anyNodeOption = getSpellcheckContextProperty("settings")["anyNode"]; + switch (anyNodeOption.behaviour) { + case "global" /* GLOBAL */: + globalSpellcheckFlag = false; + break; + case "frontmatter" /* FRONTMATTER */: + const frontmatter = getSpellcheckContextProperty("frontmatter"); + const isOverrideInFrontmatter = frontmatter !== null && anyNodeOption.frontmatterOverride !== void 0 && anyNodeOption.frontmatterOverride in frontmatter; + const isOverrideTrue = isOverrideInFrontmatter && frontmatter[anyNodeOption.frontmatterOverride] === true; + const isFallbackDefault = !isOverrideInFrontmatter && anyNodeOption.frontmatterFallback !== "global" /* GLOBAL */; + globalSpellcheckFlag = isOverrideTrue || isFallbackDefault; + break; + } + const content = this.app.workspace.containerEl.querySelector(".cm-content"); + const attributeObserver = new MutationObserver((_, observer) => { + content.setAttribute("spellcheck", String(globalSpellcheckFlag)); + observer.disconnect(); + }); + content.setAttribute("spellcheck", String(globalSpellcheckFlag)); + attributeObserver.observe(content, { + attributes: true, + attributeOldValue: true, + attributeFilter: ["spellcheck"] + }); + } + onFileOpen(file) { + var _a; + if (file === null) + return; + const metadata = this.app.metadataCache.getFileCache(file); + const frontmatter = (_a = metadata == null ? void 0 : metadata.frontmatter) != null ? _a : null; + updateSpellcheckContext({ + file, + frontmatter + }); + this.handleSpellcheckAttribute(); + } + onFileModify(file) { + var _a; + if (file === null || ((_a = this.app.workspace.getActiveFile()) == null ? void 0 : _a.basename) !== file.basename) + return; + (async () => { + var _a2; + const content = await this.app.vault.cachedRead(file); + const frontmatterInfo = (0, import_obsidian2.getFrontMatterInfo)(content); + if (!frontmatterInfo.exists) + return; + const overrideKeys = getSpellcheckContextProperty("uniqueOverrideKeys"); + const frontmatter = {}; + for (const line of frontmatterInfo.frontmatter.split("\n")) { + const [property, value] = line.split(":").map((s) => s.trim()); + if (value === void 0 || value.length === 0) + continue; + const parsedValue = value === "true" ? true : value === "false" ? false : void 0; + if (parsedValue !== void 0 && overrideKeys.includes(property)) + frontmatter[property] = parsedValue; + } + const didDiffer = updateFrontmatterWithDifference(frontmatter); + if (!didDiffer) + return; + this.handleSpellcheckAttribute(); + const editor = (_a2 = this.app.workspace.activeEditor) == null ? void 0 : _a2.editor; + if (!editor) + return; + editor.replaceRange(" ", editor.getCursor()); + editor.undo(); + })(); + } + async onload() { + await this.loadSettings(); + this.addSettingTab(new SpellcheckTogglerSettingTab(this.app, this)); + this.buildExtensions(); + this.registerEditorExtension(this.editorExtensions); + this.onFileOpen(this.app.workspace.getActiveFile()); + this.onFileOpenEventRef = this.app.workspace.on( + "file-open", + this.onFileOpen.bind(this) + ); + this.onFileModifyEventRef = this.app.vault.on( + "modify", + this.onFileModify.bind(this) + ); + } + unload() { + this.app.workspace.offref(this.onFileOpenEventRef); + this.app.vault.offref(this.onFileModifyEventRef); + super.unload(); + } +}; + +/* nosourcemap */ \ No newline at end of file diff --git a/.obsidian/plugins/spellcheck-toggler/manifest.json b/.obsidian/plugins/spellcheck-toggler/manifest.json new file mode 100644 index 0000000..d19d940 --- /dev/null +++ b/.obsidian/plugins/spellcheck-toggler/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "spellcheck-toggler", + "name": "Spellcheck Toggler", + "version": "1.4.3", + "minAppVersion": "1.5.3", + "description": "Toggle spellchecking for types of text blocks in the editing view.", + "author": "Julian Szachowicz", + "authorUrl": "https://github.com/julzerinos", + "isDesktopOnly": false +} \ No newline at end of file diff --git a/.obsidian/plugins/spellcheck-toggler/styles.css b/.obsidian/plugins/spellcheck-toggler/styles.css new file mode 100644 index 0000000..b0ec3d9 --- /dev/null +++ b/.obsidian/plugins/spellcheck-toggler/styles.css @@ -0,0 +1,38 @@ +.settings-container { + padding: 1rem 0; +} + +.setting-item { + border-top: 0px; + padding-top: .75rem; + padding-bottom: 0; +} + +.settings-container:has(.setting-item-heading):not(:nth-of-type(1)) { + border-top: 1px solid var(--background-modifier-border); +} + +.setting-item-heading { + display: flex; + flex-direction: column; + align-items: start; +} + +.setting-item-target-label { + font-weight: var(--font-normal); + color: var(--text-muted); + font-size: var(--font-ui-smaller); + padding-top: var(--size-4-1); + line-height: var(--line-height-tight); +} + +.hidden { + display: none; +} + +.frontmatter-drawer { + padding: 1rem; + margin-top: .5rem; + background-color: var(--color-base-25); + border-radius: 4px; +} \ No newline at end of file diff --git a/2026-01-06.md b/2026-01-06.md index 6f67264..56a27a7 100644 --- a/2026-01-06.md +++ b/2026-01-06.md @@ -1,7 +1,7 @@ --- id: aliases: [] -title: "2026-01-06" +title: 2026-01-06 tags: - authorship/original - destiny/permanent @@ -17,3 +17,88 @@ tags: 1. Person habitually engages in toxic behavior which they consciously believe to be benevolent 2. Person recognizes that when they stop engaging in said toxic behavior, others appreciate it 3. Person feels that they are doing a service by not engaging in said toxic behavior + +## 2026-01-06 10:00 + +Paraphrased Teams conversation with a peer +about [[distribution-designations#Terminations]]. +Peer's messages are in block quotes. + +### Content + +> Good morning! +> Do you add branch terminations to panelboards? +> If so, how does your 'distribution' section looks? + +Morning! Yes to the first question, ? to the second + +> As all panelboards on a job don't have the same size/# of terminations, +> I assume you would have a plethora of same size boards in the job, with different terms. +> How do you list them? + +| | Designation | Status | Quantity | +| --- | ----------------------------- | ------ | -------- | +| 1 | Generator - 350kW, Diesel | | 1 | +| 2 | Generator - 2000kW, Diesel | | 1 | +| 3 | ATS - 200A | | 2 | +| 4 | ATS - 250A | | 1 | +| 5 | ATS - 400A | | 2 | +| 6 | ATS - 600A | | 2 | +| 7 | ATS - 800A | | 1 | +| 8 | ATS - 1000A | | 1 | +| 9 | Panelboard - 50A, 1-Section | | 1 | +| 10 | Panelboard - 100A, 1-Section | | 18 | +| 11 | Panelboard - 125A, 1-Section | | 26 | +| 12 | Panelboard - 150A, 1-Section | | 2 | +| 13 | Panelboard - 225A, 1-Section | | 11 | +| 14 | Panelboard - 225A, 2-Section | | 5 | +| 15 | Panelboard - 400A, 1-Section | | 2 | +| 16 | Panelboard - 400A, 2-Section | | 3 | +| 17 | Panelboard - 400A, 3-Section | | 5 | +| 18 | Panelboard - 600A, 2-Section | | 1 | +| 19 | Panelboard - 800A, 1-Section | | 1 | +| 20 | Panelboard - 1200A, 1-Section | | 2 | +| 21 | Panelboard - 1200A, 2-Section | | 1 | +| 22 | Panelboard - 3000A, 1-Section | | 1 | +| 23 | CT Cabinet | | | +| 24 | Power Monitor | | 82 | +| 25 | SPD/TVSS | | 2 | + +> Ah, so you just chuck in a lot # of terms per board and call it good? + +Essentially. +Ben's direction was to pick a schedule on the upper end of terms for each size panel to be representative of the rest. +At my old place I would have made a designation for each panel, +I'm not upset to leave that behind. +I see no reason you couldn't just make each size once and keep it in a temp job. +I would, but I usually extract the schedules anyway so I just use the actual average. + +> Thank you for that. +> Joel wagged his finger at me about missing terms this morning--- +> I wanted to see how it's preferred. +> I was told terms are captured by feeders/mech connections +> and have never added them to panelboards. +> +> That would be the best way, finding the actual average. + +The mech connection assemblies have the load side term, +the feeder assemblies have both sides, +the branch assemblies don't have either, +that's why he got you, I think. + +I don't think my way is best unless you already have all the circuits in a big table for other reasons. +Lot of effort for a marginal increase in certainty above a heuristic like Ben's. + +I'm working on a job Noah started now, +he made certain panels separate from what I assume is the average +and labeled them with the name of the panel. +If I had a job where some were significantly more full than others +I _might_ do something similar, +but I'd probably name them "... 1-Section, ~25% Fill"/"... 1-Section, ~75% Fill" instead. +But that's a lot of mental overhead. + +## 2026-01-06 10:57 + +Tracking contractor growth by project square footage +rather than contract value dollars. +Account for market shifts and regional differences. diff --git a/distribution-designations.md b/distribution-designations.md new file mode 100644 index 0000000..8ba032e --- /dev/null +++ b/distribution-designations.md @@ -0,0 +1,124 @@ +--- +id: +aliases: [] +title: Distribution Designations +tags: + - authorship/original + - destiny/permanent + - occupational/takeoff + - status/draft + - type/guide +--- +# Distribution Designations + +## Naming + +> [!example] +> ``` +> Generator - 350kW, Diesel +> Generator - 2000kW, Diesel +> ATS - 200A +> ATS - 250A +> ATS - 400A +> ATS - 600A +> ATS - 800A +> ATS - 1000A +> Panelboard - 50A, 1-Section +> Panelboard - 100A, 1-Section +> Panelboard - 125A, 1-Section +> Panelboard - 150A, 1-Section +> Panelboard - 225A, 1-Section +> Panelboard - 225A, 2-Section +> Panelboard - 400A, 1-Section +> Panelboard - 400A, 2-Section +> Panelboard - 400A, 3-Section +> Panelboard - 600A, 2-Section +> Panelboard - 800A, 1-Section +> Panelboard - 1200A, 1-Section +> Panelboard - 1200A, 2-Section +> Panelboard - 3000A, 1-Section +> CT Cabinet +> Power Monitor +> SPD/TVSS +> ``` + +## Content + +### Terminations + +When building +* [[#Panelboards]] +* [[#Switchboards]] +add **branch terminations** according to an example from the schedules. +**Do not include feeder terminations.** + +## By Equipment Type + +> [!tip] When in Doubt +> If the equipment is not listed below, +> use a distribution panel of equal amps. + +### CT Cabinets + +??? + +### SPD's/TVSS's + +> Name = "SPD" + +Labor only. + +Specific item is not important, but I use +`SPLITTERS / SOCKETS / CABINETS`/`METER SOCKETS & CABINETS`/`METERING CURRENT TRANSFORMER` +(Labor = 1.5hrs) +for consistency. + +### Submeters/Power Monitors + +> Name = "Power Monitor" + +See [[switchgear#SPD's/TVSS's]] + +### Switchboards + +> Name = "Switchboard - 2000A, 4-Section" + +### Panelboards + +> Name = "Panelboard - 600A, 2-Section" + +`DISTRIBUTION`/`DIST PANELS & UNIT LOAD CENTERS`/... + +### Disconnects and Enclosed Circuit Breakers + +`COMMON ASSEMBLIES`/`DISTRIBUTION`/`MANUAL DISCONNECTS`/... + +### Generators + +> Name = "Generator - 250kW, Diesel" + +Add `DISTRIBUTION FITTINGS`/`GENERATOR SET UP`. + +### Generator Connection Cabinets + +> Name = "GCC - 200A" + +Use a Distribution Panel of equal amps. +Or a transfer switch. + +### Transfer Switches + +> Name = "Transfer Switch - 400A" + +#### Transfer Switches 1200A+ + +Use 1200A and add an additional `TRANSFER SWITCH` item +to make up the remaining amps. + +### Meter Centers + +> Name = "Meter Center - 800A, 16-Can" + +`SWITCHGEAR & METER CENTERS`/`METER CENTERS`/`... METERING SECTION` + +Adjust Factor 1 for meter stacks, delete zeroed items. diff --git a/electrical-takeoff.md b/electrical-takeoff.md index 9f25a77..fd8b6f8 100644 --- a/electrical-takeoff.md +++ b/electrical-takeoff.md @@ -40,7 +40,7 @@ add `ITEM DATABASE`/`WIRING & SYSTEM DEVICES`/`FLOOR BOXES & FITTTINGS`/`GENERIC ## Homeruns -### Lighting +### Lighting Homeruns * `Area` = "XX - Level XX Building" * `Phase` = "Building - Back of House (BOH)" diff --git a/fixture-designations.md b/fixture-designations.md index 6e4ec7f..e043e25 100644 --- a/fixture-designations.md +++ b/fixture-designations.md @@ -27,7 +27,7 @@ for use in [[fixtures-takeoff]]. > [!example] > `A - Surface (Pendant) = 2.0hrs - #12 MC+LV 20ft` -## Order +### Order Use empty designations to separate phases. @@ -49,7 +49,9 @@ Use empty designations to separate phases. > ... > ``` -## Fixture Labor +## Content + +### Fixture Labor | Fixture Type | Labor | |:------------ | ----------:| @@ -60,7 +62,7 @@ Use empty designations to separate phases. | Festoon | 2.0 hr/ea. | | All Others | 1.0 hr/ea. | -## Fixture Branch Length +### Fixture Branch Length | Case | Standard Length | | --------------------- | --------------- | @@ -78,7 +80,7 @@ Use empty designations to separate phases. Reduce as appropriate. -## Fixture Branch Wire size +### Fixture Branch Wire size | Case | Wire Size | | -------------- | --------- | diff --git a/fixtures-takeoff.md b/fixtures-takeoff.md index 55dafc5..fd2162c 100644 --- a/fixtures-takeoff.md +++ b/fixtures-takeoff.md @@ -12,7 +12,7 @@ tags: # Fixtures Takeoff This system is only for luminaires and their immediate branch wiring. -See also [[lighting-controls-takeoff]] and [[electrical-takeoff]]. +See also [[lighting-controls-takeoff]] and [[electrical-takeoff#Lighting Homeruns]]. [[fixture-designations]] diff --git a/fooled-by-randomness.md b/fooled-by-randomness.md index 6ee8e8c..47ebae1 100644 --- a/fooled-by-randomness.md +++ b/fooled-by-randomness.md @@ -13,6 +13,24 @@ tags: This is the commentary companion to [[taleb_2001_fooled-by-randomness]]. +## Review + +This is a low-effort bestseller bait +on the order of the lucky fools it (rightly) criticizes. +The only difference is that Warren Buffet is a successful trader. + +If you've read or are reading this book +and find yourself agreeing with any of its conclusions, +try _The Failure of Risk Management_ by Douglas W. Hubbard. +Hubbard dismantles Taleb's pessimism point by point, +and it's an excellent read besides. + +One might be tempted to say +that the damage Taleb and this book have done +to risk management and adjacent fields is immeasurable, +but I'm confident a rigorous model could estimate the damage +to an acceptable level of certainty. + ## Critiques ### Intellectual Insecurity @@ -28,16 +46,20 @@ relying on the strength of Taleb's logic alone, by his own stating. Taleb argues this strategy is perfectly legitimate, which is _true_, but it does not follow that it makes for the most robust argument. -In recent editions of the text, Taleb claims that his editors have implored him +In recent editions of the text, Taleb claims that his editors have _implored_ him to provide figures, graphs, studies, etc. as---_he agrees_---would be expected for any similar book on statistical phenomena, but he refuses. I don't find this approach charming at all, -especially considering how critical Taleb is of demagogues. +especially considering how critical Taleb is of demagogues like Warren Buffet, +who could write that their success was foretold burning oracle bones +and the book would still be a bestseller. +_Fooled by Randomness_ fails to differentiate itself from such books, +except in that it was written by a mediocre trader. Juxtaposing FbR with [[hubbard_2020_failure]], -which is a more traditional work of statistical thought, -well researched, and with a thorough bibliography, +which is a more traditional work of statistical thought--- +well researched, and with a thorough bibliography--- Taleb's arguments are considerably less satisfying. When Hubbard is wrong, it's clear his interpretation is flawed in that instance, when _Taleb_ is wrong, I question the foundation of all his arguments. @@ -91,7 +113,7 @@ much more so to dismiss the tests used to prove the validity of statistical meth ### False Lucky Fools Taleb repeatedly conflates legitimate lucky fools -with people with ideas he doesn't like. +and people with ideas he doesn't like. > [[hubbard_2020_failure]] > does a much better job of explaining "lucky fools" @@ -101,7 +123,7 @@ with people with ideas he doesn't like. Taleb uses the story of Nero Tulip, an incredibly cautious investor, -and his success over his risk-loving rival John +and his success over his risk-loving rival "John" to promote the idea that modern quantitative methods (as John is presumed to represent) are inherently flawed. @@ -116,6 +138,6 @@ much less that he is practicing modern portfolio theory. If I mistake Taleb's intent, and the story was only meant to convey that experienced, conscientious, and cautious decision-making -can lead a person to success, +_can_ lead a person to success, then I'm not sure who he's arguing against. diff --git a/holt_2023_estimating.md b/holt_2023_estimating.md new file mode 100644 index 0000000..9bdb5ed --- /dev/null +++ b/holt_2023_estimating.md @@ -0,0 +1,13 @@ +--- +id: +aliases: [] +title: "Mike Holt's Illustrated Guide to Electrical Estimating" +tags: [] +authors: + - Mike C. Holt +pages: 234 +publisher: Mike Holt Enterprises +type: book +year: 2023 +--- +# Mike Holt's Illustrated Guide to Electrical Estimating diff --git a/mike-holts-illustrated-guide-to-electrical-estimating.md b/mike-holts-illustrated-guide-to-electrical-estimating.md index 47250b2..b0d47bc 100644 --- a/mike-holts-illustrated-guide-to-electrical-estimating.md +++ b/mike-holts-illustrated-guide-to-electrical-estimating.md @@ -1,6 +1,7 @@ --- id: mike-holts-illustrated-guide-to-electrical-estimating aliases: [] +title: "_Mike Holt's Illustrated Guide to Electrical Estimating_" tags: - authorship/original - destiny/permanent @@ -8,44 +9,62 @@ tags: - topic/construction/electrical - topic/estimating - type/media-commentary -title: "_Mike Holt's Illustrated Guide to Electrical Estimating_" --- # _Mike Holt's Illustrated Guide to Electrical Estimating_ -```yaml -authors: - - Mike C. Holt -year: 2023 -title: "Mike Holt's Illustrated Guide to Electrical Estimating" -publisher: Mike Holt Enterprises -type: book -pages: 234 -``` +Commentary on [[holt_2023_estimating]]. -## Pros +## Goodreads Review -* Attractive design -* Detailed color illustrations -* Compelling topic organization and progression +_Mike Holt's Illustrated Guide to Electrical Estimating_, +like all of Holt's printed content I'm familiar with, +is a masterclass in construction education presentation +featuring attractive design, and plenty of detailed illustrations. +The bones of a great estimating textbook are present, +it has a compelling topic organization and progression, +however the content does not deliver on the promise. -## Cons +The text treats estimating like an invariable set of procedures: +(1) get the documents +(2) do the takeoff +(3) do the closeout +(4) send the bid; +**if** there's ambiguity in the documents, +**then** send an RFI. -* No mention of RFQ process +The reality of the industry is far messier than Holt would have you believe. -* Many/most topics are grossly oversimplified +What do you do when RFI's go unanswered? +Drop the job? -* Tone is undeservedly authoritative and principled, - refusing to acknowledge the questions of complexity - that the content itself begs +In fact, the text suggests that _all_ assumptions ==are bad== - > "Never make assumptions" - > What happens when RFI's go unanswered? +I was compelled by my employer to purchase this book +along with several others of Holt's +as part of my apprenticeship curriculum. +I can't shake the feeling that this entry +was intended to pad out the course to four years. -## Best Suited For +## Goodreads Review Scratch -* Electricians with no prior estimating experience and no real need of detail +Holt's tone is undeservedly authoritative and principled. + +It refuses to acknowledge the questions of complexity +that the content itself begs. +"Never make assumptions" + +Many topics are grossly oversimplified, +or omitted entirely (subcontractor RFQ process) + +This book is far too large in scope to lack the detail it does. + +It feels designed for those unlikely to need or understand its topics, +except it is far too long for a layman's introduction. + +If there will ever be a book to be what this one could not, +I don't think Holt would be the one to write it. +The group's content is very "teach to the book" +which is exactly what you want for contractor exams, +but not practical for estimating methodology. -## Overall -This book is far too large in scope to lack the detail it does. The curriculum -is clearly designed for those unlikely to need or understand its topics diff --git a/switchgear-takeoff.md b/switchgear-takeoff.md index f873743..c12b9d4 100644 --- a/switchgear-takeoff.md +++ b/switchgear-takeoff.md @@ -1,43 +1,22 @@ --- id: aliases: [] +title: Switchgear tags: - authorship/original - destiny/permanent - occupational/takeoff - status/draft - type/guide -title: Switchgear --- # Switchgear > [!info] > See [[distribution-equipment]]. -1. Build Items in Distribution: - * Panelboards - * Switchboards - * Meter centers - * Generators - * Transfer switches - * Generator connection cabinets - * CT cabinets - * Power monitor +1. Build [[distribution-designations|designations]] -2. Takeoff Common Assemblies: - * XFMRs - * Disconnect switches - -3. Takeoff Items in Item Database: - * Wireway (Trough/Gutter) - -4. Takeoff Temp Assemblies: - * Fire Pump - * Fire Pump Controller - -When building panelboards/switchboards, -add terminations according to an example from the schedules. -**Do not include feeder terminations.** +2. Takeoff riser scope, including designations and assemblies. > [!important] Scope to Exclude > * Tap Boxes @@ -47,69 +26,21 @@ add terminations according to an example from the schedules. > * Grounding > * Elevator Room -## By Equipment Type +## Takeoff -> [!tip] When in Doubt -> If the equipment is not listed below, -> use a distribution panel of equal amps. +* `Area` = _As shown_ +* `Phase` = "Switch Gear" +* `System` = "SWG - Switchgear" +* `Bid Item` = "3 - Building" -### SPD's/TVSS's +### Transformers -> Name = "SPD" +1. `COMMON ASSEMBLIES`/`DISTRIBUTION`/`TRANSFORMERS`/... -Labor only. +### Disconnects -Specific item is not important, but I use -`SPLITTERS / SOCKETS / CABINETS`/`METER SOCKETS & CABINETS`/`METERING CURRENT TRANSFORMER` -(Labor = 1.5hrs) -for consistency. +1. `COMMON ASSEMBLIES`/`DISTRIBUTION`/`MANUAL DISCONNECTS`/... -### Submeters/Power Monitors +### Wireway -> Name = "Power Monitor" - -See [[switchgear#SPD's/TVSS's]] - -### Switchboards - -> Name = "Switchboard - 2000A, 4-Section" - -### Panelboards - -> Name = "Panelboard - 600A, 2-Section" - -`DISTRIBUTION`/`DIST PANELS & UNIT LOAD CENTERS`/... - -### Disconnects and Enclosed Circuit Breakers - -`COMMON ASSEMBLIES`/`DISTRIBUTION`/`MANUAL DISCONNECTS`/... - -### Generators - -> Name = "Generator - 250kW, Diesel" - -Add `DISTRIBUTION FITTINGS`/`GENERATOR SET UP`. - -### Generator Connection Cabinets - -> Name = "GCC - 200A" - -Use a Distribution Panel of equal amps. -Or a transfer switch. - -### Transfer Switches - -> Name = "Transfer Switch - 400A" - -#### Transfer Switches 1200A+ - -Use 1200A and add an additional `TRANSFER SWITCH` item -to make up the remaining amps. - -### Meter Centers - -> Name = "Meter Center - 800A, 16-Can" - -`SWITCHGEAR & METER CENTERS`/`METER CENTERS`/`... METERING SECTION` - -Adjust Factor 1 for meter stacks, delete zeroed items. +1. `ITEM DATABASE`/???