Add Domain

This commit is contained in:
Andrey Kitelev 2025-01-01 19:32:46 +05:00 committed by kitelev
parent 5e01bbf24a
commit ff4362fe14
49 changed files with 5245 additions and 210 deletions

View File

@ -1,17 +0,0 @@
import ExoCommand from "./ExoCommand";
import {App, Notice} from "obsidian";
import NoteRepository from "../Domain/NoteRepository";
export default class CountNotesExoCommand implements ExoCommand {
name: string = "Количество заметок";
slug: string = "count-notes";
constructor(private app: App) {
}
async execute(): Promise<void> {
const noteRepository = new NoteRepository(this.app);
const count = noteRepository.all().length;
new Notice(`Vault has ${count} notes`);
}
}

View File

@ -1,8 +0,0 @@
import {App} from "obsidian";
export default interface ExoCommand {
name: string;
slug: string;
execute(app: App): Promise<void>;
}

View File

@ -1,17 +0,0 @@
import OpenRandomNoteExoCommand from "./OpenRandomNoteExoCommand";
import ExoCommand from "./ExoCommand";
import CountNotesExoCommand from "./CountNotesExoCommand";
import {App} from "obsidian";
export default class ExoCommands {
static all(app: App): ExoCommand[] {
return [
new OpenRandomNoteExoCommand(),
new CountNotesExoCommand(app)
];
}
static bySlug(app: App, slug: string): ExoCommand | undefined {
return ExoCommands.all(app).find(c => c.slug === slug);
}
}

View File

@ -1,18 +0,0 @@
import {FuzzySuggestModal} from "obsidian";
import ExoCommand from "./ExoCommand";
import ExoCommands from "./ExoCommands";
export class ExoCommandsModal extends FuzzySuggestModal<ExoCommand> {
getItems(): ExoCommand[] {
return ExoCommands.all(this.app);
}
getItemText(cmd: ExoCommand): string {
return cmd.name;
}
async onChooseItem(cmd: ExoCommand, evt: MouseEvent | KeyboardEvent) {
await cmd.execute(this.app);
}
}

View File

@ -1,10 +0,0 @@
import {TFile} from "obsidian";
export default class Note {
constructor(private tfile: TFile) {
}
name(): string {
return this.tfile.basename;
}
}

View File

@ -1,15 +0,0 @@
import Note from "./Note";
import {App} from "obsidian";
export default class NoteRepository {
private app: App;
constructor(app: App) {
this.app = app;
}
all(): Note[] {
const files = this.app.vault.getFiles();
return files.map(f => new Note(f));
}
}

View File

@ -1,20 +0,0 @@
import {App, Notice} from "obsidian";
import ExoCommands from "./Commands/ExoCommands";
export default class ExoApi {
constructor(private app: App) {
}
showNotice() {
new Notice("Hello from the API!");
}
commands(): ExoCommands[] {
return ExoCommands.all(this.app);
}
commandBySlug(slug: string) {
return ExoCommands.bySlug(this.app, slug);
}
}

View File

@ -3,3 +3,14 @@
A prototype plugin implementing the Exocortex concept.
Designed to enhance cognitive processes and decision-making within Obsidian.
## Expected data model
- Note - just a `.md` file
- Knowledge Object (KO) - Note with class (KOC)
- Knowledge Object Class (KOC) - Classification of a note
Every KO should have properties:
- uid
- tags (with class)

34
app/src/ExoMainModal.ts Normal file
View File

@ -0,0 +1,34 @@
import {FuzzySuggestModal, Notice} from "obsidian";
import ExoCommand from "./adapters/input/ExoCommand";
import ExoCommands from "./adapters/input/ExoCommands";
import ExoContext from "../../common/ExoContext";
export class ExoMainModal extends FuzzySuggestModal<ExoCommand> {
constructor(private ctx: ExoContext) {
super(ctx.app);
}
getItems(): ExoCommand[] {
return ExoCommands.all(this.ctx);
}
getItemText(cmd: ExoCommand): string {
return cmd.name;
}
async onChooseItem(cmd: ExoCommand) {
const startTime = performance.now();
try {
// console.log(`Executing command ${cmd.name}`);
await cmd.execute(this.ctx);
// console.log(`Command ${cmd.name} executed`);
} catch (e) {
console.error(e);
new Notice(`Error: ${e.message}`);
} finally {
const endTime = performance.now();
// console.log(`Execution time for command ${cmd.name}: ${endTime - startTime} ms`);
}
}
}

