vault backup: 2026-01-06 17:28:27

This commit is contained in:
2026-01-06 17:28:27 -05:00
parent d198d51c53
commit 97dd06e296
14 changed files with 987 additions and 125 deletions
+2 -1
View File
@@ -18,5 +18,6 @@
"easy-copy",
"anchor-display-text",
"calendar",
"periodic-notes"
"periodic-notes",
"spellcheck-toggler"
]
+26
View File
@@ -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"
}
}
+591
View File
@@ -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 */
+10
View File
@@ -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
}
+38
View File
@@ -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;
}
+86 -1
View File
@@ -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.
+124
View File
@@ -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.
+1 -1
View File
@@ -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)"
+6 -4
View File
@@ -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 |
| -------------- | --------- |
+1 -1
View File
@@ -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]]
+29 -7
View File
@@ -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.
+13
View File
@@ -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
@@ -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
+14 -83
View File
@@ -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`/???