.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"onCommand:nostr.publishEventFile",
|
||||
"onCommand:nostr.validateEventFile",
|
||||
"onCommand:nostr.publishLongForm",
|
||||
"onCommand:nostr.publishTextNote",
|
||||
"onCommand:nostr.validateFrontMatter",
|
||||
"onCommand:nostr.fetchRelays",
|
||||
"onCommand:nostr.selectBackend",
|
||||
@@ -82,6 +83,12 @@
|
||||
"category": "Nostr",
|
||||
"enablement": "editorLangId == markdown"
|
||||
},
|
||||
{
|
||||
"command": "nostr.publishTextNote",
|
||||
"title": "Nostr: Publish Current File as Text Note",
|
||||
"category": "Nostr",
|
||||
"enablement": "resourceScheme == file || resourceScheme == untitled"
|
||||
},
|
||||
{
|
||||
"command": "nostr.validateFrontMatter",
|
||||
"title": "Nostr: Validate Front-Matter",
|
||||
@@ -132,6 +139,11 @@
|
||||
"when": "editorLangId == markdown",
|
||||
"group": "nostr@2"
|
||||
},
|
||||
{
|
||||
"command": "nostr.publishTextNote",
|
||||
"when": "resourceScheme == file || resourceScheme == untitled",
|
||||
"group": "nostr@3"
|
||||
},
|
||||
{
|
||||
"command": "nostr.insertNoteHeader",
|
||||
"when": "editorLangId == markdown",
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# Sidebar Aesthetics Plan
|
||||
|
||||
Apply the design rules from `~/lt/aesthetics/WEB.md` to the Nostr sidebar webview in [`src/ui/sidebar.ts`](../src/ui/sidebar.ts:1).
|
||||
|
||||
## Context
|
||||
|
||||
`WEB.md` defines a minimalist web aesthetic:
|
||||
|
||||
- **Monochrome first**: black, white, one red accent, plus one muted grey. No other hues.
|
||||
- **Monospaced by default**: one fixed-width family for body, buttons, inputs, labels.
|
||||
- **Minimal CSS surface**: few variables, few classes, reuse shell styles.
|
||||
- **Readable over clever**: 1px borders, single radius token, no gradients/shadows as decoration.
|
||||
- **Grayscale media**: images desaturated at rest, color returns on hover.
|
||||
- **Dark mode = swapped variables**, not swapped styles.
|
||||
- **Spacing ladder**: 5, 10, 15, 20, 40 px only.
|
||||
- **Transitions**: 0.2s for interactive feedback; 0.5s max for slide-outs.
|
||||
- **Accessibility**: tab-reachable, accent focus ring, alt text, reduced-motion respected.
|
||||
|
||||
The current sidebar is a VS Code webview that leans on `--vscode-*` theme variables with a generic VS Code look: mixed monospace/sans, blue-ish buttons, 2px radii, ad-hoc spacing (6px, 8px, 12px, 16px), no grayscale on avatars, no transitions.
|
||||
|
||||
## Reconciliation: WEB.md tokens vs VS Code theme
|
||||
|
||||
The sidebar lives inside VS Code, so it must respect the user's light/dark theme. We map the WEB.md four-token palette onto VS Code theme variables so the monochrome+accent system still tracks the active theme:
|
||||
|
||||
| WEB.md token | Sidebar value |
|
||||
|---------------------|------------------------------------------------------------|
|
||||
| `--primary-color` | `var(--vscode-foreground)` |
|
||||
| `--secondary-color` | `var(--vscode-sideBar-background)` |
|
||||
| `--accent-color` | `var(--vscode-statusBarItem-errorBackground, #ff0000)` — red, used only for hover/active/focus/destructive |
|
||||
| `--muted-color` | `var(--vscode-descriptionForeground)` |
|
||||
| `--border-color` | `var(--vscode-panel-border, var(--muted-color))` |
|
||||
|
||||
This keeps the "monochrome + one red scalpel" intent while remaining theme-aware. The accent resolves to red in most themes; where a theme overrides the error background we still get a single semantic accent.
|
||||
|
||||
## Changes
|
||||
|
||||
All changes are confined to the inline `<style>` and HTML structure inside [`NostrSidebarProvider.getHtml()`](../src/ui/sidebar.ts:196). No host-side TypeScript logic changes are required.
|
||||
|
||||
### 1. Variable block (`:root`)
|
||||
|
||||
Replace the current ad-hoc `:root` with the reconciled token set plus the WEB.md minimums:
|
||||
|
||||
- `--font-family: var(--vscode-editor-font-family), "Courier", monospace;` (monospace everywhere)
|
||||
- `--primary-color`, `--secondary-color`, `--accent-color`, `--muted-color`, `--border-color` as in the table above.
|
||||
- `--border-radius: 5px;` (single token, replaces the current 2px)
|
||||
- `--border-width: 1px;` and `--border: var(--border-width) solid var(--border-color);`
|
||||
- `--avatar-size-md: 32px;` (identity avatar)
|
||||
- `--image-grayscale: 100%;` and `--image-grayscale-hover: 20%;`
|
||||
- Spacing ladder documented as a comment: `5, 10, 15, 20, 40`.
|
||||
|
||||
### 2. Typography
|
||||
|
||||
- `* { font-family: var(--font-family); box-sizing: border-box; }`
|
||||
- Body uses `var(--font-family)` and `var(--vscode-font-size)`.
|
||||
- Section headings (`h2`) keep `11px`, `font-weight: 600`, uppercase, `letter-spacing: 0.5px`, color `var(--muted-color)` — already close to WEB.md's "UI chrome 14px / compact 12px" guidance; we keep 11px for the uppercase label style.
|
||||
- Relay URLs and npub already use the editor font; ensure all inputs/buttons/selects also inherit `var(--font-family)`.
|
||||
|
||||
### 3. Buttons
|
||||
|
||||
Adopt the WEB.md `.btn` model adapted to the sidebar's full-width buttons:
|
||||
|
||||
- Default: `color: var(--primary-color); background: var(--secondary-color); border: 1px solid var(--primary-color); border-radius: var(--border-radius); padding: 8px 10px; font-weight: bold; cursor: pointer; transition: border-color 0.2s, background-color 0.2s, color 0.2s;`
|
||||
- `:hover` → `border-color: var(--accent-color);` (never fill red on hover).
|
||||
- `:active` → `background: var(--accent-color); color: var(--secondary-color); border-color: var(--accent-color);`
|
||||
- `:disabled` → `opacity: 0.5; cursor: not-allowed;` (no recolor).
|
||||
- `.secondary` → swap to muted border: `border-color: var(--muted-color); color: var(--muted-color);` hover still goes to accent border.
|
||||
- Remove the old `--btn-bg`/`--btn2-bg` blue button styling.
|
||||
|
||||
### 4. Inputs / select
|
||||
|
||||
- Share border, radius, padding with buttons so they line up.
|
||||
- `border: var(--border); border-radius: var(--border-radius); padding: 8px 10px; background: var(--vscode-inputBackground); color: var(--vscode-inputForeground);`
|
||||
- `:focus` → `outline: none; border-color: var(--accent-color);` (accent focus ring, per WEB.md §7 and §14).
|
||||
- Labels (`.field-label`) stay muted grey, above inputs, smaller than input text.
|
||||
|
||||
### 5. Avatar / images
|
||||
|
||||
- Global `img { filter: grayscale(var(--image-grayscale)); transition: filter 0.2s; }` and `img:hover { filter: grayscale(var(--image-grayscale-hover)); }`.
|
||||
- `.avatar` and `.avatar-placeholder` use `--avatar-size-md: 32px`, `border-radius: var(--border-radius)` by default (square-with-radius per WEB.md §8; round variant only if we later opt in via `999px`).
|
||||
- Avatar `alt` text set to the profile name or `"avatar"` (currently `alt="avatar"` — keep, but prefer name when available).
|
||||
|
||||
### 6. Spacing normalization
|
||||
|
||||
Migrate current values onto the ladder:
|
||||
|
||||
| Current | New |
|
||||
|--------|-----|
|
||||
| `margin-bottom: 16px` (section) | `20px` |
|
||||
| `margin: 0 0 8px 0` (h2) | `0 0 10px 0` |
|
||||
| `padding: 6px 10px` (button) | `8px 10px` |
|
||||
| `margin-bottom: 6px` (button) | `10px` |
|
||||
| `padding: 4px 6px` (input) | `8px 10px` |
|
||||
| `margin: 4px 0 2px` (label) | `5px 0` |
|
||||
| `padding: 12px` (body) | `10px` (or `15px`) |
|
||||
| `gap: 6px` (relay row) | `10px` |
|
||||
| `gap: 8px` (identity row) | `10px` |
|
||||
|
||||
### 7. Relay list and identity block
|
||||
|
||||
- Relay rows: keep flex row, `gap: 10px`, `padding: 5px 0`, monospace URL with `text-overflow: ellipsis; white-space: nowrap;` (already present).
|
||||
- Identity block: treat as a single card — `border: var(--border); border-radius: var(--border-radius); padding: 10px; margin-bottom: 15px;` containing the avatar row, name, npub, backend label, and Sign Out button. This matches WEB.md §9 card conventions (1px border, 5–10px radius, 12px-ish padding, vertical flex).
|
||||
- Empty-state text stays italic muted grey (WEB.md allows italic for empty-state).
|
||||
|
||||
### 8. Transitions and reduced motion
|
||||
|
||||
- Add `transition: border-color 0.2s, background-color 0.2s, color 0.2s, filter 0.2s;` to buttons, inputs, checkboxes, and images.
|
||||
- Add `@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }`.
|
||||
|
||||
### 9. Accessibility pass
|
||||
|
||||
- Ensure every button/input/select has visible focus: accent border on inputs; for buttons add `:focus-visible { outline: 2px solid var(--accent-color); outline-offset: 1px; }`.
|
||||
- Relay checkboxes remain native (WEB.md §7: don't reinvent).
|
||||
- Confirm tab order: Actions → Relays → Fetch button → Identity fields → Sign In/Out.
|
||||
- Avatar `alt` text uses profile name when available, else `"avatar"`.
|
||||
|
||||
### 10. CSP
|
||||
|
||||
No CSP change needed. `style-src 'unsafe-inline'` already permits the inline styles; `img-src https: data:` already permits avatar loading. We are not adding any external resources.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- No changes to host-side message handling, state shape, or commands.
|
||||
- No new files; everything stays in [`sidebar.ts`](../src/ui/sidebar.ts:1).
|
||||
- No layout shell refactor (the sidebar is a single pane, not the five-region web shell from WEB.md §4 — that section informs token choices, not structure).
|
||||
- No dark-mode class toggle: VS Code drives light/dark via theme variables, which our token mapping already honors.
|
||||
|
||||
## Verification
|
||||
|
||||
1. Build the extension (`npm run build` via [`esbuild.mjs`](../esbuild.mjs:1)) and load it in VS Code.
|
||||
2. Open the Nostr sidebar and confirm:
|
||||
- All text is monospaced.
|
||||
- Buttons are outlined monochrome; hover shows red border; click flashes red fill.
|
||||
- Inputs show red border on focus.
|
||||
- Avatar (when signed in with a profile picture) is grayscale until hover.
|
||||
- Spacing feels even (no 6/8/12/16 oddities).
|
||||
- Disabled buttons are dimmed, not recolored.
|
||||
3. Toggle a light and a dark VS Code theme; confirm the palette tracks correctly and the accent stays red.
|
||||
4. Tab through the sidebar; confirm visible focus rings and logical order.
|
||||
5. Enable OS reduced-motion; confirm no transitions animate.
|
||||
|
||||
## Mermaid overview
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[WEB.md rules] --> B[Token mapping]
|
||||
B --> C[Typography: monospace]
|
||||
B --> D[Buttons: outline + red hover/active]
|
||||
B --> E[Inputs: accent focus]
|
||||
B --> F[Avatar: grayscale + hover reveal]
|
||||
B --> G[Spacing ladder 5/10/15/20/40]
|
||||
B --> H[Transitions 0.2s + reduced-motion]
|
||||
C --> I[sidebar.ts inline style + HTML]
|
||||
D --> I
|
||||
E --> I
|
||||
F --> I
|
||||
G --> I
|
||||
H --> I
|
||||
I --> J[Build + manual verify in VS Code]
|
||||
```
|
||||
@@ -73,6 +73,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
||||
cmdPublishLongForm(state, config),
|
||||
),
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("nostr.publishTextNote", () =>
|
||||
cmdPublishTextNote(state, config),
|
||||
),
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("nostr.validateFrontMatter", () =>
|
||||
cmdValidateFrontMatter(),
|
||||
@@ -860,6 +865,188 @@ async function cmdPublishLongForm(state: StateService, config: ConfigService): P
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Publish current file as a kind 1 text note
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Command: publish the active file as a kind 1 (short text) note.
|
||||
*
|
||||
* Smart content detection:
|
||||
* - If the file is JSON and parses to an object with a `content` string
|
||||
* (and optional `tags`), those are used as the note's content/tags. This
|
||||
* lets users author a note as `{"content": "...", "tags": [["t","nostr"]]}`
|
||||
* for full control over tags.
|
||||
* - Otherwise (plain text, markdown, or any non-JSON file), the raw file
|
||||
* text is used verbatim as the note content with no tags.
|
||||
*
|
||||
* The active editor's language id is not restricted — any text file works.
|
||||
*/
|
||||
async function cmdPublishTextNote(state: StateService, config: ConfigService): Promise<void> {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
vscode.window.showErrorMessage("No active editor.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Require sign-in.
|
||||
let signer = getSigner(state, config);
|
||||
if (!signer) {
|
||||
const action = await vscode.window.showErrorMessage(
|
||||
"Sign in to Nostr first before publishing.",
|
||||
"Sign In",
|
||||
);
|
||||
if (action === "Sign In") {
|
||||
await vscode.commands.executeCommand("nostr.signIn");
|
||||
}
|
||||
signer = getSigner(state, config);
|
||||
if (!signer) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const text = editor.document.getText();
|
||||
|
||||
// Detect JSON vs plain text.
|
||||
let content: string;
|
||||
let tags: string[][] = [];
|
||||
const trimmed = text.trim();
|
||||
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
||||
let parsed: unknown = undefined;
|
||||
try {
|
||||
parsed = JSON.parse(text);
|
||||
} catch {
|
||||
// Not valid JSON — fall through to plain-text handling.
|
||||
}
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
const obj = parsed as Record<string, unknown>;
|
||||
if (typeof obj.content === "string") {
|
||||
content = obj.content;
|
||||
if (Array.isArray(obj.tags)) {
|
||||
tags = (obj.tags as unknown[]).filter(
|
||||
(t): t is string[] => Array.isArray(t) && t.every((x) => typeof x === "string"),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// JSON object without a content field — use the raw text as content.
|
||||
content = text;
|
||||
}
|
||||
} else if (parsed !== undefined) {
|
||||
// Valid JSON but not an object (e.g. a string or array) — stringify it.
|
||||
content = JSON.stringify(parsed);
|
||||
} else {
|
||||
// JSON.parse failed — treat as plain text.
|
||||
content = text;
|
||||
}
|
||||
} else {
|
||||
content = text;
|
||||
}
|
||||
|
||||
if (content.length === 0) {
|
||||
vscode.window.showErrorMessage("The active file is empty — nothing to publish.");
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const template: EventTemplate = {
|
||||
kind: 1,
|
||||
created_at: now,
|
||||
tags,
|
||||
content,
|
||||
};
|
||||
|
||||
// Validate the built template.
|
||||
const validation = validateEventTemplate(template);
|
||||
if (!validation.valid) {
|
||||
vscode.window.showErrorMessage(`Built event is invalid: ${validation.errors.join("; ")}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sign.
|
||||
let signed: VerifiedEvent;
|
||||
try {
|
||||
signed = await signer.signEvent(template);
|
||||
} catch (err) {
|
||||
vscode.window.showErrorMessage(`Signing failed: ${(err as Error).message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Relays.
|
||||
const relays = sidebarProvider ? sidebarProvider.getEnabledRelays() : config.relays;
|
||||
if (relays.length === 0) {
|
||||
vscode.window.showErrorMessage(
|
||||
"No relays enabled. Toggle relays in the Nostr sidebar or set `nostr.relays` in settings.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirmation dialog.
|
||||
const selectedRelays = config.publishConfirm
|
||||
? await confirmTextNotePublish(signed, relays, signer)
|
||||
: relays;
|
||||
if (selectedRelays.length === 0) {
|
||||
vscode.window.showInformationMessage("Publish cancelled: no relays selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Publish.
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Publishing text note…",
|
||||
cancellable: false,
|
||||
},
|
||||
async () => {
|
||||
const results = await publishToRelays(signed, selectedRelays, config.publishTimeoutMs);
|
||||
showPublishResults(signed, results);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Confirmation dialog for kind 1 text note publish. */
|
||||
async function confirmTextNotePublish(
|
||||
signed: VerifiedEvent,
|
||||
relays: string[],
|
||||
signer: NostrSigner,
|
||||
): Promise<string[]> {
|
||||
const npub = hexToNpub(signed.pubkey);
|
||||
const preview = signed.content.length > 200
|
||||
? signed.content.slice(0, 200) + "…"
|
||||
: signed.content;
|
||||
const previewItems: vscode.QuickPickItem[] = [
|
||||
{ label: `kind: ${signed.kind}` },
|
||||
{ label: `pubkey: ${npub}` },
|
||||
{ label: `id: ${signed.id}` },
|
||||
{ label: `tags: ${signed.tags.length}` },
|
||||
{ label: `content: ${signed.content.length} chars` },
|
||||
{ label: `preview: ${preview.replace(/\n/g, " ")}` },
|
||||
{ label: `signer: ${signer.describe()}` },
|
||||
{ label: `relays: ${relays.join(", ")}` },
|
||||
{ label: "" },
|
||||
];
|
||||
|
||||
const relayItems: vscode.QuickPickItem[] = relays.map((r) => ({ label: r, picked: true }));
|
||||
|
||||
const pick = await vscode.window.showQuickPick([...previewItems, ...relayItems], {
|
||||
canPickMany: true,
|
||||
placeHolder: "Preview text note. Toggle relays, then confirm.",
|
||||
ignoreFocusOut: true,
|
||||
title: "Confirm Kind 1 Publish",
|
||||
});
|
||||
if (!pick) return [];
|
||||
|
||||
const selected = pick
|
||||
.filter((item) => item.label.startsWith("wss://") || item.label.startsWith("ws://"))
|
||||
.map((item) => item.label);
|
||||
if (selected.length === 0) return [];
|
||||
|
||||
const confirm = await vscode.window.showQuickPick(["Publish", "Cancel"], {
|
||||
placeHolder: `Publish to ${selected.length} relay(s)?`,
|
||||
ignoreFocusOut: true,
|
||||
});
|
||||
return confirm === "Publish" ? selected : [];
|
||||
}
|
||||
|
||||
/** Confirmation dialog for long-form publish with NIP-23 preview fields. */
|
||||
async function confirmLongFormPublish(
|
||||
signed: VerifiedEvent,
|
||||
|
||||
@@ -13,6 +13,7 @@ type WebviewMessage =
|
||||
| { type: "createEvent" }
|
||||
| { type: "publishCurrentFile" }
|
||||
| { type: "validateCurrentFile" }
|
||||
| { type: "publishTextNote" }
|
||||
| { type: "publishLongForm" }
|
||||
| { type: "validateFrontMatter" }
|
||||
| { type: "insertNoteHeader" }
|
||||
@@ -51,6 +52,7 @@ interface SidebarState {
|
||||
relays: { url: string; enabled: boolean }[];
|
||||
canPublish: boolean;
|
||||
canPublishLongForm: boolean;
|
||||
canPublishTextNote: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +119,9 @@ export class NostrSidebarProvider implements vscode.WebviewViewProvider {
|
||||
case "publishCurrentFile":
|
||||
await vscode.commands.executeCommand("nostr.publishEventFile");
|
||||
break;
|
||||
case "publishTextNote":
|
||||
await vscode.commands.executeCommand("nostr.publishTextNote");
|
||||
break;
|
||||
case "validateCurrentFile":
|
||||
await vscode.commands.executeCommand("nostr.validateEventFile");
|
||||
break;
|
||||
@@ -162,6 +167,7 @@ export class NostrSidebarProvider implements vscode.WebviewViewProvider {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const canPublish = !!editor && editor.document.languageId === "json";
|
||||
const canPublishLongForm = !!editor && editor.document.languageId === "markdown";
|
||||
const canPublishTextNote = !!editor && editor.document.uri.scheme !== "output";
|
||||
const pubkey = this.state.getPubkeyHex();
|
||||
let signerBackend = "local";
|
||||
if (this.config.signerBackend === "nsigner") {
|
||||
@@ -182,6 +188,7 @@ export class NostrSidebarProvider implements vscode.WebviewViewProvider {
|
||||
relays: relayList.map((url) => ({ url, enabled: this.enabledRelays.has(url) })),
|
||||
canPublish,
|
||||
canPublishLongForm,
|
||||
canPublishTextNote,
|
||||
};
|
||||
this.view.webview.postMessage({ type: "state", state: snap });
|
||||
}
|
||||
@@ -281,6 +288,7 @@ export class NostrSidebarProvider implements vscode.WebviewViewProvider {
|
||||
<h2>Actions</h2>
|
||||
<button id="createBtn">+ New Event</button>
|
||||
<button id="insertNoteHeaderBtn" disabled>Insert Note Header</button>
|
||||
<button id="publishTextNoteBtn" disabled>Publish Current File as Text Note</button>
|
||||
<button id="publishLongFormBtn" disabled>Publish Current File as Long-Form Note</button>
|
||||
<button id="validateFrontMatterBtn" class="secondary" disabled>Validate Front-Matter</button>
|
||||
<button id="publishBtn" disabled>Publish Current JSON File</button>
|
||||
@@ -365,6 +373,7 @@ export class NostrSidebarProvider implements vscode.WebviewViewProvider {
|
||||
$("createBtn").addEventListener("click", () => vscode.postMessage({ type: "createEvent" }));
|
||||
$("publishBtn").addEventListener("click", () => vscode.postMessage({ type: "publishCurrentFile" }));
|
||||
$("validateBtn").addEventListener("click", () => vscode.postMessage({ type: "validateCurrentFile" }));
|
||||
$("publishTextNoteBtn").addEventListener("click", () => vscode.postMessage({ type: "publishTextNote" }));
|
||||
$("publishLongFormBtn").addEventListener("click", () => vscode.postMessage({ type: "publishLongForm" }));
|
||||
$("validateFrontMatterBtn").addEventListener("click", () => vscode.postMessage({ type: "validateFrontMatter" }));
|
||||
$("insertNoteHeaderBtn").addEventListener("click", () => vscode.postMessage({ type: "insertNoteHeader" }));
|
||||
@@ -414,6 +423,7 @@ export class NostrSidebarProvider implements vscode.WebviewViewProvider {
|
||||
// Action button states
|
||||
$("publishBtn").disabled = !s.canPublish;
|
||||
$("validateBtn").disabled = !s.canPublish;
|
||||
$("publishTextNoteBtn").disabled = !s.canPublishTextNote;
|
||||
$("publishLongFormBtn").disabled = !s.canPublishLongForm;
|
||||
$("validateFrontMatterBtn").disabled = !s.canPublishLongForm;
|
||||
$("insertNoteHeaderBtn").disabled = !s.canPublishLongForm;
|
||||
|
||||
Reference in New Issue
Block a user