Adding obsidian docs with initial notes
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"alwaysUpdateLinks": true
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"theme": "obsidian"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
"kanban-bases-view",
|
||||||
|
"obsidian-note-autocreator",
|
||||||
|
"custom-font-loader",
|
||||||
|
"calendar",
|
||||||
|
"code-styler"
|
||||||
|
]
|
||||||
Vendored
+33
@@ -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
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
Vendored
+22
@@ -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
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"shouldConfirmBeforeCreate": true,
|
||||||
|
"weekStart": "locale",
|
||||||
|
"wordsPerDot": 250,
|
||||||
|
"showWeeklyNote": false,
|
||||||
|
"weeklyNoteFormat": "",
|
||||||
|
"weeklyNoteTemplate": "",
|
||||||
|
"weeklyNoteFolder": "",
|
||||||
|
"localeOverride": "system-default"
|
||||||
|
}
|
||||||
+4459
File diff suppressed because it is too large
Load Diff
+10
@@ -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"
|
||||||
|
}
|
||||||
+20047
File diff suppressed because one or more lines are too long
@@ -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 @@
|
|||||||
|
{}
|
||||||
+1348
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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 */
|
||||||
@@ -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
|
||||||
|
}
|
||||||
+4068
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
Vendored
+266
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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 [](https://aow4.paradoxwikis.com/City_stability "City stability") City Stability
|
||||||
|
+30 [](https://aow4.paradoxwikis.com/Food "Food") Food income
|
||||||
|
+20 [](https://aow4.paradoxwikis.com/Production "Production") Production income
|
||||||
|
+20 [](https://aow4.paradoxwikis.com/Draft "Draft") Draft income
|
||||||
|
|
||||||
|
We also start with a Throne that generates +120 Gold a turn.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
- Physical
|
||||||
|
- Fire
|
||||||
|
- Electric
|
||||||
|
- Cold
|
||||||
|
- Poison
|
||||||
|
- Holy
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
type: Task
|
||||||
|
status: TODO
|
||||||
|
---
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Should have a disclaimer that this is not related to the company.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
Calculator Age of Wonders 4.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
type: Task
|
||||||
|
status: Done
|
||||||
|
---
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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]]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
type: Task
|
||||||
|
status: TODO
|
||||||
|
---
|
||||||
|
PostgresSQL
|
||||||
|
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
- Need to consider resource affinities
|
||||||
|
- Need to consider level tome requirements of previous tomes
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- Expansion
|
||||||
|
- Conquest
|
||||||
|
- Magic
|
||||||
|
-
|
||||||
@@ -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"}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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 Blazor‑based website to host:
|
||||||
|
|
||||||
|
- **Build calculators**
|
||||||
|
|
||||||
|
- **Learning documentation**
|
||||||
|
|
||||||
|
- **Game data reference libraries**
|
||||||
|
|
||||||
|
|
||||||
|
The web app is designed for **modularity**, **data portability**, and **long‑term maintainability**, with a roadmap toward **self‑hosted offline builds**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **High‑Level 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 (project‑specific choice)
|
||||||
|
|
||||||
|
- **Backend**: ASP.NET Core minimal APIs or controllers
|
||||||
|
|
||||||
|
- **Database**: PostgreSQL (hosted or local)
|
||||||
|
|
||||||
|
- **Containerization**: Docker Compose for multi‑service 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**
|
||||||
|
|
||||||
|
- **Community‑maintained 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 (read‑only tests)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Offline Mode (Long‑Term 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:
|
||||||
|
|
||||||
|
- Self‑contained 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`
|
||||||
Reference in New Issue
Block a user