Skip to content

Commit 385b47b

Browse files
committed
Root directory based activation
1 parent 34c9301 commit 385b47b

14 files changed

Lines changed: 637 additions & 154 deletions

README.md

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ For a sample knowledge base, clone https://github.com/appsoftwareltd/as-notes-de
308308

309309
### Initialise a workspace
310310

311-
AS Notes activates when it finds a `.asnotes/` directory in your workspace root (similar to `.git/` or `.obsidian/`). Without it, the extension runs in **passive mode** commands show a friendly notification prompting you to initialise, and the status bar invites you to set up.
311+
AS Notes activates when it finds a `.asnotes/` directory in your workspace root or configured `rootDirectory` subdirectory (similar to `.git/` or `.obsidian/`). Without it, the extension runs in **passive mode** -- commands show a friendly notification prompting you to initialise, and the status bar invites you to set up.
312312

313313
To initialise:
314314

@@ -317,6 +317,21 @@ To initialise:
317317

318318
This creates the `.asnotes/` directory, builds a SQLite index of all markdown files, and activates all features. The index file (`.asnotes/index.db`) is excluded from git by an auto-generated `.gitignore`.
319319

320+
### Using AS Notes alongside source code
321+
322+
AS Notes works well as a knowledge base inside a software project. You can keep notes, journals, and documentation in a subdirectory (e.g. `docs/` or `notes/`) while the rest of the repository contains source code. When a root directory is configured, all AS Notes features (wikilink highlighting, completions, hover tooltips, slash commands) are scoped to that directory. Markdown files outside it, such as a `README.md` at the workspace root, are completely unaffected.
323+
324+
During initialisation, the **Initialise Workspace** command will ask you to choose a location:
325+
326+
- **Workspace root** - the default, uses the entire workspace
327+
- **Choose a subdirectory** - opens a folder picker scoped to your workspace
328+
329+
The chosen path is saved as the `as-notes.rootDirectory` workspace setting. When set, all AS Notes data lives inside that directory: `.asnotes/`, `.asnotesignore`, journals, templates, notes, kanban boards, and the index. Scanning, file watching, and indexing are scoped to this directory so files outside it are unaffected.
330+
331+
If `as-notes.rootDirectory` is already configured before you run **Initialise Workspace**, the command uses the configured path directly.
332+
333+
> **Warning:** If you change `rootDirectory` after initialisation, you must manually move the notes directory (including `.asnotes/`) to the new location and reload the window. The extension will show a warning when the setting changes.
334+
320335
### Rebuild the index
321336

322337
If the index becomes stale or corrupted, run **AS Notes: Rebuild Index** from the Command Palette. This drops and recreates the entire index with a progress indicator.
@@ -328,11 +343,11 @@ If the extension is in a bad state (e.g. persistent WASM errors after a crash),
328343
- Removes the `.asnotes/` directory (index database, logs, git hook config)
329344
- Releases all in-memory state and switches to passive mode
330345

331-
`.asnotesignore` at the workspace root is intentionally preserved. Run **AS Notes: Initialise Workspace** afterwards to start fresh.
346+
`.asnotesignore` at the AS Notes root is intentionally preserved. Run **AS Notes: Initialise Workspace** afterwards to start fresh.
332347

333348
### Excluding files from the index
334349

