Skip to content

Commit f87048e

Browse files
committed
Gating inline editor features
1 parent a9d552e commit f87048e

2 files changed

Lines changed: 100 additions & 39 deletions

File tree

vs-code-extension/src/extension.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ let backlinkPanelProvider: BacklinkPanelProvider | undefined;
179179
/** Calendar panel provider instance -- alive while in full mode. */
180180
let calendarPanelProvider: CalendarPanelProvider | undefined;
181181

182+
/** Inline editor manager instance -- alive while in full mode. */
183+
let inlineEditorManager: InlineEditorManager | undefined;
184+
182185
/** Log service instance — alive while in full mode. */
183186
let logService: LogService = NO_OP_LOGGER;
184187

@@ -529,6 +532,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<{ exte
529532
vscode.window.showInformationMessage(`AS Notes: Licence activated - ${tierLabel} \u2714`);
530533
}
531534
updateFullModeStatusBar();
535+
inlineEditorManager?.refreshLicenceGate();
532536
}).catch((err) => {
533537
clearTimeout(progressTimer);
534538
console.warn('as-notes: licence validation failed:', err);
@@ -567,6 +571,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<{ exte
567571
verifyLicenceFromSettings(context).then((state) => {
568572
licenceState = state;
569573
updateFullModeStatusBar();
574+
inlineEditorManager?.refreshLicenceGate();
570575
}).catch((err) => {
571576
console.warn('as-notes: failed to verify licence from settings:', err);
572577
});
@@ -580,6 +585,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<{ exte
580585
showLicenceWarning();
581586
}
582587
updateFullModeStatusBar();
588+
inlineEditorManager?.refreshLicenceGate();
583589
}).catch((err) => {
584590
console.warn('as-notes: periodic licence check failed:', err);
585591
});
@@ -2211,14 +2217,20 @@ async function enterFullMode(
22112217
);
22122218

22132219
// Inline editor (Typora-like syntax shadowing)
2214-
const inlineEditorManager = new InlineEditorManager(
2220+
inlineEditorManager = new InlineEditorManager(
22152221
context,
22162222
markdownSelector,
22172223
nrp?.root,
22182224
ignoreService ? (rel) => ignoreService!.isIgnored(rel) : undefined,
2225+
() => hasProEditor(),
22192226
);
22202227
fullModeDisposables.push(inlineEditorManager);
22212228

2229+
// Apply licence gate now that the manager exists and licenceState may
2230+
// already be populated (verifyLicenceFromSettings can resolve before
2231+
// enterFullMode completes).
2232+
inlineEditorManager.refreshLicenceGate();
2233+
22222234
// Add all full-mode disposables to context
22232235
for (const d of fullModeDisposables) {
22242236
context.subscriptions.push(d);
@@ -2259,6 +2271,7 @@ function disposeFullMode(): void {
22592271
taskPanelProvider = undefined;
22602272
searchPanelProvider = undefined;
22612273
backlinkPanelProvider = undefined;
2274+
inlineEditorManager = undefined;
22622275
}
22632276

22642277
/**

vs-code-extension/src/inline-editor/InlineEditorManager.ts

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,24 @@ import { initMermaidRenderer, disposeMermaidRenderer } from './mermaid/mermaid-r
2020
*/
2121
export class InlineEditorManager implements vscode.Disposable {
2222
private readonly disposables: vscode.Disposable[] = [];
23+
private readonly providerDisposables: vscode.Disposable[] = [];
2324
private readonly decorator: Decorator;
2425
private readonly parseCache: MarkdownParseCache;
2526
private readonly linkClickHandler: LinkClickHandler;
27+
private readonly markdownSelector: vscode.DocumentSelector;
28+
private readonly hasProLicence: (() => boolean) | undefined;
29+
private providersRegistered = false;
2630

2731
constructor(
2832
context: vscode.ExtensionContext,
2933
markdownSelector: vscode.DocumentSelector,
3034
notesRootPath?: string,
3135
isIgnored?: (relativePath: string) => boolean,
36+
hasProLicence?: () => boolean,
3237
) {
38+
this.markdownSelector = markdownSelector;
39+
this.hasProLicence = hasProLicence;
40+
3341
// Mermaid renderer needs context for webview
3442
initMermaidRenderer(context);
3543

@@ -44,40 +52,21 @@ export class InlineEditorManager implements vscode.Disposable {
4452
this.decorator.updateDiffViewDecorationSetting(!diffViewApplyDecorations);
4553
this.decorator.setActiveEditor(vscode.window.activeTextEditor);
4654

47-
// Check if inline editor is enabled
48-
const inlineEditorEnabled = vscode.workspace
49-
.getConfiguration('as-notes.inlineEditor')
50-
.get<boolean>('enabled', true);
51-
if (!inlineEditorEnabled) {
55+
// Link click handler (created once, enabled/disabled dynamically)
56+
this.linkClickHandler = new LinkClickHandler(this.parseCache);
57+
58+
// Initial state: start based on shouldBeActive(). The licence callback
59+
// may return false during startup if verification hasn't completed yet;
60+
// enterFullMode() calls refreshLicenceGate() immediately after creation
61+
// to correct the state once the licence is known.
62+
if (!this.shouldBeActive()) {
5263
this.decorator.toggleDecorations(); // starts enabled, so toggle to disable
64+
this.linkClickHandler.setEnabled(false);
65+
} else {
66+
this.linkClickHandler.setEnabled(config.links.singleClickOpen());
67+
this.registerProviders();
5368
}
5469

55-
// Link provider
56-
const linkProvider = new MarkdownLinkProvider(this.parseCache);
57-
this.disposables.push(
58-
vscode.languages.registerDocumentLinkProvider(markdownSelector, linkProvider),
59-
);
60-
61-
// Hover providers
62-
this.disposables.push(
63-
vscode.languages.registerHoverProvider(
64-
markdownSelector,
65-
new MarkdownImageHoverProvider(this.parseCache),
66-
),
67-
vscode.languages.registerHoverProvider(
68-
markdownSelector,
69-
new MarkdownLinkHoverProvider(this.parseCache),
70-
),
71-
vscode.languages.registerHoverProvider(
72-
markdownSelector,
73-
new CodeBlockHoverProvider(this.parseCache),
74-
),
75-
);
76-
77-
// Link click handler
78-
this.linkClickHandler = new LinkClickHandler(this.parseCache);
79-
this.linkClickHandler.setEnabled(config.links.singleClickOpen());
80-
8170
// Toggle command
8271
this.disposables.push(
8372
vscode.commands.registerCommand('as-notes.toggleInlineEditor', () => {
@@ -142,13 +131,7 @@ export class InlineEditorManager implements vscode.Disposable {
142131
this.disposables.push(
143132
vscode.workspace.onDidChangeConfiguration((e) => {
144133
if (e.affectsConfiguration('as-notes.inlineEditor.enabled')) {
145-
const enabled = vscode.workspace
146-
.getConfiguration('as-notes.inlineEditor')
147-
.get<boolean>('enabled', true);
148-
const currentlyEnabled = this.decorator.isEnabled();
149-
if (enabled !== currentlyEnabled) {
150-
this.decorator.toggleDecorations();
151-
}
134+
this.refreshLicenceGate();
152135
}
153136
if (e.affectsConfiguration('as-notes.inlineEditor.defaultBehaviors.diffView.applyDecorations')) {
154137
const apply = config.diffView.applyDecorations();
@@ -187,6 +170,70 @@ export class InlineEditorManager implements vscode.Disposable {
187170
'codesmith.markdown-inline-editor-vscode',
188171
];
189172

173+
/** Whether the inline editor should be active (setting enabled AND pro licence). */
174+
private shouldBeActive(): boolean {
175+
const settingEnabled = vscode.workspace
176+
.getConfiguration('as-notes.inlineEditor')
177+
.get<boolean>('enabled', true);
178+
return settingEnabled && (!this.hasProLicence || this.hasProLicence());
179+
}
180+
181+
/** Register hover, link, and document-link providers. */
182+
private registerProviders(): void {
183+
if (this.providersRegistered) { return; }
184+
this.providersRegistered = true;
185+
186+
const linkProvider = new MarkdownLinkProvider(this.parseCache);
187+
this.providerDisposables.push(
188+
vscode.languages.registerDocumentLinkProvider(this.markdownSelector, linkProvider),
189+
);
190+
this.providerDisposables.push(
191+
vscode.languages.registerHoverProvider(
192+
this.markdownSelector,
193+
new MarkdownImageHoverProvider(this.parseCache),
194+
),
195+
vscode.languages.registerHoverProvider(
196+
this.markdownSelector,
197+
new MarkdownLinkHoverProvider(this.parseCache),
198+
),
199+
vscode.languages.registerHoverProvider(
200+
this.markdownSelector,
201+
new CodeBlockHoverProvider(this.parseCache),
202+
),
203+
);
204+
}
205+
206+
/** Dispose hover, link, and document-link providers. */
207+
private disposeProviders(): void {
208+
if (!this.providersRegistered) { return; }
209+
for (const d of this.providerDisposables) {
210+
d.dispose();
211+
}
212+
this.providerDisposables.length = 0;
213+
this.providersRegistered = false;
214+
}
215+
216+
/**
217+
* Re-evaluate whether the inline editor should be active based on the
218+
* current setting and licence state. Enables or disables decorations and
219+
* registers or disposes providers accordingly.
220+
*
221+
* Call this after licence state changes or when the enabled setting changes.
222+
*/
223+
refreshLicenceGate(): void {
224+
const active = this.shouldBeActive();
225+
226+
if (active && !this.decorator.isEnabled()) {
227+
this.decorator.toggleDecorations();
228+
this.linkClickHandler.setEnabled(config.links.singleClickOpen());
229+
this.registerProviders();
230+
} else if (!active && this.decorator.isEnabled()) {
231+
this.decorator.toggleDecorations();
232+
this.linkClickHandler.setEnabled(false);
233+
this.disposeProviders();
234+
}
235+
}
236+
190237
private static warnConflictingExtensions(): void {
191238
for (const id of InlineEditorManager.CONFLICTING_EXTENSIONS) {
192239
const ext = vscode.extensions.getExtension(id);
@@ -216,6 +263,7 @@ export class InlineEditorManager implements vscode.Disposable {
216263
for (const d of this.disposables) {
217264
d.dispose();
218265
}
266+
this.disposeProviders();
219267
this.decorator.dispose();
220268
this.linkClickHandler.dispose();
221269
disposeMermaidRenderer();

0 commit comments

Comments
 (0)