Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const DOCS_BASE_URL = "https://quickadd.obsidian.guide";

export const DOCS_URLS = {
gettingStarted: `${DOCS_BASE_URL}/docs/`,
formatSyntax: `${DOCS_BASE_URL}/docs/FormatSyntax/`,
onePageInputs: `${DOCS_BASE_URL}/docs/Advanced/onePageInputs/`,
userScripts: `${DOCS_BASE_URL}/docs/Choices/MacroChoice/#user-scripts`,
packages: `${DOCS_BASE_URL}/docs/Choices/Packages/`,
Expand Down
2 changes: 2 additions & 0 deletions src/gui/ChoiceBuilder/CaptureChoiceForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ChoiceNameHeader from "./components/ChoiceNameHeader.svelte";
import ValidatedInput from "./components/ValidatedInput.svelte";
import LabeledField from "./components/LabeledField.svelte";
import FormatPreviewField from "./components/FormatPreviewField.svelte";
import FormatTokenHint from "./components/FormatTokenHint.svelte";
import AppendLinkSetting from "./components/AppendLinkSetting.svelte";
import OpenFileSetting from "./components/OpenFileSetting.svelte";
import FileOpeningSetting from "./components/FileOpeningSetting.svelte";
Expand Down Expand Up @@ -170,6 +171,7 @@ function onTemplaterAfterCaptureChange(value: boolean) {
requiredMessage="Capture format is required when enabled"
makeSuggesters={formatSuggesters}
/>
<FormatTokenHint value={choice.format.format} />
<FormatPreviewField value={choice.format.format} {app} {plugin} />
{/snippet}
</LabeledField>
Expand Down
2 changes: 2 additions & 0 deletions src/gui/ChoiceBuilder/TemplateChoiceForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import ChoiceNameHeader from "./components/ChoiceNameHeader.svelte";
import ValidatedInput from "./components/ValidatedInput.svelte";
import LabeledField from "./components/LabeledField.svelte";
import FormatPreviewField from "./components/FormatPreviewField.svelte";
import FormatTokenHint from "./components/FormatTokenHint.svelte";
import AppendLinkSetting from "./components/AppendLinkSetting.svelte";
import OpenFileSetting from "./components/OpenFileSetting.svelte";
import FileOpeningSetting from "./components/FileOpeningSetting.svelte";
Expand Down Expand Up @@ -222,6 +223,7 @@ function onModeChange(value: string) {
placeholder="File name format"
makeSuggesters={fileNameSuggesters}
/>
<FormatTokenHint value={choice.fileNameFormat.format} />
<FormatPreviewField
value={choice.fileNameFormat.format}
formatterKind="fileName"
Expand Down
2 changes: 2 additions & 0 deletions src/gui/ChoiceBuilder/components/CaptureTargetSetting.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Toggle from "../../components/Toggle.svelte";
import ValidatedInput from "./ValidatedInput.svelte";
import LabeledField from "./LabeledField.svelte";
import FormatPreviewField from "./FormatPreviewField.svelte";
import FormatTokenHint from "./FormatTokenHint.svelte";
import CanvasNodePicker from "./CanvasNodePicker.svelte";
import { getCaptureTargetFeedback } from "./captureTargetFeedback";

Expand Down Expand Up @@ -146,6 +147,7 @@ function validateCaptureTo(value: string) {
validator={validateCaptureTo}
onChange={onCaptureToChange}
/>
<FormatTokenHint value={choice.captureTo} />
{#if !usesPickerTargetSyntax}
<FormatPreviewField value={choice.captureTo} formatterKind="fileName" {app} {plugin} />
{/if}
Expand Down
35 changes: 35 additions & 0 deletions src/gui/ChoiceBuilder/components/FormatTokenHint.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { DOCS_URLS } from "../../../docs";

/**
* Advertises the format-token autocomplete under a format field (issue #1542).
*
* Typing `{{` opens a list of every token, each with a one-line description, and
* nothing in the UI said so — the reporter only found it on a hunch. The
* affordance is two characters, so the cheapest honest fix is to name the two
* characters, right below the caret. It doubles as the docs entry point #1541
* deferred to this issue: the format language is the one thing you cannot guess
* from the builder alone.
*
* Hidden once the value contains `{{`: the hint has done its job, and a line
* that never goes away is the permanent chrome an "Insert token" button was
* rejected for. Deliberately NOT persisted in settings — a per-vault flag would
* mean a disk write from a keystroke, would not be reactive, and one stray `{{`
* would delete the affordance vault-wide with no way back.
*/
let { value }: { value: string } = $props();

const shown = $derived(!value.includes("{{"));
</script>

{#if shown}
<div class="qa-token-hint">
Type <code>&#123;&#123;</code> to insert a token &middot;
<a
class="quickadd-docs-link"
href={DOCS_URLS.formatSyntax}
target="_blank"
rel="noopener noreferrer">Format syntax</a
>
</div>
{/if}
52 changes: 52 additions & 0 deletions src/gui/ChoiceBuilder/components/FormatTokenHint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import { render } from "@testing-library/svelte";
import { tick } from "svelte";
import FormatTokenHint from "./FormatTokenHint.svelte";
import { DOCS_URLS } from "../../../docs";

const hint = (container: HTMLElement) =>
container.querySelector(".qa-token-hint");

/**
* Issue #1542, ask 1. The token autocomplete opens on `{{` and nothing said so.
*/
describe("FormatTokenHint", () => {
it("names the two characters that open the autocomplete", async () => {
const { container } = render(FormatTokenHint, { props: { value: "" } });
await tick();

const text = hint(container as HTMLElement)?.textContent ?? "";
expect(text).toContain("{{");
expect(text).toContain("to insert a token");
});

it("links the format syntax reference", async () => {
const { container } = render(FormatTokenHint, { props: { value: "" } });
await tick();

const link = container.querySelector("a.quickadd-docs-link");
expect(link?.getAttribute("href")).toBe(DOCS_URLS.formatSyntax);
expect(link?.getAttribute("target")).toBe("_blank");
expect(link?.getAttribute("rel")).toBe("noopener noreferrer");
});

it("stays out of the way once the field uses a token", async () => {
const { container } = render(FormatTokenHint, {
props: { value: "- [ ] {{VALUE}}" },
});
await tick();
expect(hint(container as HTMLElement)).toBeNull();
});

it("shows again if the user removes every token", async () => {
const { container, rerender } = render(FormatTokenHint, {
props: { value: "{{DATE}}" },
});
await tick();
expect(hint(container as HTMLElement)).toBeNull();

await rerender({ value: "plain text" });
await tick();
expect(hint(container as HTMLElement)).not.toBeNull();
});
});
13 changes: 13 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,19 @@
border: 0;
}

/* "Type {{ to insert a token" under a format field (#1542). A quiet secondary
line, matching .qa-preview-row: it is a teaching aid, not part of the field. */
.quickAddModal .qa-token-hint {
margin: -4px 0 8px;
font-size: var(--font-ui-smaller);
color: var(--text-muted);
}
.quickAddModal .qa-token-hint code {
font-size: inherit;
padding: 1px 4px;
border-radius: var(--radius-s, 4px);
}

/* Problems the preview pass ran into, shown here instead of as a Notice per
keystroke (#1558). Colour carries severity — no glyph — matching
.qa-folder-mode-warning. Clamped with the full text in `title`, because the
Expand Down