mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-31 08:51:23 +00:00
refactor: consume Commonlib as a package
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
<script lang="ts">
|
||||
import type { DialogHostProps } from "@vrtmrz/livesync-commonlib/compat/services/implements/base/SvelteDialog";
|
||||
import { type DialogSvelteComponentBaseProps } from "./svelteDialog";
|
||||
// type Props = DialogSvelteComponentBaseProps & {
|
||||
// /**
|
||||
// * The Svelte component to mount inside the dialog host
|
||||
// */
|
||||
// mountComponent: ComponentHasResult<any>;
|
||||
// /**
|
||||
// * Callback function to setup the dialog context
|
||||
// * @param props
|
||||
// */
|
||||
// onSetupContext?(props: DialogSvelteComponentBaseProps): void;
|
||||
// };
|
||||
const { setTitle, closeDialog, setResult, mountComponent, getInitialData, onSetupContext }: DialogHostProps =
|
||||
$props();
|
||||
const contextProps = {
|
||||
setTitle,
|
||||
closeDialog,
|
||||
setResult,
|
||||
getInitialData,
|
||||
} satisfies DialogSvelteComponentBaseProps<any,any>
|
||||
|
||||
// Call the onSetupContext function to setup the dialog context
|
||||
onSetupContext?.(contextProps);
|
||||
|
||||
/**
|
||||
* Wrapper around setResult to also close the dialog
|
||||
* @param result
|
||||
*/
|
||||
const setResultWrapper = (result: any) => {
|
||||
setResult(result);
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
const Component = mountComponent;
|
||||
let thisElement: HTMLElement;
|
||||
</script>
|
||||
|
||||
<div class="dialog-host" bind:this={thisElement}>
|
||||
<Component setResult={setResultWrapper} {getInitialData}></Component>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dialog-host {
|
||||
padding: 20px;
|
||||
gap: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: var(--keyboard-height, 0px);
|
||||
}
|
||||
|
||||
.dialog-host :global(button) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.dialog-host :global(.button-group) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.dialog-host :global(.row) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dialog-host :global(.row > input[type="text"]),
|
||||
.dialog-host :global(.row > input[type="password"]),
|
||||
.dialog-host :global(.row > textarea),
|
||||
.dialog-host :global(.row > select) {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
min-width: 10em;
|
||||
}
|
||||
.dialog-host :global(.row > input[type="password"]) {
|
||||
-webkit-text-security: disc;
|
||||
}
|
||||
|
||||
.dialog-host :global(.row > input[type="checkbox"]) {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.dialog-host :global(label > span) {
|
||||
display: block;
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.dialog-host :global(.note),
|
||||
.dialog-host :global(.note-important),
|
||||
.dialog-host :global(.note-error) {
|
||||
padding: 10px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0.5lh;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.dialog-host :global(.note) {
|
||||
background-color: var(--interactive-hover);
|
||||
border-left-color: var(--interactive-accent);
|
||||
}
|
||||
.dialog-host :global(.note-important) {
|
||||
background-color: var(--interactive-hover);
|
||||
border-left-color: var(--text-warning);
|
||||
}
|
||||
.dialog-host :global(.note-error) {
|
||||
background-color: var(--interactive-hover);
|
||||
border-left-color: var(--text-error);
|
||||
}
|
||||
.dialog-host :global(hr) {
|
||||
margin: 0.7lh 0;
|
||||
}
|
||||
.dialog-host :global(details) {
|
||||
gap: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
border-left: 2px solid var(--interactive-accent);
|
||||
}
|
||||
.dialog-host :global(summary::marker) {
|
||||
display: none;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.dialog-host :global(summary) {
|
||||
border-left: 4px solid var(--interactive-accent);
|
||||
padding-left: 0.5em;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
.dialog-host :global(details > summary::after) {
|
||||
content: "⏷";
|
||||
float: right;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.dialog-host :global(details[open] > summary::after) {
|
||||
content: "⏶";
|
||||
float: right;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.dialog-host :global(input:invalid),
|
||||
.dialog-host :global(textarea:invalid) {
|
||||
border-color: var(--background-modifier-error);
|
||||
}
|
||||
.dialog-host :global(.sub-section) {
|
||||
margin-left: 1em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.dialog-host :global(.row > input[type="text"]:disabled),
|
||||
.dialog-host :global(.row > input[type="password"]:disabled),
|
||||
.dialog-host :global(.row > textarea:disabled),
|
||||
.dialog-host :global(.row > select:disabled) {
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
value: boolean;
|
||||
noteOnSelected?: () => any;
|
||||
noteOnUnselected?: () => any;
|
||||
children?: () => any;
|
||||
};
|
||||
|
||||
let { title, value = $bindable(), noteOnSelected, noteOnUnselected, children }: Props = $props();
|
||||
const translatedTitle = $derived.by(() => translate(title));
|
||||
</script>
|
||||
|
||||
<label class="choice-row">
|
||||
<input type="checkbox" bind:checked={value} />
|
||||
<span class="choice-title">{translatedTitle}</span>
|
||||
</label>
|
||||
<div class="choice-notes">
|
||||
<!-- TODO Highlight selected option -->
|
||||
{#if value && noteOnSelected}
|
||||
{@render noteOnSelected()}
|
||||
{:else if !value && noteOnUnselected}
|
||||
{@render noteOnUnselected()}
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.choice-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.choice-row span.choice-title {
|
||||
width: auto;
|
||||
}
|
||||
.choice-row input[type="checkbox"] {
|
||||
/* width: 1.2rem;
|
||||
height: 1.2rem; */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.choice-notes {
|
||||
margin-left: 2rem;
|
||||
margin-top: 0.25rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
commit: () => Promise<void> | void;
|
||||
important?: boolean;
|
||||
destructive?: boolean;
|
||||
additionalClasses?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
let { title, commit, additionalClasses, important, disabled = $bindable(), destructive }: Props = $props();
|
||||
const translatedTitle = $derived.by(() => translate(title));
|
||||
function onclick() {
|
||||
fireAndForget(async () => commit());
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="button {additionalClasses} {important ? 'mod-cta' : ''} {destructive ? 'mod-destructive' : ''}"
|
||||
{onclick}
|
||||
{disabled}>{translatedTitle}</button
|
||||
>
|
||||
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { onMount, tick } from "svelte";
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
import { getDialogContext } from "@/modules/services/LiveSyncUI/svelteDialog";
|
||||
import { _activeDocument } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
children?: () => unknown;
|
||||
};
|
||||
let { title = $bindable(), subtitle }: Props = $props();
|
||||
const context = getDialogContext();
|
||||
const translatedTitle = $derived.by(() => translate(title));
|
||||
const translatedSubtitle = $derived.by(() => (subtitle ? translate(subtitle) : ""));
|
||||
const modalTitle = $derived.by(() => `${translatedTitle}${translatedSubtitle ? ` - ${translatedSubtitle}` : ""}`);
|
||||
|
||||
$effect(() => {
|
||||
if (translatedTitle) {
|
||||
context.setTitle(modalTitle);
|
||||
}
|
||||
});
|
||||
onMount(async () => {
|
||||
context.setTitle(modalTitle);
|
||||
await tick();
|
||||
_activeDocument.querySelector(".modal")?.scrollTo(0, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="dialog-header">
|
||||
<h2>{translatedTitle}</h2>
|
||||
{#if translatedSubtitle}
|
||||
<h4>{translatedSubtitle}</h4>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dialog-header {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
children?: () => any;
|
||||
};
|
||||
const { children, title }: Props = $props();
|
||||
const translatedTitle = $derived.by(() => (title ? translate(title) : ""));
|
||||
</script>
|
||||
|
||||
<details>
|
||||
<summary>{translatedTitle}</summary>
|
||||
<div class="sub-section">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</details>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type Props = {
|
||||
children?: () => any;
|
||||
important?: boolean;
|
||||
title?: string;
|
||||
};
|
||||
const { children, important, title }: Props = $props();
|
||||
const cssClass = $derived.by(() => {
|
||||
return important ? "guidance important" : "guidance";
|
||||
});
|
||||
const translatedTitle = $derived.by(() => (title ? translate(title) : ""));
|
||||
</script>
|
||||
|
||||
<div class={cssClass}>
|
||||
{#if translatedTitle}
|
||||
<h3>{translatedTitle}</h3>
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -0,0 +1,87 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type SignalWord = "danger" | "warning" | "caution" | "notice";
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
message?: string;
|
||||
children?: () => any;
|
||||
cssClass?: string;
|
||||
warning?: boolean;
|
||||
caution?: boolean;
|
||||
error?: boolean;
|
||||
notice?: boolean;
|
||||
info?: boolean;
|
||||
signalWord?: string | false;
|
||||
visible?: boolean;
|
||||
};
|
||||
const {
|
||||
title,
|
||||
message,
|
||||
children,
|
||||
cssClass,
|
||||
warning: isWarning,
|
||||
caution: isCaution,
|
||||
error: isError,
|
||||
notice: isNotice,
|
||||
info: isInfo = true,
|
||||
signalWord,
|
||||
visible,
|
||||
}: Props = $props();
|
||||
const derivedCssClass = $derived.by(() => {
|
||||
if (isError) {
|
||||
return "note-error sls-info-note sls-info-note-danger";
|
||||
} else if (isWarning) {
|
||||
return "note-important sls-info-note sls-info-note-warning";
|
||||
} else if (isCaution) {
|
||||
return "note-important sls-info-note sls-info-note-caution";
|
||||
} else if (isNotice) {
|
||||
return "note sls-info-note sls-info-note-notice";
|
||||
} else if (isInfo) {
|
||||
return "note sls-info-note";
|
||||
} else {
|
||||
return "sls-info-note";
|
||||
}
|
||||
});
|
||||
|
||||
const signalWordKind = $derived.by((): SignalWord | undefined => {
|
||||
if (isError) return "danger";
|
||||
if (isWarning) return "warning";
|
||||
if (isCaution) return "caution";
|
||||
if (isNotice) return "notice";
|
||||
return undefined;
|
||||
});
|
||||
const defaultSignalWord = $derived.by(() => {
|
||||
switch (signalWordKind) {
|
||||
case "danger":
|
||||
return "Ui.Common.Signal.Danger";
|
||||
case "warning":
|
||||
return "Ui.Common.Signal.Warning";
|
||||
case "caution":
|
||||
return "Ui.Common.Signal.Caution";
|
||||
case "notice":
|
||||
return "Ui.Common.Signal.Notice";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
});
|
||||
const signalWordText = $derived.by(() => {
|
||||
if (signalWord === false) return "";
|
||||
return signalWord ? translate(signalWord) : defaultSignalWord ? translate(defaultSignalWord) : "";
|
||||
});
|
||||
const signalWordCssKind = $derived.by(() => signalWordKind ?? "custom");
|
||||
const translatedTitle = $derived.by(() => (title ? translate(title) : ""));
|
||||
const translatedMessage = $derived.by(() => (message ? translate(message) : ""));
|
||||
</script>
|
||||
|
||||
{#if visible === undefined || visible === true}
|
||||
<div class={(cssClass ?? "") + " " + derivedCssClass}>
|
||||
{#if signalWordText}
|
||||
<div class="sls-signal-word sls-signal-word-{signalWordCssKind}">{signalWordText}</div>
|
||||
{/if}
|
||||
{#if translatedTitle}<h3>{translatedTitle}</h3>{/if}
|
||||
{#if translatedMessage}<p>{translatedMessage}</p>{/if}
|
||||
{@render children?.()}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
type Props = {
|
||||
info: Record<string, any>;
|
||||
};
|
||||
const { info }: Props = $props();
|
||||
const infoEntries = $derived.by(() => Object.entries(info ?? {}));
|
||||
</script>
|
||||
|
||||
<div class="info-panel">
|
||||
<div class="info-grid" role="list">
|
||||
{#each infoEntries as [key, value]}
|
||||
<div class="info-entry info-key" role="listitem" aria-label={key}>
|
||||
<div class="key">{key}</div>
|
||||
</div>
|
||||
<div class="info-entry info-item" role="listitem" aria-label={key}>
|
||||
<div class="value">{value}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.info-panel {
|
||||
padding: 0.6rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* Main Grid (Info Items) 120px to 1fr, repeat */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(120px, 1fr) 1fr;
|
||||
column-count: 2;
|
||||
gap: 0.6rem;
|
||||
margin-top: 0.5rem;
|
||||
grid-area: "info-key" "info-value";
|
||||
}
|
||||
.info-entry {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
min-height: 1.2em;
|
||||
}
|
||||
|
||||
.info-key {
|
||||
font-weight: 600;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--background-modifier-hover);
|
||||
border-bottom: 1px solid var(--background-modifier-hover);
|
||||
grid-area: "info-key";
|
||||
}
|
||||
.info-item {
|
||||
align-items: start;
|
||||
padding: 0.5rem;
|
||||
background: var(--background-modifier-hover, rgba(0, 0, 0, 0.03));
|
||||
grid-area: "info-value";
|
||||
}
|
||||
|
||||
.value {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text-normal, #e6e6e6);
|
||||
min-height: 1em;
|
||||
}
|
||||
|
||||
@container (max-width: 340px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.info-item {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
children?: () => any;
|
||||
};
|
||||
const { label, children }: Props = $props();
|
||||
const translatedLabel = $derived.by(() => translate(label));
|
||||
</script>
|
||||
|
||||
<label class="row"
|
||||
><span>{translatedLabel}</span>
|
||||
{@render children?.()}
|
||||
</label>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
type Props = {
|
||||
children?: () => any;
|
||||
};
|
||||
const { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="question-container">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
value: string;
|
||||
selectedValue: string;
|
||||
noteOnSelected?: () => any;
|
||||
noteOnUnselected?: () => any;
|
||||
group?: string;
|
||||
children?: () => any;
|
||||
};
|
||||
const definedGroupContext = getContext<string>("radioGroup");
|
||||
|
||||
let {
|
||||
title,
|
||||
value = $bindable(),
|
||||
noteOnSelected,
|
||||
noteOnUnselected,
|
||||
selectedValue,
|
||||
group,
|
||||
children,
|
||||
}: Props = $props();
|
||||
const actualGroup = group ?? definedGroupContext;
|
||||
const translatedTitle = $derived.by(() => translate(title));
|
||||
</script>
|
||||
|
||||
<div class="option-container {value === selectedValue ? 'selected' : ''}">
|
||||
<label>
|
||||
<div class="choice-row">
|
||||
<input type="radio" bind:group={value} name={actualGroup} value={selectedValue} />
|
||||
<span class="choice-title">{translatedTitle}</span>
|
||||
</div>
|
||||
<div class="choice-notes">
|
||||
{#if value === selectedValue && noteOnSelected}
|
||||
{@render noteOnSelected()}
|
||||
{:else if value !== selectedValue && noteOnUnselected}
|
||||
{@render noteOnUnselected()}
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.option-container {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25lh;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.option-container.selected {
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
.choice-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
/* margin-top: 1rem; */
|
||||
cursor: pointer;
|
||||
}
|
||||
.choice-row span.choice-title {
|
||||
width: auto;
|
||||
}
|
||||
.choice-row input[type="radio"] {
|
||||
/* width: 1.2rem;
|
||||
height: 1.2rem; */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.choice-notes {
|
||||
margin-left: 2rem;
|
||||
margin-top: 0.25rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.option-container.selected .choice-notes {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { setContext } from "svelte";
|
||||
|
||||
type Props = {
|
||||
children?: () => any;
|
||||
};
|
||||
const { children }: Props = $props();
|
||||
const questionGroupID = Math.random().toString(36).substring(2, 15);
|
||||
setContext("radioGroup", questionGroupID);
|
||||
</script>
|
||||
|
||||
<div class="options-container">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { translateIfAvailable as translate } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
name?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
};
|
||||
let {
|
||||
value = $bindable(),
|
||||
name = "password",
|
||||
placeholder = "Enter your password",
|
||||
disabled = false,
|
||||
required = false,
|
||||
}: Props = $props();
|
||||
let showPassword = $state(false);
|
||||
const type = $derived.by(() => (showPassword ? "text" : "password"));
|
||||
const translatedPlaceholder = $derived.by(() => translate(placeholder));
|
||||
</script>
|
||||
|
||||
<input
|
||||
{type}
|
||||
{name}
|
||||
placeholder={translatedPlaceholder}
|
||||
bind:value
|
||||
{disabled}
|
||||
{required}
|
||||
spellcheck="false"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
/>
|
||||
<input type="checkbox" bind:checked={showPassword} />
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { setContext } from "svelte";
|
||||
|
||||
type Props = {
|
||||
children?: () => any;
|
||||
question?: () => any;
|
||||
};
|
||||
const { children, question }: Props = $props();
|
||||
const questionGroupID = Math.random().toString(36).substring(2, 15);
|
||||
setContext("radioGroup", questionGroupID);
|
||||
</script>
|
||||
|
||||
<div class="question-container">
|
||||
{#if question}<h3>{@render question?.()}</h3>{/if}
|
||||
<div class="question-content">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.question-container {
|
||||
border-bottom: 2px solid var(--interactive-accent);
|
||||
margin-bottom: 0.5lh;
|
||||
padding-bottom: 0.5lh;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
export type Props = {
|
||||
children?: () => any;
|
||||
};
|
||||
const { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="button-group">
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import type { GuestDialogProps } from "@/modules/services/LiveSyncUI/svelteDialog";
|
||||
import DialogHeader from "@/modules/services/LiveSyncUI/components/DialogHeader.svelte";
|
||||
import Instruction from "@/modules/services/LiveSyncUI/components/Instruction.svelte";
|
||||
import InputRow from "@/modules/services/LiveSyncUI/components/InputRow.svelte";
|
||||
import Decision from "@/modules/services/LiveSyncUI/components/Decision.svelte";
|
||||
import UserDecisions from "@/modules/services/LiveSyncUI/components/UserDecisions.svelte";
|
||||
import InfoNote from "@/modules/services/LiveSyncUI/components/InfoNote.svelte";
|
||||
const TYPE_OK = "ok";
|
||||
type ResultType = typeof TYPE_OK;
|
||||
type Options = {
|
||||
title?: string;
|
||||
dataToCopy: string;
|
||||
};
|
||||
type Props = GuestDialogProps<ResultType, Options>;
|
||||
const { setResult, getInitialData }: Props = $props();
|
||||
let dataToCopy = $state("");
|
||||
let title = $state<string | undefined>(undefined);
|
||||
let copied = $state(false);
|
||||
onMount(() => {
|
||||
if (getInitialData) {
|
||||
const initialData = getInitialData();
|
||||
if (initialData) {
|
||||
dataToCopy = initialData.dataToCopy;
|
||||
title = initialData.title;
|
||||
}
|
||||
}
|
||||
});
|
||||
function commit() {
|
||||
setResult(TYPE_OK);
|
||||
}
|
||||
async function copyToClipboard() {
|
||||
await navigator.clipboard.writeText(dataToCopy);
|
||||
copied = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<DialogHeader title="Your {title || 'Data'} is ready to be copied" />
|
||||
<Instruction>
|
||||
<InputRow label={title || "Data to Copy"}>
|
||||
<textarea readonly rows="4">{dataToCopy}</textarea>
|
||||
<button onclick={() => copyToClipboard()}
|
||||
>{#if !copied}📋{:else}✔️{/if}
|
||||
</button>
|
||||
</InputRow>
|
||||
</Instruction>
|
||||
<InfoNote visible={copied}>
|
||||
Your {title || "data"} has been copied to the clipboard.
|
||||
</InfoNote>
|
||||
<UserDecisions>
|
||||
<Decision title="OK" important={true} {commit} />
|
||||
</UserDecisions>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
export type {
|
||||
HasSetResult,
|
||||
HasGetInitialData,
|
||||
ComponentHasResult,
|
||||
GuestDialogProps,
|
||||
DialogSvelteComponentBaseProps,
|
||||
DialogControlBase,
|
||||
} from "@vrtmrz/livesync-commonlib/compat/services/implements/base/SvelteDialog";
|
||||
export {
|
||||
CONTEXT_DIALOG_CONTROLS,
|
||||
setupDialogContext,
|
||||
getDialogContext,
|
||||
SvelteDialogManagerBase,
|
||||
} from "@vrtmrz/livesync-commonlib/compat/services/implements/base/SvelteDialog";
|
||||
@@ -1,11 +1,11 @@
|
||||
import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import { InjectableAPIService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableAPIService";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import { Platform, type Command, type ViewCreator } from "@/deps.ts";
|
||||
import { ObsHttpHandler } from "@/modules/essentialObsidian/APILib/ObsHttpHandler";
|
||||
import { ObsidianConfirm } from "./ObsidianConfirm";
|
||||
import type { Confirm } from "@lib/interfaces/Confirm";
|
||||
import type { Confirm } from "@vrtmrz/livesync-commonlib/compat/interfaces/Confirm";
|
||||
import { requestUrl, type RequestUrlParam } from "@/deps";
|
||||
import { compatGlobal } from "@lib/common/coreEnvFunctions";
|
||||
import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
// All Services will be migrated to be based on Plain Services, not Injectable Services.
|
||||
// This is a migration step.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppLifecycleServiceBase } from "@lib/services/implements/injectable/InjectableAppLifecycleService";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import { AppLifecycleServiceBase } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableAppLifecycleService";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
declare module "obsidian" {
|
||||
interface App {
|
||||
commands: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type App, type Plugin, Notice } from "@/deps";
|
||||
import { scheduleTask, memoIfNotExist, memoObject, retrieveMemoObject, disposeMemoObject } from "@/common/utils";
|
||||
import { $msg } from "@lib/common/i18n";
|
||||
import type { Confirm } from "@lib/interfaces/Confirm";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import { $msg } from "@vrtmrz/livesync-commonlib/compat/common/i18n";
|
||||
import type { Confirm } from "@vrtmrz/livesync-commonlib/compat/interfaces/Confirm";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import {
|
||||
askYesNo,
|
||||
askString,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { initializeStores } from "@/common/stores";
|
||||
|
||||
// import { InjectableDatabaseService } from "@/lib/src/services/implements/injectable/InjectableDatabaseService";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import { DatabaseService, type DatabaseServiceDependencies } from "@lib/services/base/DatabaseService.ts";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import { DatabaseService, type DatabaseServiceDependencies } from "@vrtmrz/livesync-commonlib/compat/services/base/DatabaseService";
|
||||
|
||||
export class ObsidianDatabaseService<T extends ObsidianServiceContext> extends DatabaseService<T> {
|
||||
private __onOpenDatabase(vaultName: string) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import { normalizePath } from "@/deps";
|
||||
import { PathService } from "@lib/services/base/PathService";
|
||||
import { PathService } from "@vrtmrz/livesync-commonlib/compat/services/base/PathService";
|
||||
|
||||
import {
|
||||
type BASE_IS_NEW,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
compareFileFreshness,
|
||||
isMarkedAsSameChanges,
|
||||
} from "@/common/utils";
|
||||
import type { UXFileInfo, AnyEntry, UXFileInfoStub, FilePathWithPrefix } from "@lib/common/types";
|
||||
import type { UXFileInfo, AnyEntry, UXFileInfoStub, FilePathWithPrefix } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
export class ObsidianPathService extends PathService<ObsidianServiceContext> {
|
||||
override markChangesAreSame(
|
||||
old: UXFileInfo | AnyEntry | FilePathWithPrefix,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type ObsidianLiveSyncPlugin from "@/main";
|
||||
import type { App, Plugin } from "@/deps";
|
||||
import { ServiceContext } from "@vrtmrz/livesync-commonlib/context";
|
||||
import { eventHub } from "@/common/events";
|
||||
import { translateLiveSyncMessage } from "@/common/translation";
|
||||
|
||||
/** Host capabilities owned by one Self-hosted LiveSync plug-in instance. */
|
||||
export class ObsidianServiceContext extends ServiceContext {
|
||||
app: App;
|
||||
plugin: Plugin;
|
||||
liveSyncPlugin: ObsidianLiveSyncPlugin;
|
||||
|
||||
constructor(app: App, plugin: Plugin, liveSyncPlugin: ObsidianLiveSyncPlugin) {
|
||||
super({ events: eventHub, translate: translateLiveSyncMessage });
|
||||
this.app = app;
|
||||
this.plugin = plugin;
|
||||
this.liveSyncPlugin = liveSyncPlugin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { eventHub } from "@/common/events";
|
||||
import { translateLiveSyncMessage } from "@/common/translation";
|
||||
import { observeServiceContext } from "../../../test/contracts/serviceContext";
|
||||
import { ObsidianServiceContext } from "./ObsidianServiceContext";
|
||||
|
||||
const TRANSLATION_KEY = "Replicator.Message.InitialiseFatalError";
|
||||
|
||||
describe("ObsidianServiceContext contract", () => {
|
||||
it("preserves the plug-in capabilities and host-neutral API results", () => {
|
||||
type Parameters = ConstructorParameters<typeof ObsidianServiceContext>;
|
||||
const app = {} as Parameters[0];
|
||||
const plugin = {} as Parameters[1];
|
||||
const liveSyncPlugin = {} as Parameters[2];
|
||||
const context = new ObsidianServiceContext(app, plugin, liveSyncPlugin);
|
||||
|
||||
expect(observeServiceContext(context, TRANSLATION_KEY)).toEqual({
|
||||
translation: translateLiveSyncMessage(TRANSLATION_KEY),
|
||||
receivedEvents: ["context-contract-event"],
|
||||
});
|
||||
expect(context.events).toBe(eventHub);
|
||||
expect(context.app).toBe(app);
|
||||
expect(context.plugin).toBe(plugin);
|
||||
expect(context.liveSyncPlugin).toBe(liveSyncPlugin);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub";
|
||||
import { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import type { ServiceInstances } from "@lib/services/ServiceHub";
|
||||
import { InjectableServiceHub } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableServiceHub";
|
||||
import { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import type { ServiceInstances } from "@vrtmrz/livesync-commonlib/compat/services/ServiceHub";
|
||||
import type ObsidianLiveSyncPlugin from "@/main";
|
||||
import {
|
||||
ObsidianConflictService,
|
||||
@@ -23,6 +23,8 @@ import { ObsidianPathService } from "./ObsidianPathService";
|
||||
import { ObsidianVaultService } from "./ObsidianVaultService";
|
||||
import { ObsidianUIService } from "./ObsidianUIService";
|
||||
import { createScreenWakeLockManager } from "octagonal-wheels/browser/wakeLock";
|
||||
import { PouchDB } from "@vrtmrz/livesync-commonlib/compat/pouchdb/pouchdb-browser";
|
||||
import { OpenKeyValueDatabase } from "@/common/KeyValueDB";
|
||||
|
||||
// InjectableServiceHub
|
||||
|
||||
@@ -43,6 +45,7 @@ export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceCont
|
||||
settingService: setting,
|
||||
});
|
||||
const remote = new ObsidianRemoteService(context, {
|
||||
pouchDB: PouchDB,
|
||||
APIService: API,
|
||||
appLifecycle: appLifecycle,
|
||||
setting: setting,
|
||||
@@ -62,12 +65,14 @@ export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceCont
|
||||
return true;
|
||||
});
|
||||
const database = new ObsidianDatabaseService(context, {
|
||||
pouchDB: PouchDB,
|
||||
path: path,
|
||||
vault: vault,
|
||||
setting: setting,
|
||||
API: API,
|
||||
});
|
||||
const keyValueDB = new ObsidianKeyValueDBService(context, {
|
||||
openKeyValueDatabase: OpenKeyValueDatabase,
|
||||
appLifecycle: appLifecycle,
|
||||
databaseEvents: databaseEvents,
|
||||
vault: vault,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { InjectableConflictService } from "@lib/services/implements/injectable/InjectableConflictService";
|
||||
import { InjectableDatabaseEventService } from "@lib/services/implements/injectable/InjectableDatabaseEventService";
|
||||
import { InjectableFileProcessingService } from "@lib/services/implements/injectable/InjectableFileProcessingService";
|
||||
import { InjectableRemoteService } from "@lib/services/implements/injectable/InjectableRemoteService";
|
||||
import { InjectableReplicationService } from "@lib/services/implements/injectable/InjectableReplicationService";
|
||||
import { InjectableReplicatorService } from "@lib/services/implements/injectable/InjectableReplicatorService";
|
||||
import { InjectableTestService } from "@lib/services/implements/injectable/InjectableTestService";
|
||||
import { InjectableTweakValueService } from "@lib/services/implements/injectable/InjectableTweakValueService";
|
||||
import { ConfigServiceBrowserCompat } from "@lib/services/implements/browser/ConfigServiceBrowserCompat";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext.ts";
|
||||
import { KeyValueDBService } from "@lib/services/base/KeyValueDBService";
|
||||
import { ControlService } from "@lib/services/base/ControlService";
|
||||
import { InjectableConflictService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableConflictService";
|
||||
import { InjectableDatabaseEventService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableDatabaseEventService";
|
||||
import { InjectableFileProcessingService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableFileProcessingService";
|
||||
import { InjectableRemoteService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableRemoteService";
|
||||
import { InjectableReplicationService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableReplicationService";
|
||||
import { InjectableReplicatorService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableReplicatorService";
|
||||
import { InjectableTestService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableTestService";
|
||||
import { InjectableTweakValueService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableTweakValueService";
|
||||
import { ConfigServiceBrowserCompat } from "@vrtmrz/livesync-commonlib/compat/services/implements/browser/ConfigServiceBrowserCompat";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import { KeyValueDBService } from "@vrtmrz/livesync-commonlib/compat/services/base/KeyValueDBService";
|
||||
import { ControlService } from "@vrtmrz/livesync-commonlib/compat/services/base/ControlService";
|
||||
|
||||
export class ObsidianDatabaseEventService extends InjectableDatabaseEventService<ObsidianServiceContext> {}
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { compatGlobal } from "@lib/common/coreEnvFunctions";
|
||||
import { type ObsidianLiveSyncSettings } from "@lib/common/types";
|
||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED } from "@lib/events/coreEvents";
|
||||
import { eventHub } from "@lib/hub/hub";
|
||||
import { SettingService, type SettingServiceDependencies } from "@lib/services/base/SettingService";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
|
||||
import { type ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED } from "@vrtmrz/livesync-commonlib/compat/events/coreEvents";
|
||||
import { SettingService, type SettingServiceDependencies } from "@vrtmrz/livesync-commonlib/compat/services/base/SettingService";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
|
||||
export class ObsidianSettingService<T extends ObsidianServiceContext> extends SettingService<T> {
|
||||
constructor(context: T, dependencies: SettingServiceDependencies) {
|
||||
super(context, dependencies);
|
||||
this.onSettingSaved.addHandler((settings) => {
|
||||
eventHub.emitEvent(EVENT_SETTING_SAVED, settings);
|
||||
this.context.events.emitEvent(EVENT_SETTING_SAVED, settings);
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
this.onSettingLoaded.addHandler((settings) => {
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
this.context.events.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ConfigService } from "@lib/services/base/ConfigService";
|
||||
import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService";
|
||||
import type { ReplicatorService } from "@lib/services/base/ReplicatorService";
|
||||
import { UIService } from "@lib/services/implements/base/UIService";
|
||||
import { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import type { ConfigService } from "@vrtmrz/livesync-commonlib/compat/services/base/ConfigService";
|
||||
import type { AppLifecycleService } from "@vrtmrz/livesync-commonlib/compat/services/base/AppLifecycleService";
|
||||
import type { ReplicatorService } from "@vrtmrz/livesync-commonlib/compat/services/base/ReplicatorService";
|
||||
import { UIService } from "@vrtmrz/livesync-commonlib/compat/services/implements/base/UIService";
|
||||
import { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import { ObsidianSvelteDialogManager } from "./SvelteDialogObsidian";
|
||||
import DialogToCopy from "@lib/UI/dialogues/DialogueToCopy.svelte";
|
||||
import type { IAPIService, IControlService } from "@lib/services/base/IService";
|
||||
import DialogToCopy from "@/modules/services/LiveSyncUI/dialogues/DialogueToCopy.svelte";
|
||||
import type { IAPIService, IControlService } from "@vrtmrz/livesync-commonlib/compat/services/base/IService";
|
||||
export type ObsidianUIServiceDependencies<T extends ObsidianServiceContext = ObsidianServiceContext> = {
|
||||
appLifecycle: AppLifecycleService<T>;
|
||||
config: ConfigService<T>;
|
||||
@@ -28,7 +28,6 @@ export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
||||
control: dependents.control,
|
||||
});
|
||||
super(context, {
|
||||
appLifecycle: dependents.appLifecycle,
|
||||
dialogManager: obsidianSvelteDialogManager,
|
||||
APIService: dependents.APIService,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getPathFromTFile, isValidPath } from "@/common/utils";
|
||||
import { InjectableVaultService } from "@lib/services/implements/injectable/InjectableVaultService";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import type { FilePath } from "@lib/common/types";
|
||||
import { InjectableVaultService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableVaultService";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import type { FilePath } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
|
||||
declare module "obsidian" {
|
||||
interface DataAdapter {
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
SvelteDialogMixIn,
|
||||
type ComponentHasResult,
|
||||
type SvelteDialogManagerDependencies,
|
||||
} from "@lib/services/implements/base/SvelteDialog";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||
import DialogHost from "@lib/UI/DialogHost.svelte";
|
||||
} from "@vrtmrz/livesync-commonlib/compat/services/implements/base/SvelteDialog";
|
||||
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
|
||||
import DialogHost from "@/modules/services/LiveSyncUI/DialogHost.svelte";
|
||||
export const SvelteDialogBase = SvelteDialogMixIn(Modal, DialogHost);
|
||||
export class SvelteDialogObsidian<
|
||||
T,
|
||||
|
||||
Reference in New Issue
Block a user