335-
When AS Notes initialises a workspace it creates a `.asnotesignore` file at the workspace root. This file uses [`.gitignore` pattern syntax](https://git-scm.com/docs/gitignore) and controls which files and directories are excluded from the AS Notes index.
350+
When AS Notes initialises a workspace it creates a `.asnotesignore` file at the AS Notes root directory. This file uses [`.gitignore` pattern syntax](https://git-scm.com/docs/gitignore) and controls which files and directories are excluded from the AS Notes index.
336351

337352
**Default contents:**
338353

@@ -345,7 +360,7 @@ logseq/
345360
.trash/
346361
```
347362

348-
Patterns without a leading `/` match at any depth - `logseq/` excludes `logseq/pages/foo.md` and `vaults/work/logseq/pages/foo.md` equally. Prefix with `/` to anchor a pattern to the workspace root only (e.g. `/logseq/`).
363+
Patterns without a leading `/` match at any depth - `logseq/` excludes `logseq/pages/foo.md` and `vaults/work/logseq/pages/foo.md` equally. Prefix with `/` to anchor a pattern to the AS Notes root only (e.g. `/logseq/`).
349364

350365
Edit `.asnotesignore` at any time. AS Notes watches the file and automatically re-scans the index when it changes - newly ignored files are removed from the index and un-ignored files are added.
351366

@@ -569,7 +584,7 @@ The **AS Notes Kanban** sidebar and editor panel let you manage work visually wi
569584

570585
#### Boards
571586

572-
A workspace can contain any number of named boards, stored as plain files in a `kanban/` directory at the workspace root. Each board has its own lanes and set of cards.
587+
A workspace can contain any number of named boards, stored as plain files in a `kanban/` directory at the AS Notes root. Each board has its own lanes and set of cards.
573588

574589
- **Create a board** — run **AS Notes: Create Kanban Board** from the Command Palette and enter a name. The first board is selected automatically on activation.
575590
- **Switch board** — type in the board-switcher field in the sidebar to filter and select from existing boards. The editor panel opens automatically.
@@ -664,11 +679,12 @@ Front-matter holds the structured fields; the Markdown body is the card descript
664679

665680
| Setting | Default | Description |
666681
|---|---|---|
682+
| `as-notes.rootDirectory` | *(empty)* | Relative path from the workspace root to the AS Notes root directory (e.g. `docs` or `notes`). Leave empty to use the workspace root. All AS Notes data (`.asnotes/`, journals, templates, notes, kanban, `.asnotesignore`) lives within this directory. See [Using AS Notes in a subdirectory](#using-as-notes-in-a-subdirectory) below. |
667683
| `as-notes.periodicScanInterval` | `300` | Seconds between automatic background scans for file changes. Set to `0` to disable. Minimum: `30`. |
668-
| `as-notes.journalFolder` | `journals` | Folder for daily journal files, relative to workspace root. |
669-
| `as-notes.notesFolder` | `notes` | Folder for new notes, relative to workspace root. Used when creating pages via wikilink navigation and the Create Note / Create Encrypted Note commands. |
684+
| `as-notes.journalFolder` | `journals` | Folder for daily journal files, relative to the AS Notes root directory. |
685+
| `as-notes.notesFolder` | `notes` | Folder for new notes, relative to the AS Notes root directory. Used when creating pages via wikilink navigation and the Create Note / Create Encrypted Note commands. |
670686
| `as-notes.createNotesInCurrentDirectory` | `false` | When enabled, new notes created via wikilink navigation are placed in the current editing file's directory instead of the notes folder. Ignored when the source file is in the journal folder. |
671-
| `as-notes.templateFolder` | `templates` | Folder for note templates, relative to workspace root. Templates are markdown files inserted via the `/Template` slash command. |
687+
| `as-notes.templateFolder` | `templates` | Folder for note templates, relative to the AS Notes root directory. Templates are markdown files inserted via the `/Template` slash command. |
672688
| `as-notes.licenceKey` | *(empty)* | AS Notes Pro licence key (format: `ASNO-XXXX-XXXX-XXXX-XXXX`). Enter via **AS Notes: Enter Licence Key** in the Command Palette or directly in Settings. Scope: machine (not synced). |
673689
| `as-notes.enableLogging` | `false` | Enable diagnostic logging to `.asnotes/logs/`. Rolling 10 MB files, max 5. Requires reload after changing. Also activated by setting the `AS_NOTES_DEBUG=1` environment variable. |
674690

@@ -709,7 +725,7 @@ AS Notes directories can be managed via sync, though Git is recommended as it do
709725
The backlinks panel shows this message when the current file is not in the AS Notes index. Common causes:
710726

711727
- **VS Code `files.exclude` / `search.exclude` settings** - AS Notes uses `vscode.workspace.findFiles()` to discover markdown files, which respects these VS Code settings. Files in excluded folders (e.g. `logseq/version-files/`) are silently omitted from the scan and will never be indexed. Check **Settings → Files: Exclude** and **Settings → Search: Exclude** if a file you expect to be indexed is missing.
712-
- **`.asnotesignore` patterns** - Files matching patterns in `.asnotesignore` at the workspace root are excluded from the index. See [Excluding files from the index](#excluding-files-from-the-index) above.
728+
- **`.asnotesignore` patterns** - Files matching patterns in `.asnotesignore` at the AS Notes root are excluded from the index. See [Excluding files from the index](#excluding-files-from-the-index) above.
713729
- **File not yet saved** - New unsaved files are not indexed until they are saved to disk for the first time.
714730

715731
To resolve, check your workspace settings and `.asnotesignore` file. If the file should be indexed, ensure it is not matched by any exclusion pattern, then run **AS Notes: Rebuild Index** from the Command Palette.

TECHNICAL.md

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ This allows users to "uninitialise" a workspace by simply deleting the `.asnotes
426426

427427
## Activation model
428428

429-
The extension uses a `.asnotes/` directory in the workspace root as a project marker (analogous to `.git/` or `.obsidian/`). Its presence determines the operating mode.
429+
The extension uses a `.asnotes/` directory in the workspace root (or a configured `rootDirectory` subdirectory) as a project marker (analogous to `.git/` or `.obsidian/`). Its presence determines the operating mode.
430430

431431
### Passive mode
432432

@@ -558,12 +558,74 @@ The **AS Notes: Clean Workspace** command (`as-notes.cleanWorkspace`) performs a
558558
3. `fs.rmSync(.asnotes/, { recursive: true, force: true })` — deletes the entire `.asnotes/` directory tree (index database, logs, git hook config).
559559
4. Shows an informational message directing the user to run **Initialise Workspace** to start fresh.
560560

561-
`.asnotesignore` at the workspace root is intentionally preserved it is a user-editable, version-controlled configuration file.
561+
`.asnotesignore` at the AS Notes root is intentionally preserved -- it is a user-editable, version-controlled configuration file.
562562

563563
If the `.asnotes/` directory does not exist, the command shows an informational message and returns early. If the directory deletion fails (e.g. file lock on Windows), an error message is displayed.
564564

565565
---
566566

567+
## Root directory (subdirectory mode)
568+
569+
### Setting: `as-notes.rootDirectory`
570+
571+
The `as-notes.rootDirectory` setting allows AS Notes to operate in a subdirectory of the workspace rather than the workspace root. When empty (default), behaviour is identical to previous versions (full backward compatibility). When set to a relative path like `docs` or `notes/wiki`, all AS Notes data lives under that directory.
572+
573+
The setting is declared with `"scope": "resource"` in the extension manifest. This means VS Code resolves it per workspace folder rather than globally. The value is inherently workspace-specific (a relative path within that workspace), so it should always be set in Workspace settings, never in User settings. The `resource` scope ensures the setting appears in the correct section of the Settings UI and is stored in `.vscode/settings.json`.
574+
575+
### NotesRootService
576+
577+
`NotesRootService.ts` is a pure-logic module (no VS Code imports) that centralises all path computation related to the notes root:
578+
579+
| Export | Purpose |
580+
|---|---|
581+
| `normaliseRootDirectory(dir)` | Strips leading/trailing slashes, normalises separators |
582+
| `computeNotesRoot(workspaceRoot, rootDirectory)` | Returns the absolute notes root path |
583+
| `computeNotesRootPaths(workspaceRoot, rootDirectory)` | Returns a `NotesRootPaths` object with all well-known paths (`root`, `rootUri`, `asnotesDir`, `databasePath`, `logDir`, `ignoreFilePath`) |
584+
| `toNotesRelativePath(notesRoot, absolutePath)` | Computes a notes-root-relative path (replacing `vscode.workspace.asRelativePath()` for index lookups) |
585+
| `isInsideNotesRoot(notesRoot, absolutePath)` | Checks whether a path is inside the notes root |
586+
587+
### Architecture pattern
588+
589+
- **Single source of truth:** Module-level `notesRootPaths: NotesRootPaths` and `notesRootUri: vscode.Uri` in `extension.ts`, computed once during activation from the workspace root and `rootDirectory` setting.
590+
- **Service injection:** Services receive the notes root via constructor parameter (`WikilinkFileService`, `WikilinkRenameTracker`, `BacklinkPanelProvider`) or setter method (`SearchPanelProvider.setNotesRootUri()`, `TaskPanelProvider.setNotesRootUri()`).
591+
- **Scoped scanning:** `IndexScanner` uses `vscode.RelativePattern(notesRoot, '**/*.{md,markdown}')` to limit `findFiles()` to the notes root. The same pattern scopes encrypted file scanning.
592+
- **Notes-root-relative paths:** All paths stored in the index (the `pages.path` column) are relative to the notes root, not the workspace root. The `toNotesRelativePath()` function replaces `workspace.asRelativePath()` throughout event handlers, rename tracking, and backlink resolution.
593+
- **Fallback safety:** Where `notesRootPaths` may be null (e.g. before activation completes or during teardown), code falls back to `vscode.workspace.asRelativePath()` or `workspaceFolders[0]`.
594+
595+
### Initialisation flow
596+
597+
When **Initialise Workspace** runs and `rootDirectory` is not yet configured:
598+
599+
1. A `QuickPick` offers "Workspace root" or "Choose subdirectory..."
600+
2. If "Choose subdirectory..." is selected, `showOpenDialog` opens a folder picker scoped to the workspace
601+
3. The chosen relative path is saved to `as-notes.rootDirectory` as a workspace-scoped setting
602+
4. `.asnotes/`, `.asnotesignore`, and configured folders are created at the chosen root
603+
5. The extension enters full mode with scanning scoped to the chosen root
604+
605+
### Configuration change handling
606+
607+
When `as-notes.rootDirectory` changes after activation, the extension shows a warning: "AS Notes root directory changed. Reload the window for changes to take effect.", with a "Reload Window" action. The extension does not auto-migrate data.
608+
609+
### Feature scoping to the notes root
610+
611+
When `rootDirectory` is configured, AS Notes features are completely invisible for markdown files outside the notes root. A `README.md` at the workspace root will have no wikilink highlighting, no hover tooltips, no completions, and no slash commands.
612+
613+
**Language providers:** The `DocumentSelector` used for all `register*Provider` calls includes a `RelativePattern` when `rootDirectory` is set:
614+
615+
```typescript
616+
const markdownSelector = nrUri
617+
? { language: 'markdown', pattern: new RelativePattern(nrUri, '**') }
618+
: { language: 'markdown' };
619+
```
620+
621+
VS Code only invokes the provider when both the language and the pattern match. When `rootDirectory` is empty, no pattern is applied (all markdown files match, same as before).
622+
623+
**WikilinkDecorationManager:** Receives `notesRootFsPath` and guards `rebuildCacheAndDecorate()` and `applyDecorations()` with `isInsideNotesRoot()`. Files outside the root get stale decorations cleared.
624+
625+
**Event handlers:** All seven event handlers in `extension.ts` (`onDidSaveTextDocument`, `onDidChangeTextDocument` x2, `onDidCreateFiles`, `onDidDeleteFiles`, `onDidRenameFiles`, `onDidChangeActiveTextEditor`) include an early `isInsideNotesRoot()` bail-out. When `notesRootPaths` is null (backward compat), the guard is skipped.
626+
627+
---
628+
567629
## Aliases and front matter
568630

569631
### FrontMatterService

vs-code-extension/package.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@
222222
"configuration": {
223223
"title": "AS Notes",
224224
"properties": {
225+
"as-notes.rootDirectory": {
226+
"type": "string",
227+
"default": "",
228+
"scope": "resource",
229+
"description": "Subdirectory for AS Notes within the workspace (e.g. 'docs' or 'notes'). When empty, the workspace root is used. All AS Notes data (.asnotes, journals, templates, etc.) lives within this directory. Configure this per workspace, not globally. Warning: if you change this after initialisation, you must manually move your notes directory to the new location."
230+
},
225231
"as-notes.periodicScanInterval": {
226232
"type": "number",
227233
"default": 300,
@@ -231,7 +237,7 @@
231237
"as-notes.journalFolder": {
232238
"type": "string",
233239
"default": "journals",
234-
"description": "Folder for daily journal files, relative to workspace root."
240+
"description": "Folder for daily journal files, relative to the AS Notes root directory."
235241
},
236242
"as-notes.licenceKey": {
237243
"type": "string",
@@ -242,7 +248,7 @@
242248
"as-notes.assetPath": {
243249
"type": "string",
244250
"default": "assets/images",
245-
"description": "Workspace-relative folder where dropped or pasted files are saved by VS Code's built-in markdown editor. AS Notes configures the built-in markdown.copyFiles.destination setting to use this path."
251+
"description": "Folder where dropped or pasted files are saved by VS Code's built-in markdown editor, relative to the AS Notes root directory. AS Notes configures the built-in markdown.copyFiles.destination setting to use this path."
246252
},
247253
"as-notes.enableLogging": {
248254
"type": "boolean",
@@ -279,12 +285,12 @@
279285
"as-notes.templateFolder": {
280286
"type": "string",
281287
"default": "templates",
282-
"description": "Folder for note templates, relative to workspace root. Templates are markdown files that can be inserted via the /Template slash command."
288+
"description": "Folder for note templates, relative to the AS Notes root directory. Templates are markdown files that can be inserted via the /Template slash command."
283289
},
284290
"as-notes.notesFolder": {
285291
"type": "string",
286292
"default": "notes",
287-
"description": "Folder for new notes, relative to workspace root. Used when creating pages via wikilink navigation and the Create Note / Create Encrypted Note commands."
293+
"description": "Folder for new notes, relative to the AS Notes root directory. Used when creating pages via wikilink navigation and the Create Note / Create Encrypted Note commands."
288294
},
289295
"as-notes.createNotesInCurrentDirectory": {
290296
"type": "boolean",

vs-code-extension/src/BacklinkPanelProvider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
22
import { IndexService, type BacklinkChainGroup, type BacklinkChainInstance, type PageRow } from './IndexService.js';
33
import type { LogService } from './LogService.js';
44
import * as path from 'path';
5+
import { toNotesRelativePath } from './NotesRootService.js';
56

67
// ── Types ──────────────────────────────────────────────────────────────────
78

@@ -214,7 +215,7 @@ export class BacklinkPanelProvider implements vscode.Disposable {
214215
private renderBacklinksForUri(uri: vscode.Uri): void {
215216
if (!this.panel || !this.indexService.isOpen) { return; }
216217

217-
const relativePath = vscode.workspace.asRelativePath(uri, false);
218+
const relativePath = toNotesRelativePath(this.workspaceRoot.fsPath, uri.fsPath);
218219
this.logger?.info('BacklinkPanel', `renderBacklinksForUri: looking up path="${relativePath}" from uri="${uri.toString()}"`);
219220
const page = this.indexService.getPageByPath(relativePath);
220221

0 commit comments

Comments
 (0)