View File

@ -0,0 +1,41 @@
import {App, CachedMetadata, TFile} from "obsidian";
import KObject from "../../../core/src/domain/KObject";
import AppUtils from "../utils/AppUtils";
export default class VaultAdapter {
constructor(private app: App, private appUtils: AppUtils) {
}
getAllMdFiles() {
return this.app.vault.getMarkdownFiles();
}
getFileCache(file: TFile): CachedMetadata | null {
return this.app.metadataCache.getFileCache(file);
}
findMdWith(filter: (f: TFile) => boolean) {
return this.getAllMdFiles().filter(filter);
}
getObjectFileOrThrow(ko: KObject): TFile {
let res = this.getObjectFile(ko);
if (!res) {
throw new Error("Object file not found for " + ko);
}
return res;
}
getObjectFile(ko: KObject): TFile | null {
const a = this.findMdWith(f => {
const frontmatter = this.appUtils.getFrontmatterOrNull(f);
if (!frontmatter) {
return false;
}
const id: string = frontmatter["uid"];
return id === ko.id;
});
return a[0];
}
}

View File

@ -0,0 +1,17 @@
import ExoCommand from "./ExoCommand";
import {Notice} from "obsidian";
import CountNotesUseCase from "../../../../core/src/ports/input/CountNotesUseCase";
export default class CountNotesExoCommand implements ExoCommand {
name = "Notes Count";
slug = "count-notes";
constructor(private useCase: CountNotesUseCase) {
}
async execute(): Promise<void> {
const result = this.useCase.count();
new Notice(`Vault has ${result} notes`);
}
}

View File

@ -0,0 +1,22 @@
import ExoCommand from "./ExoCommand";
import ExoContext from "../../../../common/ExoContext";
import Area from "../../../../core/src/domain/Area";
export default class CreateEffortUnderAreaExoCommand implements ExoCommand {
name = "Create Effort Under Area";
slug = "create-effort-under-area";
constructor(private ctx: ExoContext) {
}
async execute() {
const activeFile = this.ctx.appUtils.getActiveFileOrThrow();
const activeKo = this.ctx.kObjectCreator.createFromTFileTyped(activeFile);
if (!(activeKo instanceof Area)) {
throw new Error("Active file is not an Area");
}
this.ctx.createEffortUseCase.taskUnderArea(activeKo);
}
}

View File

@ -0,0 +1,16 @@
import ExoCommand from "./ExoCommand";
import ExoContext from "../../../../common/ExoContext";
export default class CreateEmptyNoteWithinInboxExoCommand implements ExoCommand {
name = "Create Effort Within Inbox";
slug = "create-effort-within-inbox";
constructor(private ctx: ExoContext) {
}
async execute() {
const uid = this.ctx.utils.generateUid();
const path = `/0 Inbox/${uid}.md`;
await this.ctx.appUtils.createFile(path, uid);
}
}

View File

@ -0,0 +1,8 @@
import ExoContext from "../../../../common/ExoContext";
export default interface ExoCommand {
name: string;
slug: string;
execute(ctx: ExoContext): Promise<void>;
}

View File

@ -0,0 +1,27 @@
import OpenRandomNoteExoCommand from "./OpenRandomNoteExoCommand";
import ExoCommand from "./ExoCommand";
import CountNotesExoCommand from "./CountNotesExoCommand";
import CreateEmptyNoteWithinInboxExoCommand from "./CreateEmptyNoteWithinInboxExoCommand";
import GetActiveFileTagsExoCommand from "./GetActiveFileTagsExoCommand";
import GetCurrentKOCExoCommand from "./GetCurrentKOCExoCommand";
import OpenCurrentDailyNoteExoCommand from "./OpenCurrentDailyNoteExoCommand";
import ExoContext from "../../../../common/ExoContext";
import CreateEffortUnderAreaExoCommand from "./CreateEffortUnderAreaExoCommand";
export default class ExoCommands {
static all(ctx: ExoContext): ExoCommand[] {
return [
new OpenRandomNoteExoCommand(),
new CountNotesExoCommand(ctx.countNotesUseCase),
new CreateEmptyNoteWithinInboxExoCommand(ctx),
new GetActiveFileTagsExoCommand(ctx),
new GetCurrentKOCExoCommand(ctx),
new OpenCurrentDailyNoteExoCommand(ctx, ctx.getCurrentDNUseCase),
new CreateEffortUnderAreaExoCommand(ctx)
];
}
static bySlug(ctx: ExoContext, slug: string): ExoCommand | undefined {
return ExoCommands.all(ctx).find(c => c.slug === slug);
}
}

