Adding obsidian docs with initial notes

This commit is contained in:
2026-05-25 14:52:15 -04:00
parent 211429c54b
commit db8dfd6aba
51 changed files with 35217 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"alwaysUpdateLinks": true
}
+3
View File
@@ -0,0 +1,3 @@
{
"theme": "obsidian"
}
+7
View File
@@ -0,0 +1,7 @@
[
"kanban-bases-view",
"obsidian-note-autocreator",
"custom-font-loader",
"calendar",
"code-styler"
]
+33
View File
@@ -0,0 +1,33 @@
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"canvas": true,
"outgoing-link": true,
"tag-pane": true,
"footnotes": false,
"properties": true,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"bookmarks": true,
"markdown-importer": false,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": false,
"sync": true,
"bases": true,
"webviewer": false
}
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
{
"collapse-filter": true,
"search": "",
"showTags": false,
"showAttachments": false,
"hideUnresolved": false,
"showOrphans": true,
"collapse-color-groups": true,
"colorGroups": [],
"collapse-display": true,
"showArrow": false,
"textFadeMultiplier": 0,
"nodeSizeMultiplier": 1,
"lineSizeMultiplier": 1,
"collapse-forces": true,
"centerStrength": 0.518713248970312,
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1,
"close": true
}
+10
View File
@@ -0,0 +1,10 @@
{
"shouldConfirmBeforeCreate": true,
"weekStart": "locale",
"wordsPerDot": 250,
"showWeeklyNote": false,
"weeklyNoteFormat": "",
"weeklyNoteTemplate": "",
"weeklyNoteFolder": "",
"localeOverride": "system-default"
}
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
{
"id": "calendar",
"name": "Calendar",
"description": "Calendar view of your daily notes",
"version": "1.5.10",
"author": "Liam Cain",
"authorUrl": "https://github.com/liamcain/",
"isDesktopOnly": false,
"minAppVersion": "0.9.11"
}
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
{
"id": "code-styler",
"name": "Code Styler",
"version": "1.1.7",
"minAppVersion": "0.15.0",
"description": "Style and customize codeblocks and inline code in both editing mode and reading mode.",
"author": "Mayuran Visakan",
"authorUrl": "https://github.com/mayurankv",
"fundingUrl": "https://www.buymeacoffee.com/mayurankv2",
"isDesktopOnly": false
}
@@ -0,0 +1 @@
{}
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
{
"font_folder": ".obsidian/fonts/",
"font": "JetBrainsMono-Regular.ttf",
"force_mode": false,
"custom_css_mode": false,
"custom_css": ""
}
File diff suppressed because one or more lines are too long
+357
View File
@@ -0,0 +1,357 @@
/*
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: () => FontPlugin
});
module.exports = __toCommonJS(main_exports);
var import_obsidian = require("obsidian");
var DEFAULT_SETTINGS = {
font_folder: "",
font: "None",
force_mode: false,
custom_css_mode: false,
custom_css: ""
};
function get_default_css(font_family_name, css_class = ":root *") {
return `${css_class} {
--font-default: '${font_family_name}';
--default-font: '${font_family_name}';
--font-family-editor: '${font_family_name}';
--font-monospace-default: '${font_family_name}';
--font-interface-override: '${font_family_name}';
--font-text-override: '${font_family_name}';
--font-monospace-override: '${font_family_name}';
}
`;
}
function get_custom_css(font_family_name, css_class = ":root *") {
return `${css_class} * {
font-family: '${font_family_name}' !important;
}`;
}
function arrayBufferToBase64(buffer) {
let binary = "";
const bytes = new Uint8Array(buffer);
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
function applyCss(css, css_id, appendMode = false) {
const existingStyle = document.getElementById(css_id);
if (existingStyle && appendMode) {
existingStyle.innerHTML += css;
} else {
const style = document.createElement("style");
style.innerHTML = css;
document.head.appendChild(style);
if (existingStyle) {
existingStyle.remove();
}
style.id = css_id;
}
}
var FontPlugin = class extends import_obsidian.Plugin {
constructor() {
super(...arguments);
this.config_dir = this.app.vault.configDir;
this.plugin_folder_path = `${this.config_dir}/plugins/custom-font-loader`;
}
async load_plugin() {
await this.loadSettings();
try {
const font_file_name = this.settings.font;
if (font_file_name && font_file_name.toLowerCase() != "none") {
if (font_file_name != "all") {
await this.process_and_load_font(font_file_name, false);
} else {
applyCss("", "custom_font_base64");
const files = await this.app.vault.adapter.list(
this.settings.font_folder
);
for (const file of files.files) {
const file_name = file.replace(
this.settings.font_folder,
""
);
await this.process_and_load_font(file_name, true);
}
}
} else {
applyCss("", "custom_font_base64");
applyCss("", "custom_font_general");
}
} catch (error) {
new import_obsidian.Notice(error);
}
}
async process_and_load_font(font_file_name, load_all_fonts) {
console.log("loading %s", font_file_name);
const css_font_path = `${this.plugin_folder_path}/${font_file_name.toLowerCase().replace(".", "_")}.css`;
if (!await this.app.vault.adapter.exists(css_font_path)) {
await this.convert_font_to_css(font_file_name, css_font_path);
} else {
await this.load_font(css_font_path, load_all_fonts);
await this.load_css(font_file_name);
}
}
async load_font(css_font_path, appendMode) {
const content = await this.app.vault.adapter.read(css_font_path);
applyCss(content, "custom_font_base64", appendMode);
}
async load_css(font_file_name) {
let css_string = "";
const font_family_name = font_file_name.split(".")[0].toLowerCase();
if (this.settings.custom_css_mode) {
css_string = this.settings.custom_css;
} else {
css_string = get_default_css(font_family_name);
}
if (this.settings.force_mode)
css_string += `
* {
font-family: '${font_family_name}' !important;
}
`;
applyCss(css_string, "custom_font_general");
}
async convert_font_to_css(font_file_name, css_font_path) {
new import_obsidian.Notice("Processing Font files");
const file = `${this.settings.font_folder}/${font_file_name}`;
const arrayBuffer = await this.app.vault.adapter.readBinary(file);
const base64 = arrayBufferToBase64(arrayBuffer);
const font_family_name = font_file_name.split(".")[0].toLowerCase();
const font_extension_name = font_file_name.split(".")[1].toLowerCase();
let css_type = "";
switch (font_extension_name) {
case "woff":
css_type = "font/woff";
break;
case "ttf":
css_type = "font/truetype";
break;
case "woff2":
css_type = "font/woff2";
break;
case "otf":
css_type = "font/opentype";
break;
default:
css_type = "font";
}
const base64_css = `@font-face{
font-family: '${font_family_name}';
src: url(data:${css_type};base64,${base64});
}`;
this.app.vault.adapter.write(css_font_path, base64_css);
console.log("saved font %s into %s", font_family_name, css_font_path);
console.log("Font CSS Saved into %s", css_font_path);
await this.load_plugin();
}
async onload() {
this.load_plugin();
this.addSettingTab(new FontSettingTab(this.app, this));
}
async onunload() {
applyCss("", "custom_font_base64");
applyCss("", "custom_font_general");
}
async loadSettings() {
this.settings = Object.assign(
{},
DEFAULT_SETTINGS,
await this.loadData()
);
}
async saveSettings() {
await this.saveData(this.settings);
}
};
var FontSettingTab = class extends import_obsidian.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
async display() {
const { containerEl } = this;
containerEl.empty();
const infoContainer = containerEl.createDiv();
infoContainer.setText(
"In Order to set the font, copy your font into fonts directory that you set"
);
new import_obsidian.Setting(containerEl).setName("Fonts Folder").setDesc("Folder to look for your custom fonts").addText((text) => {
text.onChange(async (value) => {
this.plugin.settings.font_folder = value;
await this.plugin.saveSettings();
await this.plugin.loadSettings();
});
if (this.plugin.settings.font_folder.trim() == "") {
this.plugin.settings.font_folder = `${this.app.vault.configDir}/fonts`;
}
if (!this.plugin.settings.font_folder.endsWith("/"))
this.plugin.settings.font_folder = this.plugin.settings.font_folder + "/";
text.setValue(this.plugin.settings.font_folder);
});
const font_folder_path = this.plugin.settings.font_folder;
const options = [{ name: "none", value: "None" }];
try {
if (!await this.app.vault.adapter.exists(font_folder_path)) {
await this.app.vault.adapter.mkdir(font_folder_path);
}
if (await this.app.vault.adapter.exists(font_folder_path)) {
const files = await this.app.vault.adapter.list(
font_folder_path
);
for (const file of files.files) {
const file_name = file.replace(font_folder_path, "");
if (file_name.startsWith("."))
continue;
options.push({ name: file_name, value: file_name });
}
}
options.push({ name: "all", value: "Multiple fonts" });
} catch (error) {
console.log(error);
}
new import_obsidian.Setting(containerEl).setName("Reload fonts from folder").setDesc(
"This button reloades from the folder you specified (it also creates the folder for you)"
).addButton((button) => {
button.setButtonText("Reload");
button.onClick((callback) => {
this.plugin.saveSettings();
this.plugin.load_plugin();
this.display();
});
});
this.containerEl.createDiv();
new import_obsidian.Setting(containerEl).setName("Font").setDesc(
`Choose font (If you can't see your fonts, make sure your fonts are in the folder you specified and hit reload.
Also if you choose multiple fonts option, we will load and process all fonts in the folder for you. In that Case, enable Custom CSS Mode)`
).addDropdown((dropdown) => {
for (const opt of options) {
dropdown.addOption(opt.name, opt.value);
}
dropdown.setValue(this.plugin.settings.font).onChange(async (value) => {
this.plugin.settings.font = value;
await this.plugin.saveSettings();
await this.plugin.load_plugin();
this.display();
});
});
if (this.plugin.settings.font.toLowerCase() != "none") {
new import_obsidian.Setting(containerEl).setName("Force Style").setDesc(
"This option should only be used if you have installed a community theme and normal mode doesn't work"
).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.force_mode);
toggle.onChange(async (value) => {
this.plugin.settings.force_mode = value;
await this.plugin.saveSettings();
await this.plugin.load_plugin();
});
});
new import_obsidian.Setting(containerEl).setName("Custom CSS Mode").setDesc(
"If you want to apply a custom css style rather than default style, choose this."
).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.custom_css_mode);
toggle.onChange(async (value) => {
if (this.plugin.settings.custom_css_mode == false) {
this.plugin.settings.custom_css = "";
}
this.plugin.settings.custom_css_mode = value;
this.plugin.saveSettings();
this.plugin.load_plugin();
this.display();
});
});
if (this.plugin.settings.custom_css_mode) {
new import_obsidian.Setting(containerEl).setName("Custom CSS Style").setDesc("Input your custom css style. Use the font filename without extension (in lowercase) as the font-family name. For example, if your font file is 'MyFont.ttf', use 'myfont' in your CSS.").addTextArea(async (text) => {
text.onChange(async (new_value) => {
this.plugin.settings.custom_css = new_value;
await this.plugin.saveSettings();
await this.plugin.load_plugin();
});
text.setDisabled(!this.plugin.settings.custom_css_mode);
if (this.plugin.settings.custom_css == "") {
let font_family_name = "";
try {
font_family_name = this.plugin.settings.font.split(".")[0].toLowerCase();
} catch (error) {
console.log(error);
}
if (font_family_name == "all") {
if (await this.app.vault.adapter.exists(
font_folder_path
)) {
const files = await this.app.vault.adapter.list(
font_folder_path
);
let final_str = "";
for (const file of files.files) {
const file_name = file.split("/")[2];
const font_family = file_name.split(".")[0].toLowerCase();
final_str += "\n" + get_custom_css(
font_family,
"." + font_family
);
}
text.setValue(final_str);
}
} else {
const template = `/* Example CSS for your font: ${font_family_name} */
/* Apply to all text */
:root * {
--font-default: '${font_family_name}';
--default-font: '${font_family_name}';
--font-family-editor: '${font_family_name}';
--font-interface-override: '${font_family_name}';
--font-text-override: '${font_family_name}';
}
/* Example: Apply to custom CSS class */
.custom-font * {
font-family: '${font_family_name}' !important;
}
/* Example: Apply to specific elements only */
.custom-font h1, .custom-font h2, .custom-font h3 {
font-family: '${font_family_name}' !important;
}`;
text.setValue(template);
}
} else {
text.setValue(this.plugin.settings.custom_css);
}
text.onChanged();
text.inputEl.style.width = "100%";
text.inputEl.style.height = "100px";
});
}
}
}
};
/* nosourcemap */
+10
View File
@@ -0,0 +1,10 @@
{
"id": "custom-font-loader",
"name": "Custom Font Loader",
"version": "1.8.0",
"minAppVersion": "0.15.0",
"description": "Customize your Obsidian vault with any font you want (+ Support for Android and IOS)",
"author": "Amir Pourmand",
"authorUrl": "https://amirpourmand.ir",
"isDesktopOnly": false
}
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
{
"id": "kanban-bases-view",
"name": "Kanban Bases View",
"version": "0.10.0",
"minAppVersion": "1.10.2",
"description": "A kanban-style drag-and-drop custom view for Bases.",
"author": "I. Welch Canavan",
"authorUrl": "https://welchcanavan.com",
"isDesktopOnly": false
}
+617
View File
@@ -0,0 +1,617 @@
/* Kanban View Container */
.obk-view-container {
container-type: inline-size;
height: 100%;
display: flex;
flex-direction: column;
padding: 10px;
overflow: hidden;
}
/* Property Selector */
.obk-property-selector {
margin-bottom: 15px;
padding: 10px;
background: var(--background-secondary);
border-radius: 6px;
display: flex;
align-items: center;
gap: 10px;
}
.obk-property-label {
font-weight: 500;
color: var(--text-normal);
}
.obk-property-select {
padding: 6px 12px;
border: 1px solid var(--background-modifier-border);
border-radius: 4px;
background: var(--background-primary);
color: var(--text-normal);
font-size: 14px;
cursor: pointer;
}
.obk-property-select:hover {
border-color: var(--interactive-hover);
}
.obk-property-select:focus {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
}
/* Empty State */
.obk-empty-state {
padding: 40px 20px;
text-align: center;
color: var(--text-muted);
font-style: italic;
}
/* Kanban Board */
.obk-board {
display: flex;
gap: 15px;
overflow-x: auto;
overflow-y: hidden;
flex: 1;
padding-bottom: 10px;
}
.obk-board::-webkit-scrollbar {
height: 8px;
}
.obk-board::-webkit-scrollbar-track {
background: var(--background-secondary);
border-radius: 4px;
}
.obk-board::-webkit-scrollbar-thumb {
background: var(--background-modifier-border);
border-radius: 4px;
}
.obk-board::-webkit-scrollbar-thumb:hover {
background: var(--background-modifier-border-hover);
}
/* Swimlane mode: stack lanes vertically. The lane body becomes the
horizontal column flex (replacing what .obk-board does in flat mode). */
.obk-board--with-swimlanes {
flex-direction: column;
overflow-x: hidden;
overflow-y: auto;
gap: 12px;
}
.obk-swimlane {
display: flex;
flex-direction: column;
background: var(--background-secondary-alt);
border-radius: 8px;
border: 1px solid var(--background-modifier-border);
overflow: hidden;
}
.obk-swimlane-header {
padding: 8px 14px;
background: var(--background-primary-alt);
border-bottom: 1px solid var(--background-modifier-border);
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
font-size: 13px;
color: var(--text-normal);
text-transform: capitalize;
flex-shrink: 0;
}
.obk-swimlane-count {
font-size: 12px;
color: var(--text-muted);
background: var(--background-modifier-border);
padding: 2px 8px;
border-radius: 12px;
font-weight: 500;
}
.obk-swimlane-body {
display: flex;
align-items: stretch;
gap: 12px;
overflow-x: auto;
overflow-y: visible;
padding: 12px;
min-height: 140px;
}
/* In swimlane mode, each lane grows tall enough to fit the fullest column,
and shorter column bodies stretch to that height so their Sortable drop
target spans the whole lane row. */
.obk-board--with-swimlanes .obk-column {
min-height: 0;
max-height: none;
height: auto;
overflow: visible;
align-self: stretch;
}
.obk-board--with-swimlanes .obk-column-body {
flex: 1 1 auto;
max-height: none;
overflow-y: visible;
min-height: 0;
}
/* The outer container caps height in flat mode; release it in swimlane mode
so the board grows to fit all lanes and the parent scroll-area scrolls. */
.obk-view-container--with-swimlanes {
overflow: visible;
height: auto;
}
/* Collapsed lane: cap the column body at about 30% less than the original
420px height and scroll within the
column. The lane and column themselves stay flexible — only the card
container is capped. */
.obk-swimlane--collapsed .obk-column-body {
max-height: 294px;
overflow-y: auto;
}
.obk-swimlane-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
color: var(--text-muted);
background: var(--background-modifier-border);
border: 0;
border-radius: 4px;
cursor: pointer;
user-select: none;
--icon-size: var(--icon-xs);
transition:
background 0.1s ease,
color 0.1s ease;
}
.obk-swimlane-toggle:hover {
color: var(--text-normal);
background: var(--background-modifier-border-hover);
}
.obk-swimlane-toggle:focus-visible {
outline: 2px solid var(--background-modifier-border-focus);
outline-offset: 2px;
}
.obk-swimlane-drag-handle {
cursor: grab;
padding: 2px 4px;
opacity: 0.5;
user-select: none;
font-size: 14px;
line-height: 1;
color: var(--text-muted);
display: flex;
align-items: center;
flex-shrink: 0;
}
.obk-swimlane-drag-handle:hover {
opacity: 1;
color: var(--text-normal);
}
.obk-swimlane-drag-handle:active {
cursor: grabbing;
}
.obk-swimlane-dragging {
opacity: 0.5;
}
.obk-swimlane-ghost {
opacity: 0.3;
background: var(--background-modifier-border);
}
.obk-swimlane-body::-webkit-scrollbar {
height: 8px;
}
.obk-swimlane-body::-webkit-scrollbar-track {
background: transparent;
}
.obk-swimlane-body::-webkit-scrollbar-thumb {
background: var(--background-modifier-border);
border-radius: 4px;
}
.obk-swimlane-body::-webkit-scrollbar-thumb:hover {
background: var(--background-modifier-border-hover);
}
/* Kanban Column */
.obk-column {
--obk-column-accent-color: transparent;
flex: 0 0 clamp(200px, 60cqw, 280px);
display: flex;
flex-direction: column;
background: var(--background-secondary);
border-radius: 8px;
border: 1px solid var(--background-modifier-border);
min-height: 200px;
max-height: 100%;
overflow: hidden;
}
.obk-column-header {
padding: 12px 16px;
background: color-mix(in srgb, var(--obk-column-accent-color, transparent) 15%, var(--background-primary-alt));
border-bottom: 1px solid var(--background-modifier-border);
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
gap: 8px;
}
/* Column color picker button */
.obk-column-color-btn {
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid var(--background-modifier-border);
background: var(--obk-column-accent-color, transparent);
cursor: pointer;
flex-shrink: 0;
transition:
transform 0.1s ease,
border-color 0.1s ease;
}
.obk-column-color-btn:hover {
border-color: var(--text-muted);
transform: scale(1.15);
}
/* Color picker popover */
.obk-column-color-popover {
position: fixed;
background: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: 8px;
padding: 8px;
display: flex;
gap: 6px;
flex-wrap: wrap;
width: 164px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 100;
}
.obk-column-color-swatch {
width: 20px;
height: 20px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
transition:
transform 0.1s ease,
border-color 0.1s ease;
}
.obk-column-color-swatch:hover {
transform: scale(1.2);
}
.obk-column-color-swatch--active {
border-color: var(--text-normal);
}
.obk-column-color-none {
background: var(--background-modifier-border);
position: relative;
}
.obk-column-color-none::before,
.obk-column-color-none::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 1.5px;
background: var(--text-muted);
border-radius: 1px;
transform-origin: center;
}
.obk-column-color-none::before {
transform: translate(-50%, -50%) rotate(45deg);
}
.obk-column-color-none::after {
transform: translate(-50%, -50%) rotate(-45deg);
}
.obk-column-drag-handle {
cursor: grab;
padding: 4px;
opacity: 0.5;
user-select: none;
font-size: 16px;
line-height: 1;
color: var(--text-muted);
display: flex;
align-items: center;
}
.obk-column-drag-handle:hover {
opacity: 1;
color: var(--text-normal);
}
.obk-column-drag-handle:active {
cursor: grabbing;
}
.obk-column-title {
flex: 1;
font-weight: 600;
font-size: 14px;
color: var(--text-normal);
text-transform: capitalize;
}
.obk-column-count {
font-size: 12px;
color: var(--text-muted);
background: color-mix(in srgb, var(--obk-column-accent-color, transparent) 15%, var(--background-modifier-border));
padding: 2px 8px;
border-radius: 12px;
}
.obk-column-add-btn,
.obk-column-remove-btn {
width: 24px;
height: 24px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
cursor: pointer;
flex-shrink: 0;
transition:
background 0.1s ease,
color 0.1s ease,
opacity 0.1s ease;
}
.obk-column-add-btn {
opacity: 0.55;
}
.obk-column:hover .obk-column-add-btn,
.obk-column-add-btn:focus-visible {
opacity: 1;
}
.obk-column-add-btn:hover,
.obk-column-remove-btn:hover {
color: var(--text-normal);
background: var(--background-modifier-hover);
}
.obk-column-add-btn .svg-icon {
width: 16px;
height: 16px;
}
.obk-column-remove-btn {
font-size: 18px;
line-height: 1;
}
.obk-column-body {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 10px;
min-height: 0;
}
.obk-column-body::-webkit-scrollbar {
width: 6px;
}
.obk-column-body::-webkit-scrollbar-track {
background: transparent;
}
.obk-column-body::-webkit-scrollbar-thumb {
background: var(--background-modifier-border);
border-radius: 3px;
}
.obk-column-body::-webkit-scrollbar-thumb:hover {
background: var(--background-modifier-border-hover);
}
/* Kanban Card */
.obk-card {
background: var(--background-primary);
border: 1px solid
color-mix(in srgb, var(--obk-column-accent-color, transparent) 15%, var(--background-modifier-border));
border-radius: 6px;
padding: 12px;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* targets touch-first devices (tablets, phones) to make the kanban genuinely usable on
touch screens — any-pointer: coarse alone would also match hybrid devices (e.g.
touchscreen laptops) where the primary pointer is still a mouse */
@media (any-pointer: coarse) and (hover: none) {
.obk-card {
user-select: none;
-webkit-user-select: none;
}
}
.obk-card--hover {
border-color: var(--interactive-accent);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
.obk-card--active {
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 25%, transparent);
}
.obk-card-cover {
display: block;
/* Bleed the cover to the card's inner border edge. Card has padding: 12px,
so we expand the width by 24px and pull the box out with negative margins.
width: 100% alone only fills the content box and leaves a 12px gap on each side. */
width: calc(100% + 24px);
margin: -12px -12px 8px -12px;
/* aspect-ratio is set inline from the imageAspectRatio config */
overflow: hidden;
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.obk-card-cover img {
width: 100%;
height: 100%;
display: block;
}
.obk-card-cover--fit-cover img {
object-fit: cover;
}
.obk-card-cover--fit-contain img {
object-fit: contain;
background: var(--background-secondary);
}
.obk-card-title {
font-weight: 500;
font-size: 14px;
color: var(--text-normal);
line-height: 1.4;
word-wrap: break-word;
}
.obk-card-preview {
font-size: 12px;
color: var(--text-muted);
line-height: 1.4;
margin-top: 6px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.obk-card-property {
font-size: var(--font-ui-smaller);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: flex;
gap: 6px;
margin-top: 4px;
}
.obk-card-property-wrap {
white-space: normal;
text-overflow: initial;
}
.obk-card-property-wrap .obk-card-property-value {
white-space: normal;
text-overflow: initial;
}
.obk-card-property-label {
color: var(--text-muted);
flex-shrink: 0;
}
.obk-card-property-value {
overflow: hidden;
text-overflow: ellipsis;
}
.obk-card-property-value p {
display: inline;
margin: 0;
}
.obk-quick-add-form {
display: flex;
flex-direction: column;
gap: 14px;
}
.obk-quick-add-input {
width: 100%;
}
.obk-quick-add-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
/* Drag and Drop States */
.obk-card-dragging {
opacity: 0.5;
transform: rotate(2deg);
}
.obk-card-ghost {
opacity: 0.3;
background: var(--interactive-accent);
border-color: var(--interactive-accent);
}
.obk-card-chosen {
cursor: grabbing;
transform: rotate(2deg);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
/* Column Drag and Drop States */
.obk-column-dragging {
opacity: 0.5;
}
.obk-column-ghost {
opacity: 0.3;
background: var(--background-modifier-border);
}
/* Sortable placeholder */
.obk-sortable-ghost {
opacity: 0.4;
background: var(--interactive-accent);
border: 2px dashed var(--interactive-accent);
border-radius: 6px;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
{
"id": "obsidian-note-autocreator",
"name": "Note Auto Creator",
"version": "1.6.0",
"minAppVersion": "0.14.2",
"description": "Automatically create notes when links are created to them.",
"author": "Simon T. Clement",
"authorUrl": "https://github.com/SimonTC",
"isDesktopOnly": false
}
@@ -0,0 +1,3 @@
.setting-item-control.setting-warning input {
border-color: yellow;
}
+266
View File
@@ -0,0 +1,266 @@
{
"main": {
"id": "ab0a61f00f01b42e",
"type": "split",
"children": [
{
"id": "330f0f204ec3f22e",
"type": "tabs",
"children": [
{
"id": "7a5b3ed118d43e9a",
"type": "leaf",
"state": {
"type": "bases",
"state": {
"file": "_Tasks Kanban.base",
"viewName": "View"
},
"icon": "columns",
"title": "_Tasks Kanban"
}
},
{
"id": "76f6d93232643435",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Find the Gold Cost Rush Formula.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Find the Gold Cost Rush Formula"
}
},
{
"id": "84990bbb12e210ae",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Test Automation.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Test Automation"
}
},
{
"id": "a152291798e86a03",
"type": "leaf",
"state": {
"type": "bases",
"state": {
"file": "_Tasks Kanban.base",
"viewName": "View"
},
"icon": "columns",
"title": "_Tasks Kanban"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "e5fa77420afb0950",
"type": "split",
"children": [
{
"id": "6073552ca7ff5134",
"type": "tabs",
"children": [
{
"id": "c5eb2e4263cad6dc",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "Files"
}
},
{
"id": "1705fb26380f3efc",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "Search"
}
},
{
"id": "9fc96f43d75dd799",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "Bookmarks"
}
}
]
}
],
"direction": "horizontal",
"width": 300
},
"right": {
"id": "bfd5504e61304ea1",
"type": "split",
"children": [
{
"id": "1e799187502263b3",
"type": "tabs",
"children": [
{
"id": "bf0da2169168b000",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"file": "_Tasks Kanban.base",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks for _Tasks Kanban"
}
},
{
"id": "373bc9c19b0f52bb",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "_Tasks Kanban.base",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links from _Tasks Kanban"
}
},
{
"id": "1a90e1d05d844c65",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "Tags"
}
},
{
"id": "90fa634b651649f2",
"type": "leaf",
"state": {
"type": "all-properties",
"state": {
"sortOrder": "frequency",
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-archive",
"title": "All properties"
}
},
{
"id": "6dbba8d8a0977a83",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "_Tasks Kanban.base",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of _Tasks Kanban"
}
},
{
"id": "1406320b187ae7a5",
"type": "leaf",
"state": {
"type": "calendar",
"state": {},
"icon": "calendar-with-checkmark",
"title": "Calendar"
}
}
]
}
],
"direction": "horizontal",
"width": 300,
"collapsed": true
},
"left-ribbon": {
"hiddenItems": {
"switcher:Open quick switcher": false,
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false,
"bases:Create new base": false
}
},
"active": "7a5b3ed118d43e9a",
"lastOpenFiles": [
"Find the Gold Cost Rush Formula.md",
"_Tasks Kanban.base",
"Dark Mode UI.md",
"Damage Tool.md",
"agent.md.md",
"Building Calculator.md",
"Building Data Tables.md",
"Building Stats Reference.md",
"Calculators Page.md",
"Database for Saving Calculation.md",
"Unit Data Tables.md",
"Victory Condition Information.md",
"Spell Data Tables.md",
"Learning Pages.md",
"Equipment Calculator.md",
"Test Automation.md",
"Building Plan Calculator.md",
"_Plan.canvas",
"Resources.md",
"Rush Gold Cost.md",
"constant gold value.md",
"Stub out the Home Page.md",
"Reference Pages.md",
"Tome Calculator.md",
"Home Page.md",
"Setup Test Database.md",
"Untitled 1.md"
]
}
+26
View File
@@ -0,0 +1,26 @@
- Draft
- Food
- Knowledge
- Industry
- Magic
- Gold
- Imperial
Need to calculate sum of outputs every turn.
One unit per turn, one building per turn.
User starts off with a Town Hall 1, which generates some basic resources a turn.
+1 Hero Cap
+10 [![Morale.png](https://aow4.paradoxwikis.com/thumb.php?f=Morale.png&width=21)](https://aow4.paradoxwikis.com/City_stability "City stability") City Stability
+30 [![Resource food.png](https://aow4.paradoxwikis.com/thumb.php?f=Resource_food.png&width=21)](https://aow4.paradoxwikis.com/Food "Food") Food income
+20 [![Resource structure production.png](https://aow4.paradoxwikis.com/thumb.php?f=Resource_structure_production.png&width=21)](https://aow4.paradoxwikis.com/Production "Production") Production income
+20 [![Resource unit production.png](https://aow4.paradoxwikis.com/thumb.php?f=Resource_unit_production.png&width=21)](https://aow4.paradoxwikis.com/Draft "Draft") Draft income
We also start with a Throne that generates +120 Gold a turn.
+25
View File
@@ -0,0 +1,25 @@
Can generate these resources per turn:
- Draft
- Food
- Knowledge
- Industry
- Magic
- Gold
- Imperial
Has a Industry Cost
Can be spend up with a [[Rush Gold Cost]].
Telerik UI.
Example data:
- Name: Store House
- Categories: Food
- Effects: +10 Food Income
- Requirements: Town Hall 1
- Costs: 130 Industry, 60 Gold
- Boost: 1 Forester
- Source: General
+28
View File
@@ -0,0 +1,28 @@
---
type: Task
status: Working On
---
# Building Plan Calculator
In general, resources are always produced by buildings. So by building buildings, you have more resources to build buildings a bit faster.
We want to consider every turn, to the last turn the player is on. So if we are on turn 60, we consider all turns between 1 and 60.
We need the income added every turn to a total pool, where appropriate. This pool gets subtracted from via build costs at the specific turn they are required, and any ongoing upkeep costs that occur every turn after said entity exists.
Store JSON of the build order of each building of when it's made.
Store Gold values over time. Table out the stored gold total for every turn that exists, such as the 1 to 60 example.
So to represent this, make a build order class that has an id to and item, and a built at time and a built finish time. The built finish time will be calculated when a stored required industry value, starting with the cost industry and then subtracted by the total income industry reaches a zero cutoff.
![[Building Calculator]]
![[Building Stats Reference]]
+34
View File
@@ -0,0 +1,34 @@
These are the general variable stats that will exist on a building. More to come as calculator considers more data cases.
# String
- Name required
- Description?
- Image?
- SourceId required: "General"
- RequirementIds[]?
# Int
- IncomeDraft: 0
- IncomeFood: 0
- IncomeKnowledge: 0
- IncomeIndustry: 0
- IncomeMagic: 0
- IncomeGold: 0
- IncomeImperial: 0
- IncomeStability: 0
- UpkeepGold: 0
- CostGold: 60
- CostIndustry: 120
- CostMana: 0
- BoostPopulation: 0
- BoostForester: 0
- BoostFarm: 0
- BoostQuarry: 0
- BoostGoldMine: 0
- BoostConduit: 0
View File
+7
View File
@@ -0,0 +1,7 @@
- Physical
- Fire
- Electric
- Cold
- Poison
- Holy
+4
View File
@@ -0,0 +1,4 @@
---
type: Task
status: TODO
---
View File
+8
View File
@@ -0,0 +1,8 @@
---
type: Task
status: TODO
---
I need to know how the gold cost works for rushing.
I am assuming rushing buildings is important for a build order.
+1
View File
@@ -0,0 +1 @@
Should have a disclaimer that this is not related to the company.
View File
+2
View File
@@ -0,0 +1,2 @@
Calculator Age of Wonders 4.
+4
View File
@@ -0,0 +1,4 @@
---
type: Task
status: Done
---
View File
+17
View File
@@ -0,0 +1,17 @@
# Culminative Resources
Gold
Mana
Imperium
Food
## Use it or Lose It
Knowledge
## Excess Converted
Draft -> 25% becomes Food
Industry -> 25% becomes Gold
+4
View File
@@ -0,0 +1,4 @@
Some calculation, based on industry produced and industry cost times by some constant gold value.
[[Find the Gold Cost Rush Formula]]
+7
View File
@@ -0,0 +1,7 @@
---
type: Task
status: TODO
---
PostgresSQL
View File
+13
View File
@@ -0,0 +1,13 @@
---
type: Task
status: Done
---
Generate a home page with three sections.
- Calculators
- References
- Learning
These three sections will have future subpages that will be added in the future. Organize the data into C# data files, and consume data to render the links on the home page. Make a nice rectangular frame around those links, and specific the section.
+6
View File
@@ -0,0 +1,6 @@
## Prompt
Generate C# Selenium test Project.
Use the page object pattern. Create tests the go to each page with the UI with clicks. Write a standalone test that scan and verifies any broken links.
+2
View File
@@ -0,0 +1,2 @@
- Need to consider resource affinities
- Need to consider level tome requirements of previous tomes
View File
+4
View File
@@ -0,0 +1,4 @@
- Expansion
- Conquest
- Magic
-
+33
View File
@@ -0,0 +1,33 @@
{
"nodes":[
{"id":"d392e6e61a41a9f9","type":"file","file":"Home Page.md","x":-780,"y":-1240,"width":400,"height":400},
{"id":"ec66808ddc2ccede","type":"file","file":"Calculators Page.md","x":-2260,"y":-700,"width":400,"height":320},
{"id":"a10ea2bfe08eaab6","type":"file","file":"Equipment Calculator.md","x":-2120,"y":0,"width":400,"height":400},
{"id":"388932b5ed97b551","type":"file","file":"Tome Calculator.md","x":-1660,"y":0,"width":400,"height":400},
{"id":"be06aa9b621f72a5","type":"file","file":"Database for Saving Calculation.md","x":-2120,"y":540,"width":400,"height":400},
{"id":"24b2a67ac5271ad8","type":"file","file":"Damage Tool.md","x":-520,"y":-120,"width":400,"height":400},
{"id":"88aea53e8fc7eb6f","type":"file","file":"Unit Data Tables.md","x":-520,"y":320,"width":400,"height":400},
{"id":"5d6f34d49c1df86e","type":"file","file":"Building Data Tables.md","x":-1080,"y":-120,"width":400,"height":400,"color":"4"},
{"id":"43c91e4e6967b569","type":"file","file":"Spell Data Tables.md","x":-1080,"y":360,"width":400,"height":400},
{"id":"13e1923067cfcf9b","type":"file","file":"Reference Pages.md","x":-780,"y":-660,"width":400,"height":400},
{"id":"bbbf447fc1602d59","type":"file","file":"Learning Pages.md","x":240,"y":-700,"width":400,"height":400},
{"id":"6d7d63854782ff9e","type":"file","file":"Victory Condition Information.md","x":240,"y":-200,"width":400,"height":400},
{"id":"bf2a479683623e4e","type":"file","file":"Building Calculator.md","x":-2720,"y":-200,"width":400,"height":400,"color":"4"}
],
"edges":[
{"id":"f54d8c93d7012115","fromNode":"a10ea2bfe08eaab6","fromSide":"bottom","toNode":"be06aa9b621f72a5","toSide":"top"},
{"id":"64a3ff0958685e69","fromNode":"bf2a479683623e4e","fromSide":"bottom","toNode":"be06aa9b621f72a5","toSide":"top"},
{"id":"ddbe3b68f6a17341","fromNode":"388932b5ed97b551","fromSide":"bottom","toNode":"be06aa9b621f72a5","toSide":"top"},
{"id":"87c01662980ef355","fromNode":"d392e6e61a41a9f9","fromSide":"bottom","toNode":"ec66808ddc2ccede","toSide":"top"},
{"id":"62fda2fcf4a6d568","fromNode":"ec66808ddc2ccede","fromSide":"bottom","toNode":"bf2a479683623e4e","toSide":"top"},
{"id":"ed0ed5f26cad4a27","fromNode":"ec66808ddc2ccede","fromSide":"bottom","toNode":"a10ea2bfe08eaab6","toSide":"top"},
{"id":"e67b94a44488d3f8","fromNode":"ec66808ddc2ccede","fromSide":"bottom","toNode":"388932b5ed97b551","toSide":"top"},
{"id":"ff856078baf2a901","fromNode":"d392e6e61a41a9f9","fromSide":"bottom","toNode":"13e1923067cfcf9b","toSide":"top"},
{"id":"a3a650b5fdafc356","fromNode":"13e1923067cfcf9b","fromSide":"bottom","toNode":"5d6f34d49c1df86e","toSide":"top"},
{"id":"af7f5aba6f3ec33b","fromNode":"13e1923067cfcf9b","fromSide":"bottom","toNode":"24b2a67ac5271ad8","toSide":"top"},
{"id":"3602a383caea61b9","fromNode":"13e1923067cfcf9b","fromSide":"bottom","toNode":"43c91e4e6967b569","toSide":"top"},
{"id":"2f9267a64626f611","fromNode":"13e1923067cfcf9b","fromSide":"bottom","toNode":"88aea53e8fc7eb6f","toSide":"top"},
{"id":"01d0ca393824b749","fromNode":"d392e6e61a41a9f9","fromSide":"bottom","toNode":"bbbf447fc1602d59","toSide":"top"},
{"id":"c680dc00a9ceb2e9","fromNode":"bbbf447fc1602d59","fromSide":"bottom","toNode":"6d7d63854782ff9e","toSide":"top"}
]
}
+44
View File
@@ -0,0 +1,44 @@
views:
- type: kanban-view
name: View
filters:
and:
- type == "Task"
order:
- file.name
- status
columnOrders:
file.file:
- Untitled.base
- Untitled.canvas
- Test.md
- Untitled.md
- Test 2.md
note.status:
- TODO
- Working On
- Done
cardOrders:
file.file:
Untitled.base: []
Untitled.canvas: []
Test.md:
- Test.md
- Untitled.canvas
- Untitled.base
note.status:
Uncategorized: []
Done:
- Stub out the Home Page.md
- Plan Basic Pages.md
TODO:
- Setup Test Database.md
- Find the Gold Cost Rush Formula.md
- Dark Mode UI.md
Working On:
- Building Plan Calculator.md
columnColors:
file.file: {}
note.status:
Done: green
groupByProperty: note.status
+253
View File
@@ -0,0 +1,253 @@
# **agent.md — Game Reference Platform Agent Specification**
## **Purpose**
This agent defines the architecture, responsibilities, and operational expectations for the **Game Reference**, a Blazorbased website to host:
- **Build calculators**
- **Learning documentation**
- **Game data reference libraries**
The web app is designed for **modularity**, **data portability**, and **longterm maintainability**, with a roadmap toward **selfhosted offline builds**.
---
## **HighLevel Responsibilities**
- **Data ingestion**: Regularly update game data sets from external sources (official APIs, community wikis, manual imports).
- **Data persistence**: Store all game data, user builds, and metadata in PostgreSQL.
- **Web delivery**: Serve interactive Blazor WebAssembly or Server pages for calculators and documentation.
- **Automation**: Validate functionality through NUnit unit tests and Selenium UI automation.
- **Containerization**: Package each game site as a Docker image for deployment or local use.
- **Offline mode**: Allow users to download the site and run it with their own local PostgreSQL instance.
---
## **System Architecture**
### **Platform Overview**
- **Frontend**: Blazor WebAssembly or Blazor Server (projectspecific choice)
- **Backend**: ASP.NET Core minimal APIs or controllers
- **Database**: PostgreSQL (hosted or local)
- **Containerization**: Docker Compose for multiservice orchestration
- **Testing**: NUnit + Selenium (C#)
- **CI/CD**: GitHub Actions or Azure DevOps (future)
---
### **Data Update Strategy**
Data ingestion may occur via:
- **Manual imports**
- **Official game APIs**
- **Communitymaintained datasets**
The agent must support:
- Scheduled updates
- Versioning of game data
- Rollback capability
- Validation before ingestion
---
## **Docker Architecture**
### **Container Layout**
Each game site includes:
- `blazor-app` container
- `postgres` container (optional for production, required for offline mode)
- `data-import` container (optional automation)
### **Local Development**
Users can run:
```
docker compose up --build
```
This launches:
- Blazor site
- Local PostgreSQL
- Optional admin tools (pgAdmin)
---
## **Testing Strategy**
### **NUnit Unit Tests**
Unit tests cover:
- Data models
- Calculation logic
- API endpoints
- Data ingestion validation
Tests live in:
```
/tests/GameSlop.Tests.Unit
```
### **Selenium UI Automation**
A separate C# Selenium project validates:
- Page load behavior
- Calculator functionality
- Navigation flow
- Form inputs
- Error handling
Tests live in:
```
/tests/GameSlop.Tests.UI
```
Automation runs against:
- Local Docker environment
- Staging environment
- Production (readonly tests)
---
## **Offline Mode (LongTerm Goal)**
Users will be able to:
1. Download a packaged version of a game site.
2. Run it locally via Docker.
3. Use a local PostgreSQL instance.
4. Save builds offline.
5. Import/export builds as JSON.
This requires:
- Selfcontained Docker Compose bundles
- Local EF migrations
---
## **Agent Responsibilities**
### **Build Calculators**
The agent must:
- Load game data dynamically
- Apply calculation logic (damage, stats, modifiers)
- Support multiple build variants
- Allow saving/loading builds from PostgreSQL
### **Documentation Engine**
The agent must:
- Render markdown or rich text
- Support versioned documentation
### **Data Sync**
The agent must:
- Detect outdated data
- Fetch new data
- Validate and sanitize
- Apply migrations if needed
### **Observability**
The agent should log:
- Data ingestion events
- User build saves
- Errors and exceptions
- Performance metrics
---
## **Contributor Guidelines**
- Follow C# coding standards
- Use EF migrations for schema changes
- Write NUnit tests for all logic
- Add Selenium tests for UI changes
- Document new features in `/docs`