mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-24 05:22:58 +00:00
Complete Commonlib rc.6 integration and setup workflows
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { Confirm, ConfirmActionLayout } from "@vrtmrz/livesync-commonlib/compat/interfaces/Confirm";
|
||||
import { createNativeElement } from "@/apps/browserDom";
|
||||
|
||||
import MessageBox from "./ui/MessageBox.svelte";
|
||||
import TextInputBox from "./ui/TextInputBox.svelte";
|
||||
@@ -13,9 +14,9 @@ function displayMessageBox<T, U extends string[]>(
|
||||
buttons: U,
|
||||
title: string,
|
||||
commit: (ret: U[number]) => T,
|
||||
actionLayout?: ConfirmActionLayout
|
||||
actionLayout: ConfirmActionLayout = "vertical"
|
||||
): Promise<T> {
|
||||
const el = _activeDocument.createElement("div");
|
||||
const el = createNativeElement(_activeDocument, "div");
|
||||
const p = promiseWithResolvers<T>();
|
||||
mount(MessageBox, {
|
||||
target: el,
|
||||
@@ -42,7 +43,7 @@ function promptForInput(
|
||||
placeholder: string,
|
||||
isPassword?: boolean
|
||||
): Promise<string | false> {
|
||||
const el = _activeDocument.createElement("div");
|
||||
const el = createNativeElement(_activeDocument, "div");
|
||||
const p = promiseWithResolvers<string | false>();
|
||||
mount(TextInputBox, {
|
||||
target: el,
|
||||
@@ -103,12 +104,12 @@ export class BrowserConfirm<T extends ServiceContext> implements Confirm {
|
||||
const existing = _activeDocument.querySelector(`[data-livesync-popup="${CSS.escape(key)}"]`);
|
||||
existing?.remove();
|
||||
|
||||
const notice = _activeDocument.createElement("div");
|
||||
const notice = createNativeElement(_activeDocument, "div");
|
||||
notice.className = "livesync-browser-notice";
|
||||
notice.dataset.livesyncPopup = key;
|
||||
const [beforeText, afterText] = dialogText.split("{HERE}", 2);
|
||||
notice.append(beforeText);
|
||||
const anchor = _activeDocument.createElement("a");
|
||||
const anchor = createNativeElement(_activeDocument, "a");
|
||||
anchor.href = "#";
|
||||
anchorCallback(anchor);
|
||||
anchor.addEventListener("click", () => notice.remove());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promiseWithResolvers, type PromiseWithResolvers } from "octagonal-wheels/promises";
|
||||
import { createNativeElement } from "@/apps/browserDom";
|
||||
import { mount } from "svelte";
|
||||
import MenuView from "./ui/MenuView.svelte";
|
||||
import { _activeDocument } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
@@ -41,7 +42,7 @@ export class Menu {
|
||||
}
|
||||
waitingForClose?: PromiseWithResolvers<void>;
|
||||
showAtPosition(pos: { x: number; y: number }) {
|
||||
const el = _activeDocument.createElement("div");
|
||||
const el = createNativeElement(_activeDocument, "div");
|
||||
if (this.waitingForClose) {
|
||||
this.waitingForClose.resolve();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
SvelteDialogManagerBase,
|
||||
SvelteDialogMixIn,
|
||||
} from "@vrtmrz/livesync-commonlib/compat/services/implements/base/SvelteDialog";
|
||||
import { createNativeElement } from "@/apps/browserDom";
|
||||
import type { ServiceContext } from "@vrtmrz/livesync-commonlib/context";
|
||||
import type { SvelteDialogManagerDependencies } from "@vrtmrz/livesync-commonlib/compat/services/implements/base/SvelteDialog";
|
||||
import { _activeDocument } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
@@ -15,13 +16,13 @@ export class ShimModal {
|
||||
isOpen: boolean = false;
|
||||
baseEl: HTMLElement;
|
||||
constructor() {
|
||||
const baseEl = _activeDocument.createElement("popup");
|
||||
const baseEl = createNativeElement(_activeDocument, "popup");
|
||||
this.baseEl = baseEl;
|
||||
this.contentEl = _activeDocument.createElement("div");
|
||||
this.contentEl = createNativeElement(_activeDocument, "div");
|
||||
this.contentEl.className = "modal-content";
|
||||
this.titleEl = _activeDocument.createElement("div");
|
||||
this.titleEl = createNativeElement(_activeDocument, "div");
|
||||
this.titleEl.className = "modal-title";
|
||||
this.modalEl = _activeDocument.createElement("div");
|
||||
this.modalEl = createNativeElement(_activeDocument, "div");
|
||||
this.modalEl.className = "modal";
|
||||
this.modalEl.hidden = true;
|
||||
this.modalEl.appendChild(this.titleEl);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { BrowserServiceHub, type BrowserServiceHost } from "@vrtmrz/livesync-commonlib/compat/services/BrowserServices";
|
||||
import type { KeyValueDatabaseFactory } from "@vrtmrz/livesync-commonlib/compat/interfaces/KeyValueDatabase";
|
||||
import type { ServiceContext } from "@vrtmrz/livesync-commonlib/context";
|
||||
import { ServiceContext } from "@vrtmrz/livesync-commonlib/context";
|
||||
import { BrowserAPIService } from "@vrtmrz/livesync-commonlib/compat/services/implements/browser/BrowserAPIService";
|
||||
import { BrowserConfirm } from "./BrowserConfirm";
|
||||
import { LiveSyncBrowserUIService } from "./LiveSyncBrowserUIService";
|
||||
import { setLang, translateLiveSyncMessage } from "@/common/translation";
|
||||
|
||||
export type LiveSyncBrowserServiceHubOptions<T extends ServiceContext> = {
|
||||
context?: T;
|
||||
@@ -26,8 +27,11 @@ function createLiveSyncBrowserHost<T extends ServiceContext>(): BrowserServiceHo
|
||||
export function createLiveSyncBrowserServiceHub<T extends ServiceContext>(
|
||||
options: LiveSyncBrowserServiceHubOptions<T> = {}
|
||||
): BrowserServiceHub<T> {
|
||||
const context = options.context ?? (new ServiceContext({ translate: translateLiveSyncMessage }) as T);
|
||||
return new BrowserServiceHub<T>({
|
||||
...options,
|
||||
context,
|
||||
onDisplayLanguageChanged: setLang,
|
||||
host: createLiveSyncBrowserHost<T>(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ describe("LiveSync browser service context contract", () => {
|
||||
});
|
||||
const hub = createLiveSyncBrowserServiceHub({ context });
|
||||
|
||||
expect(observeServiceContext(context, "message")).toEqual({
|
||||
translation: "webapp:message",
|
||||
expect(observeServiceContext(context, "moduleLocalDatabase.logWaitingForReady")).toEqual({
|
||||
translation: "webapp:moduleLocalDatabase.logWaitingForReady",
|
||||
receivedEvents: ["context-contract-event"],
|
||||
});
|
||||
const composition = observeServiceComposition(hub, context);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Native DOM creation for browser applications which run outside Obsidian.
|
||||
*
|
||||
* Obsidian adds creation helpers to its own DOM environment. The standalone
|
||||
* Webapp and WebPeer hosts do not own those prototype extensions, and the
|
||||
* Webapp compatibility layer implements them on top of this native boundary.
|
||||
*/
|
||||
type NativeDocumentCreation = Pick<Document, "createElement" | "createDocumentFragment">;
|
||||
|
||||
export function createNativeElement<K extends keyof HTMLElementTagNameMap>(
|
||||
document: NativeDocumentCreation,
|
||||
tag: K
|
||||
): HTMLElementTagNameMap[K];
|
||||
export function createNativeElement(document: NativeDocumentCreation, tag: string): HTMLElement;
|
||||
export function createNativeElement(document: NativeDocumentCreation, tag: string): HTMLElement {
|
||||
return document.createElement(tag);
|
||||
}
|
||||
|
||||
export function createNativeFragment(document: NativeDocumentCreation): DocumentFragment {
|
||||
return document.createDocumentFragment();
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import { path as nodePath } from "@vrtmrz/livesync-commonlib/node";
|
||||
import type { KeyValueDBService } from "@vrtmrz/livesync-commonlib/compat/services/base/KeyValueDBService";
|
||||
import { PouchDB } from "@/apps/cli/lib/pouchdb-node";
|
||||
import { NodeServiceContext } from "./NodeServiceContext";
|
||||
import { setLang } from "@/common/translation";
|
||||
|
||||
export { NodeServiceContext } from "./NodeServiceContext";
|
||||
|
||||
@@ -95,7 +96,11 @@ export class NodeServiceHub<T extends NodeServiceContext> extends InjectableServ
|
||||
const conflict = new InjectableConflictService(context);
|
||||
const fileProcessing = new InjectableFileProcessingService(context);
|
||||
|
||||
const setting = new NodeSettingService(context, { APIService: API }, localStoragePath);
|
||||
const setting = new NodeSettingService(
|
||||
context,
|
||||
{ APIService: API, onDisplayLanguageChanged: setLang },
|
||||
localStoragePath
|
||||
);
|
||||
|
||||
const appLifecycle = new NodeAppLifecycleService<T>(context, {
|
||||
settingService: setting,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LiveSyncWebApp } from "./main";
|
||||
import { createNativeElement } from "@/apps/browserDom";
|
||||
import { VaultHistoryStore, type VaultHistoryItem } from "./vaultSelector";
|
||||
import { compatGlobal, _activeDocument } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
|
||||
@@ -55,24 +56,24 @@ async function renderHistoryList(): Promise<VaultHistoryItem[]> {
|
||||
emptyEl.classList.toggle("is-hidden", items.length > 0);
|
||||
|
||||
for (const item of items) {
|
||||
const row = _activeDocument.createElement("div");
|
||||
const row = createNativeElement(_activeDocument, "div");
|
||||
row.className = "vault-item";
|
||||
|
||||
const info = _activeDocument.createElement("div");
|
||||
const info = createNativeElement(_activeDocument, "div");
|
||||
info.className = "vault-item-info";
|
||||
|
||||
const name = _activeDocument.createElement("div");
|
||||
const name = createNativeElement(_activeDocument, "div");
|
||||
name.className = "vault-item-name";
|
||||
name.textContent = item.name;
|
||||
|
||||
const meta = _activeDocument.createElement("div");
|
||||
const meta = createNativeElement(_activeDocument, "div");
|
||||
meta.className = "vault-item-meta";
|
||||
const label = item.id === lastUsedId ? "Last used" : "Used";
|
||||
meta.textContent = `${label}: ${formatLastUsed(item.lastUsedAt)}`;
|
||||
|
||||
info.append(name, meta);
|
||||
|
||||
const useButton = _activeDocument.createElement("button");
|
||||
const useButton = createNativeElement(_activeDocument, "button");
|
||||
useButton.type = "button";
|
||||
useButton.textContent = "Use this vault";
|
||||
useButton.addEventListener("click", () => {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Obsidian mock, and must not become a general test environment for the plug-in. When the Webapp compatibility boundary
|
||||
* is redesigned, replace this implementation here rather than extending it as a shared Obsidian simulation.
|
||||
*/
|
||||
import { createNativeElement, createNativeFragment } from "@/apps/browserDom";
|
||||
import type {
|
||||
Command,
|
||||
DataWriteOptions,
|
||||
@@ -456,7 +457,7 @@ class Workspace extends Events {
|
||||
revealLeaf() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
containerEl: HTMLElement = document.createElement("div");
|
||||
containerEl: HTMLElement = createNativeElement(document, "div");
|
||||
}
|
||||
export class App {
|
||||
vaultName: string = "MockVault";
|
||||
@@ -543,19 +544,19 @@ export class Modal {
|
||||
|
||||
constructor(app: App) {
|
||||
this.app = app;
|
||||
this.contentEl = document.createElement("div");
|
||||
this.contentEl = createNativeElement(document, "div");
|
||||
this.contentEl.className = "modal-content";
|
||||
this.titleEl = document.createElement("div");
|
||||
this.titleEl = createNativeElement(document, "div");
|
||||
this.titleEl.className = "modal-title";
|
||||
this.modalEl = document.createElement("div");
|
||||
this.modalEl = createNativeElement(document, "div");
|
||||
this.modalEl.className = "modal";
|
||||
this.modalEl.style.display = "none";
|
||||
this.modalEl.hidden = true;
|
||||
this.modalEl.appendChild(this.titleEl);
|
||||
this.modalEl.appendChild(this.contentEl);
|
||||
}
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
this.modalEl.style.display = "block";
|
||||
this.modalEl.hidden = false;
|
||||
if (!this.modalEl.parentElement) {
|
||||
document.body.appendChild(this.modalEl);
|
||||
}
|
||||
@@ -563,7 +564,7 @@ export class Modal {
|
||||
}
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
this.modalEl.style.display = "none";
|
||||
this.modalEl.hidden = true;
|
||||
this.onClose();
|
||||
}
|
||||
onOpen() {}
|
||||
@@ -581,7 +582,7 @@ export class PluginSettingTab {
|
||||
constructor(app: App, plugin: Plugin) {
|
||||
this.app = app;
|
||||
this.plugin = plugin;
|
||||
this.containerEl = document.createElement("div");
|
||||
this.containerEl = createNativeElement(document, "div");
|
||||
}
|
||||
display() {}
|
||||
}
|
||||
@@ -621,12 +622,12 @@ export class Component {
|
||||
}
|
||||
|
||||
export class ButtonComponent extends Component {
|
||||
buttonEl: HTMLButtonElement = document.createElement("button");
|
||||
buttonEl: HTMLButtonElement = createNativeElement(document, "button");
|
||||
private clickHandler: ((evt: MouseEvent) => unknown) | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.buttonEl = document.createElement("button");
|
||||
this.buttonEl = createNativeElement(document, "button");
|
||||
this.buttonEl.type = "button";
|
||||
}
|
||||
|
||||
@@ -664,12 +665,12 @@ export class ButtonComponent extends Component {
|
||||
}
|
||||
|
||||
export class TextComponent extends Component {
|
||||
inputEl: HTMLInputElement = document.createElement("input");
|
||||
inputEl: HTMLInputElement = createNativeElement(document, "input");
|
||||
private changeHandler: ((value: string) => unknown) | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.inputEl = document.createElement("input");
|
||||
this.inputEl = createNativeElement(document, "input");
|
||||
this.inputEl.type = "text";
|
||||
}
|
||||
|
||||
@@ -708,12 +709,12 @@ export class TextComponent extends Component {
|
||||
}
|
||||
|
||||
export class ToggleComponent extends Component {
|
||||
inputEl: HTMLInputElement = document.createElement("input");
|
||||
inputEl: HTMLInputElement = createNativeElement(document, "input");
|
||||
private changeHandler: ((value: boolean) => unknown) | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.inputEl = document.createElement("input");
|
||||
this.inputEl = createNativeElement(document, "input");
|
||||
this.inputEl.type = "checkbox";
|
||||
}
|
||||
|
||||
@@ -738,16 +739,16 @@ export class ToggleComponent extends Component {
|
||||
}
|
||||
|
||||
export class DropdownComponent extends Component {
|
||||
selectEl: HTMLSelectElement = document.createElement("select");
|
||||
selectEl: HTMLSelectElement = createNativeElement(document, "select");
|
||||
private changeHandler: ((value: string) => unknown) | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.selectEl = document.createElement("select");
|
||||
this.selectEl = createNativeElement(document, "select");
|
||||
}
|
||||
|
||||
addOption(v: string, d: string) {
|
||||
const option = document.createElement("option");
|
||||
const option = createNativeElement(document, "option");
|
||||
option.value = v;
|
||||
option.textContent = d;
|
||||
this.selectEl.appendChild(option);
|
||||
@@ -782,12 +783,12 @@ export class DropdownComponent extends Component {
|
||||
}
|
||||
|
||||
export class SliderComponent extends Component {
|
||||
inputEl: HTMLInputElement = document.createElement("input");
|
||||
inputEl: HTMLInputElement = createNativeElement(document, "input");
|
||||
private changeHandler: ((value: number) => unknown) | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.inputEl = document.createElement("input");
|
||||
this.inputEl = createNativeElement(document, "input");
|
||||
this.inputEl.type = "range";
|
||||
}
|
||||
|
||||
@@ -917,7 +918,7 @@ if (typeof HTMLElement !== "undefined") {
|
||||
info?: DomElementInfo | string,
|
||||
callback?: (element: HTMLDivElement) => void
|
||||
): HTMLDivElement {
|
||||
const element = document.createElement("div");
|
||||
const element = createNativeElement(document, "div");
|
||||
applyDomElementInfo(element, info);
|
||||
this.appendChild(element);
|
||||
callback?.(element);
|
||||
@@ -929,7 +930,7 @@ if (typeof HTMLElement !== "undefined") {
|
||||
info?: DomElementInfo | string,
|
||||
callback?: (element: HTMLElementTagNameMap[K]) => void
|
||||
): HTMLElementTagNameMap[K] {
|
||||
const element = document.createElement(tag);
|
||||
const element = createNativeElement(document, tag);
|
||||
applyDomElementInfo(element, info);
|
||||
this.appendChild(element);
|
||||
callback?.(element);
|
||||
@@ -940,7 +941,7 @@ if (typeof HTMLElement !== "undefined") {
|
||||
info?: DomElementInfo | string,
|
||||
callback?: (element: HTMLSpanElement) => void
|
||||
): HTMLSpanElement {
|
||||
const element = document.createElement("span");
|
||||
const element = createNativeElement(document, "span");
|
||||
applyDomElementInfo(element, info);
|
||||
this.appendChild(element);
|
||||
callback?.(element);
|
||||
@@ -980,7 +981,7 @@ export class FuzzySuggestModal<T> {
|
||||
|
||||
function parseHtmlFragment(html: string): DocumentFragment {
|
||||
const parsed = new DOMParser().parseFromString(html, "text/html");
|
||||
const fragment = document.createDocumentFragment();
|
||||
const fragment = createNativeFragment(document);
|
||||
for (const child of [...parsed.body.childNodes]) {
|
||||
fragment.appendChild(document.importNode(child, true));
|
||||
}
|
||||
@@ -999,7 +1000,7 @@ export class ItemView {}
|
||||
export class WorkspaceLeaf {}
|
||||
|
||||
export function sanitizeHTMLToDom(html: string) {
|
||||
const div = document.createElement("div");
|
||||
const div = createNativeElement(document, "div");
|
||||
div.appendChild(parseHtmlFragment(html));
|
||||
return div;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user