View File

@ -0,0 +1,17 @@
import ExoCommand from "./ExoCommand";
import {Notice} from "obsidian";
import ExoContext from "../../../../common/ExoContext";
export default class GetActiveFileTagsExoCommand implements ExoCommand {
name = "Get Active File Tags";
slug = "get-active-file-tags";
constructor(private ctx: ExoContext) {
}
async execute(): Promise<void> {
const activeFile = this.ctx.appUtils.getActiveFileOrThrow();
const tags = this.ctx.appUtils.getTagsFromFile(activeFile);
new Notice(`The current opened note has tags: ${tags.join(", ")}`);
}
}

View File

@ -0,0 +1,17 @@
import ExoCommand from "./ExoCommand";
import {Notice} from "obsidian";
import ExoContext from "../../../../common/ExoContext";
export default class GetCurrentKOCExoCommand implements ExoCommand {
name = "Get Current KOC";
slug = "get-current-koc";
constructor(private ctx: ExoContext) {
}
async execute(): Promise<void> {
const file = this.ctx.appUtils.getActiveFileOrThrow();
const currentKO = this.ctx.kObjectCreator.createFromTFile(file);
new Notice(`The current object's KOC is ${currentKO.koc}`);
}
}

View File

@ -0,0 +1,27 @@
import ExoCommand from "./ExoCommand";
import ExoContext from "../../../../common/ExoContext";
import GetCurrentDailyNoteUseCase from "../../../../core/src/ports/input/GetCurrentDailyNoteUseCase";
export default class OpenCurrentDailyNoteExoCommand implements ExoCommand {
name = "Open Current Daily Note";
slug = "open-current-daily-note";
constructor(private ctx: ExoContext,
private getCurrentDailyNoteUseCase: GetCurrentDailyNoteUseCase) {
}
async execute() {
const currentDN = await this.getCurrentDailyNoteUseCase.get();
if (!currentDN) {
throw new Error("No current daily note found");
}
let file = this.ctx.vaultAdapter.getObjectFile(currentDN);
if (!file) {
throw new Error("Daily note file not found");
}
await this.ctx.appUtils.openFile(file);
}
}

View File

@ -1,12 +1,13 @@
import ExoCommand from "./ExoCommand";
import {App, Notice} from "obsidian";
import {Notice} from "obsidian";
import ExoContext from "../../../../common/ExoContext";
export default class OpenRandomNoteExoCommand implements ExoCommand {
name: string = "Рандомная заметка из прошлого";
slug: string = "open-random-note";
name = "Рандомная заметка из прошлого";
slug = "open-random-note";
async execute(app: App): Promise<void> {
const files = app.vault.getFiles();
async execute(ctx: ExoContext): Promise<void> {
const files = ctx.vaultAdapter.getAllMdFiles();
const today = new Date();
const lastMonth = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate()).setHours(0, 0, 0, 0); // Дата месяц назад без времени
@ -16,9 +17,9 @@ export default class OpenRandomNoteExoCommand implements ExoCommand {
if (oldNotes.length > 0) {
// Выбираем случайную заметку
const randomNote = oldNotes[Math.floor(Math.random() * oldNotes.length)];
// Открываем её в активной панели
const leaf = app.workspace.getLeaf(false);
await leaf.openFile(randomNote);
await ctx.appUtils.openFile(randomNote);
} else {
new Notice("No old notes found.");
}

View File

@ -0,0 +1,27 @@
import DailyNote from "../../../../core/src/domain/DailyNote";
import {TFile} from "obsidian";
import VaultAdapter from "../VaultAdapter";
import DailyNoteCreator from "../../utils/DailyNoteCreator";
import AppUtils from "../../utils/AppUtils";
import DailyNoteRepository from "../../../../core/src/ports/output/DailyNoteRepository";
export default class DailyNotePersistenceAdapter implements DailyNoteRepository {
constructor(private appUtils: AppUtils,
private vaultAdapter: VaultAdapter,
private dailyNoteCreator: DailyNoteCreator) {
}
async findCurrent(): Promise<DailyNote | null> {
const allDNs = await this.findAll();
let currentDailyNotes = allDNs.filter(dn => dn.date.toDateString() === new Date().toDateString());
return currentDailyNotes.length > 0 ? currentDailyNotes[0] : null;
}
async findAll(): Promise<DailyNote[]> {
const rawDailyNotes: TFile[] = this.vaultAdapter.findMdWith((f: TFile) => {
return this.appUtils.getTagsFromFile(f).includes("TMS/DailyNote");
});
return rawDailyNotes.map(f => this.dailyNoteCreator.createFromTFile(f));
}
}

View File

@ -0,0 +1,30 @@
import EffortRepository from "../../../../core/src/ports/output/EffortRepository";
import Effort from "../../../../core/src/domain/effort/Effort";
import ExoContext from "../../../../common/ExoContext";
export default class EffortPersistenceAdapter implements EffortRepository {
constructor(private ctx: ExoContext) {
}
async save(effort: Effort): Promise<void> {
const folderPath: string = this.getPathForCreate(effort)
const filePath = folderPath + "/" + effort.title + ".md";
const data: string = "---\ntags:\n - EMS/Effort\n---\n\nThis is effort created with Exo!";
await this.ctx.app.vault.create(filePath, data);
}
// TODO should be in EffortPathRulesHelper in app module
getPathForCreate(effort: Effort): string {
if (effort.area !== null) {
const areaFile = this.ctx.vaultAdapter.getObjectFileOrThrow(effort.area);
const areaFolder = areaFile.parent;
if (!areaFolder) {
throw new Error("Area file has no parent folder");
}
return areaFolder.path;
}
return "/0 Inbox/";
}
}

View File

@ -0,0 +1,6 @@
import {TFile} from "obsidian";
export default class PersistentObject {
constructor(private file: TFile, private props: any) {
}
}

68
app/src/utils/AppUtils.ts Normal file
View File

@ -0,0 +1,68 @@
import {App, FrontMatterCache, TFile} from "obsidian";
export default class AppUtils {
constructor(private app: App) {
}
async createFile(path: string, textContent: string) {
await this.app.vault.create(path, textContent);
}
async openFile(file: TFile) {
const leaf = this.app.workspace.getLeaf(false);
await leaf.openFile(file);
}
getActiveFileOrThrow(): TFile {
const file = this.getActiveFileOrNull();
if (!file) {
throw new Error('No note opened.');
}
return file;
}
getActiveFileOrNull(): TFile | null {
return this.app.workspace.getActiveFile();
}
getFrontmatterOrNull(file: TFile): FrontMatterCache | null {
try {
return this.getFrontmatterOrThrow(file)
} catch (e) {
return null;
}
}
getFrontmatterOrThrow(file: TFile): FrontMatterCache {
let fileCache = this.app.metadataCache.getFileCache(file);
if (!fileCache) {
throw new Error(`File cache not found for ${file.path}`);
}
if (!fileCache.frontmatter) {
throw new Error(`Frontmatter not found for ${file.path}`);
}
return fileCache.frontmatter;
}
getTagsFromFile(file: TFile): string[] {
const frontmatter = this.app.metadataCache.getFileCache(file)?.frontmatter;
if (frontmatter && frontmatter.tags) {
if (typeof frontmatter.tags === "string") {
return frontmatter.tags.split(",").map(tag => tag.trim());
}
if (Array.isArray(frontmatter.tags)) {
return frontmatter.tags;
}
}
return [];
}
getFileByName(parentFileName: string): TFile {
return this.app.vault.getMarkdownFiles().filter(f => f.name == parentFileName)[0];
}
}

View File

@ -0,0 +1,17 @@
import {TFile} from "obsidian";
import DailyNote from "../../../core/src/domain/DailyNote";
import AppUtils from "./AppUtils";
import {UUID} from "node:crypto";
export default class DailyNoteCreator {
constructor(private appUtils: AppUtils) {
}
createFromTFile(file: TFile) {
const frontmatter = this.appUtils.getFrontmatterOrThrow(file);
const id = frontmatter["uid"] as UUID;
const dateStr = frontmatter["dn-date"];
const date = new Date(dateStr);
return new DailyNote(id, date);
}
}

View File

@ -0,0 +1,60 @@
import {TFile} from "obsidian";
import KObject from "../../../core/src/domain/KObject";
import {KOC} from "../../../core/src/domain/KOC";
import AppUtils from "./AppUtils";
import Area from "../../../core/src/domain/Area";
import {UUID} from "node:crypto";
export default class KObjectCreator {
constructor(private appUtils: AppUtils) {
}
createFromTFile(file: TFile) {
const frontmatter = this.appUtils.getFrontmatterOrThrow(file);
const id = frontmatter["uid"] as UUID;
const koc = this.getFileKoc(file);
return new KObject(id, koc);
}
createFromTFileTyped(file: TFile) {
const koc = this.getFileKoc(file);
switch (koc) {
case KOC.EMS_AREA:
return this.createArea(file);
default:
throw new Error("Not implemented createFromTFileTyped")
}
}
createArea(file: TFile): Area {
const koProperties = this.appUtils.getFrontmatterOrThrow(file);
const id: UUID = koProperties["uid"] as UUID;
let parentArea: Area | null = null;
const parentStr: string = koProperties["a-parent"];
if (parentStr) {
const parentFileName = parentStr.replace("[[", "").replace("]]", "");
const parentAreaFile: TFile = this.appUtils.getFileByName(parentFileName + ".md");
parentArea = this.createArea(parentAreaFile);
}
return new Area(id, file.name.replace(".md", ""), parentArea)
}
getFileKoc(file: TFile): KOC {
const tags = this.appUtils.getTagsFromFile(file);
if (tags.includes("IMS/MOC")) {
return KOC.IMS_MOC
} else if (tags.includes("EMS/Area")) {
return KOC.EMS_AREA;
} else if (tags.includes("EMS/Effort")) {
return KOC.EMS_EFFORT;
} else if (tags.includes("TMS/DailyNote")) {
return KOC.TMS_DN;
} else {
return KOC.UNKNOWN;
}
}
}

View File

@ -0,0 +1,31 @@
import ExoContext from "../../../common/ExoContext";
export default class KObjectUtility {
constructor(private ctx: ExoContext) {
}
async addMissingId(): Promise<void> {
let allMdFiles = this.ctx.vaultAdapter.getAllMdFiles();
const KOs = allMdFiles.filter(f => {
const tags = this.ctx.appUtils.getTagsFromFile(f);
let isKo = tags.some(tag => tag.startsWith("TMS/") || tag.startsWith("IMS/") || tag.startsWith("EMS/") || tag.startsWith("KMS/"));
return isKo && !f.path.startsWith("9 Meta/");
});
const withoutId = KOs.filter(f => {
let frontmatter = this.ctx.appUtils.getFrontmatterOrThrow(f);
if (!frontmatter) {
return false;
}
return !frontmatter["uid"];
})
for (let f of withoutId) {
await this.ctx.app.fileManager.processFrontMatter(f, (frontmatter) => {
frontmatter['uid'] = crypto.randomUUID();
});
}
}
}

6
babel.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};

48
common/ExoContext.ts Normal file
View File

@ -0,0 +1,48 @@
import {App} from "obsidian";
import DailyNoteRepository from "../core/src/ports/output/DailyNoteRepository";
import Utils from "../core/src/utils/Utils";
import DailyNoteCreator from "../app/src/utils/DailyNoteCreator";
import VaultAdapter from "../app/src/adapters/VaultAdapter";
import KObjectCreator from "../app/src/utils/KObjectCreator";
import AppUtils from "../app/src/utils/AppUtils";
import CountNotesUseCase from "../core/src/ports/input/CountNotesUseCase";
import CountNotesService from "../core/src/service/CountNotesService";
import DailyNotePersistenceAdapter from "../app/src/adapters/output/DailyNotePersistenceAdapter";
import GetCurrentDailyNoteUseCase from "../core/src/ports/input/GetCurrentDailyNoteUseCase";
import GetCurrentDailyNoteService from "../core/src/service/GetCurrentDailyNoteService";
import CreateEffortUseCase from "../core/src/ports/input/CreateEffortUseCase";
import CreateEffortService from "../core/src/service/CreateEffortService";
import EffortRepository from "../core/src/ports/output/EffortRepository";
import EffortPersistenceAdapter from "../app/src/adapters/output/EffortPersistenceAdapter";
import KObjectUtility from "../app/src/utils/KObjectUtility";
export default class ExoContext {
public readonly utils: Utils;
public readonly kObjectCreator: KObjectCreator
public readonly dailyNoteCreator: DailyNoteCreator;
public readonly dailyNoteRepository: DailyNoteRepository;
public readonly kObjectUtility: KObjectUtility;
public readonly vaultAdapter: VaultAdapter;
public readonly appUtils: AppUtils;
public readonly countNotesUseCase: CountNotesUseCase;
public readonly getCurrentDNUseCase: GetCurrentDailyNoteUseCase;
public readonly createEffortUseCase: CreateEffortUseCase;
public readonly effortRepository: EffortRepository;
constructor(public app: App) {
this.utils = new Utils(this.app);
this.appUtils = new AppUtils(this.app);
this.vaultAdapter = new VaultAdapter(this.app, this.appUtils);
this.kObjectCreator = new KObjectCreator(this.appUtils);
this.dailyNoteCreator = new DailyNoteCreator(this.appUtils);
this.dailyNoteRepository = new DailyNotePersistenceAdapter(this.appUtils, this.vaultAdapter, this.dailyNoteCreator);
this.kObjectUtility = new KObjectUtility(this);
this.countNotesUseCase = new CountNotesService(this.vaultAdapter);
this.getCurrentDNUseCase = new GetCurrentDailyNoteService(this.dailyNoteRepository);
this.effortRepository = new EffortPersistenceAdapter(this);
this.createEffortUseCase = new CreateEffortService(this.effortRepository);
}
}

14
core/src/ExoApi.ts Normal file
View File

@ -0,0 +1,14 @@
import {Notice} from "obsidian";
import ExoContext from "../../common/ExoContext";
export default class ExoApi {
// noinspection JSUnusedLocalSymbols
constructor(private ctx: ExoContext) {
}
// noinspection JSUnusedGlobalSymbols
showNotice() {
new Notice("Hello from the API!");
}
}

11
core/src/domain/Area.ts Normal file
View File

@ -0,0 +1,11 @@
import KObject from "./KObject";
import {KOC} from "./KOC";
import {UUID} from "node:crypto";
export default class Area extends KObject {
constructor(public id: UUID,
public readonly name: string,
public readonly parent: Area | null) {
super(id, KOC.EMS_AREA);
}
}

View File

@ -0,0 +1,10 @@
import KObject from "./KObject";
import {UUID} from "node:crypto";
import {KOC} from "./KOC";
export default class DailyNote extends KObject {
constructor(public id: UUID,
public date: Date) {
super(id, KOC.TMS_DN);
}
}

7
core/src/domain/KOC.ts Normal file
View File

@ -0,0 +1,7 @@
export enum KOC {
EMS_AREA = "EMS/Area",
EMS_EFFORT = "EMS/Effort",
IMS_MOC = "IMS/MOC",
TMS_DN = "TMS/DailyNote",
UNKNOWN = "UNKNOWN"
}

View File

@ -0,0 +1,8 @@
import {KOC} from "./KOC";
import {UUID} from "node:crypto";
export default class KObject {
constructor(public readonly id: UUID,
public readonly koc: KOC) {
}
}

View File

@ -0,0 +1,26 @@
import KObject from "../KObject";
import {EffortStatus} from "./EffortStatus";
import {KOC} from "../KOC";
import Area from "../Area";
import {UUID} from "node:crypto";
export default class Effort extends KObject {
constructor(public id: UUID,
public title: string,
public status: EffortStatus,
public started: Date | null,
public ended: Date | null,
public area: Area | null) {
super(id, KOC.EMS_EFFORT);
}
start() {
this.started = new Date();
this.status = EffortStatus.STARTED;
}
end() {
this.ended = new Date();
this.status = EffortStatus.ENDED;
}
}

View File

@ -0,0 +1,8 @@
export enum EffortStatus {
DRAFT,
BACKLOG,
READY,
STARTED,
ENDED,
TRASHED
}

View File

@ -0,0 +1,3 @@
export default interface CountNotesUseCase {
count(): number;
}

View File

@ -0,0 +1,6 @@
import Effort from "../../domain/effort/Effort";
import Area from "../../domain/Area";
export default interface CreateEffortUseCase {
taskUnderArea(area: Area): Effort;
}

View File

@ -0,0 +1,5 @@
import DailyNote from "../../domain/DailyNote";
export default interface GetCurrentDailyNoteUseCase {
get(): Promise<DailyNote | null>;
}

View File

@ -0,0 +1,7 @@
import DailyNote from "../../domain/DailyNote";
export default interface DailyNoteRepository {
findCurrent(): Promise<DailyNote | null>;
findAll(): Promise<DailyNote[]>;
}

View File

@ -0,0 +1,5 @@
import Effort from "../../domain/effort/Effort";
export default interface EffortRepository {
save(effort: Effort): void;
}

View File

@ -0,0 +1,12 @@
import CountNotesUseCase from "../ports/input/CountNotesUseCase";
import VaultAdapter from "../../../app/src/adapters/VaultAdapter";
export default class CountNotesService implements CountNotesUseCase {
constructor(private vaultAdapter: VaultAdapter) {
}
count(): number {
return this.vaultAdapter.getAllMdFiles().length;
}
}

View File

@ -0,0 +1,21 @@
import CreateEffortUseCase from "../ports/input/CreateEffortUseCase";
import Area from "../domain/Area";
import {EffortStatus} from "../domain/effort/EffortStatus";
import EffortRepository from "../ports/output/EffortRepository";
import {UUID} from "node:crypto";
import Effort from "../domain/effort/Effort";
export default class CreateEffortService implements CreateEffortUseCase {
constructor(private effortRepository: EffortRepository) {
}
taskUnderArea(area: Area): Effort {
const title = "Task under " + area.name;
const id = crypto.randomUUID() as UUID;
const effort = new Effort(id, title, EffortStatus.DRAFT, null, null, area);
this.effortRepository.save(effort);
return effort;
}
}

View File

@ -0,0 +1,13 @@
import GetCurrentDailyNoteUseCase from "../ports/input/GetCurrentDailyNoteUseCase";
import DailyNote from "../domain/DailyNote";
import DailyNoteRepository from "../ports/output/DailyNoteRepository";
export default class GetCurrentDailyNoteService implements GetCurrentDailyNoteUseCase {
constructor(private repository: DailyNoteRepository) {
}
async get(): Promise<DailyNote | null> {
return this.repository.findCurrent();
}
}

12
core/src/utils/Utils.ts Normal file
View File

@ -0,0 +1,12 @@
import {App} from "obsidian";
export default class Utils {
constructor(private app: App) {
}
generateUid(): string {
return crypto.randomUUID();
}
}

View File

@ -0,0 +1,7 @@
import {describe, expect} from '@jest/globals';
describe('JEST', () => {
it('is working', async () => {
expect(true).toBe(true);
});
});

20
main.ts
View File

@ -1,21 +1,19 @@
import {Plugin} from 'obsidian';
import {ExoCommandsModal} from "./Commands/ExoCommandsModal";
import {ExoMainModal} from "./app/src/ExoMainModal";
import "localforage";
import ExoApi from "./ExoApi";
import ExoApi from "./core/src/ExoApi";
import ExoContext from "./common/ExoContext";
export default class ExoPlugin extends Plugin {
private api: ExoApi;
private ctx: ExoContext;
async onload() {
this.addRibbonIcon('star', 'Exocortex Commands List', () => {
new ExoCommandsModal(this.app).open();
this.ctx = new ExoContext(this.app);
this.api = new ExoApi(this.ctx);
this.addRibbonIcon('star', 'Exocortex commands List', () => {
new ExoMainModal(this.ctx).open();
});
this.api = new ExoApi(this.app);
(this.app as any).plugins.plugins["exo-api"] = this.api;
}
onunload() {
delete (this.app as any).plugins.plugins["exo-api"];
}
}

4596
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,18 +5,24 @@
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production && jest",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-typescript": "^7.26.0",
"@jest/globals": "^29.7.0",
"@types/jest": "^29.5.14",
"@types/luxon": "^3.4.2",
"@types/node": "^16.11.6",
"@types/parsimmon": "^1.10.9",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"babel-jest": "^29.7.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
@ -26,6 +32,7 @@
"typescript": "4.7.4"
},
"dependencies": {
"compare-versions": "^6.1.1"
"compare-versions": "^6.1.1",
"jest": "^29.7.0"
}
}

View File

@ -24,6 +24,7 @@
"./types",
"./node_modules/obsidian-dataview"
],
"types": ["jest"],
"paths": {
"*": [
"./node_modules/obsidian-dataview/lib/*"