mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
feat(rename): complete searchSpace to workspace transition across frontend and backend
- Introduced a comprehensive specification for renaming `searchSpace` to `workspace` in `surfsense_web` and `surfsense_desktop`, ensuring all TypeScript identifiers, React props, and local data structures are updated. - Implemented migration shims for persisted local state to prevent data loss during the transition. - Updated observability metrics and IPC channels to reflect the new naming convention. - Removed legacy `active-search-space` module and replaced it with `active-workspace` to maintain consistency. - Ensured no behavioral changes or data loss for users during the renaming process.
This commit is contained in:
parent
2a020629c5
commit
a8c1fb660d
259 changed files with 5480 additions and 2285 deletions
|
|
@ -7,7 +7,7 @@ import type {
|
|||
NotePayload,
|
||||
RenameAck,
|
||||
RenameItem,
|
||||
SearchSpace,
|
||||
Workspace,
|
||||
SyncAck,
|
||||
} from "./types";
|
||||
|
||||
|
|
@ -94,14 +94,14 @@ export class SurfSenseApiClient {
|
|||
return await this.request<HealthResponse>("GET", "/api/v1/obsidian/health");
|
||||
}
|
||||
|
||||
async listSearchSpaces(): Promise<SearchSpace[]> {
|
||||
const resp = await this.request<SearchSpace[] | { items: SearchSpace[] }>(
|
||||
async listWorkspaces(): Promise<Workspace[]> {
|
||||
const resp = await this.request<Workspace[] | { items: Workspace[] }>(
|
||||
"GET",
|
||||
"/api/v1/searchspaces/"
|
||||
"/api/v1/workspaces/"
|
||||
);
|
||||
if (Array.isArray(resp)) return resp;
|
||||
if (resp && Array.isArray((resp as { items?: SearchSpace[] }).items)) {
|
||||
return (resp as { items: SearchSpace[] }).items;
|
||||
if (resp && Array.isArray((resp as { items?: Workspace[] }).items)) {
|
||||
return (resp as { items: Workspace[] }).items;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ export class SurfSenseApiClient {
|
|||
}
|
||||
|
||||
async connect(input: {
|
||||
searchSpaceId: number;
|
||||
workspaceId: number;
|
||||
vaultId: string;
|
||||
vaultName: string;
|
||||
vaultFingerprint: string;
|
||||
|
|
@ -125,7 +125,7 @@ export class SurfSenseApiClient {
|
|||
{
|
||||
vault_id: input.vaultId,
|
||||
vault_name: input.vaultName,
|
||||
search_space_id: input.searchSpaceId,
|
||||
workspace_id: input.workspaceId,
|
||||
vault_fingerprint: input.vaultFingerprint,
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export default class SurfSensePlugin extends Plugin {
|
|||
name: "Sync current note",
|
||||
checkCallback: (checking) => {
|
||||
const file = this.app.workspace.getActiveFile();
|
||||
if (!file || file.extension.toLowerCase() !== "md") return false;
|
||||
if (file?.extension.toLowerCase() !== "md") return false;
|
||||
if (checking) return true;
|
||||
this.queue.enqueueUpsert(file.path);
|
||||
void this.engine.flushQueue();
|
||||
|
|
@ -186,7 +186,7 @@ export default class SurfSensePlugin extends Plugin {
|
|||
if (!changed) return;
|
||||
this.engine?.refreshStatus();
|
||||
this.notifyStatusChange();
|
||||
if (this.settings.searchSpaceId !== null) {
|
||||
if (this.settings.workspaceId !== null) {
|
||||
void this.engine.ensureConnected();
|
||||
}
|
||||
}
|
||||
|
|
@ -252,16 +252,24 @@ export default class SurfSensePlugin extends Plugin {
|
|||
}
|
||||
|
||||
async loadSettings() {
|
||||
const data = (await this.loadData()) as Partial<SurfsensePluginSettings> | null;
|
||||
// One-time migration: the workspace was previously persisted as `searchSpaceId`.
|
||||
// Destructure it out so the migrated value moves into `workspaceId` and the
|
||||
// dead key is not spread back in / re-persisted.
|
||||
const data = (await this.loadData()) as
|
||||
| (Partial<SurfsensePluginSettings> & { searchSpaceId?: number | null })
|
||||
| null;
|
||||
const { searchSpaceId: legacyWorkspaceId, ...persisted } = data ?? {};
|
||||
this.settings = {
|
||||
...DEFAULT_SETTINGS,
|
||||
...(data ?? {}),
|
||||
queue: (data?.queue ?? []).map((i: QueueItem) => ({ ...i })),
|
||||
tombstones: { ...(data?.tombstones ?? {}) },
|
||||
includeFolders: [...(data?.includeFolders ?? [])],
|
||||
excludeFolders: [...(data?.excludeFolders ?? [])],
|
||||
excludePatterns: data?.excludePatterns?.length
|
||||
? [...data.excludePatterns]
|
||||
...persisted,
|
||||
workspaceId:
|
||||
persisted.workspaceId ?? legacyWorkspaceId ?? DEFAULT_SETTINGS.workspaceId,
|
||||
queue: (persisted.queue ?? []).map((i: QueueItem) => ({ ...i })),
|
||||
tombstones: { ...(persisted.tombstones ?? {}) },
|
||||
includeFolders: [...(persisted.includeFolders ?? [])],
|
||||
excludeFolders: [...(persisted.excludeFolders ?? [])],
|
||||
excludePatterns: persisted.excludePatterns?.length
|
||||
? [...persisted.excludePatterns]
|
||||
: [...DEFAULT_SETTINGS.excludePatterns],
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ import { normalizeFolder, parseExcludePatterns } from "./excludes";
|
|||
import { FolderSuggestModal } from "./folder-suggest-modal";
|
||||
import type SurfSensePlugin from "./main";
|
||||
import { STATUS_VISUALS } from "./status-visuals";
|
||||
import type { SearchSpace } from "./types";
|
||||
import type { Workspace } from "./types";
|
||||
|
||||
/** Plugin settings tab. */
|
||||
|
||||
export class SurfSenseSettingTab extends PluginSettingTab {
|
||||
private readonly plugin: SurfSensePlugin;
|
||||
private searchSpaces: SearchSpace[] = [];
|
||||
private workspaces: Workspace[] = [];
|
||||
private loadingSpaces = false;
|
||||
private connectionIndicator: HTMLElement | null = null;
|
||||
private readonly onStatusChange = (): void => this.updateConnectionIndicator();
|
||||
|
|
@ -51,7 +51,7 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
|||
const next = value.trim();
|
||||
const previous = this.plugin.settings.serverUrl;
|
||||
if (previous !== "" && next !== previous) {
|
||||
this.plugin.settings.searchSpaceId = null;
|
||||
this.plugin.settings.workspaceId = null;
|
||||
this.plugin.settings.connectorId = null;
|
||||
}
|
||||
this.plugin.settings.serverUrl = next;
|
||||
|
|
@ -80,7 +80,7 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
|||
const next = value.trim();
|
||||
const previous = this.plugin.settings.apiToken;
|
||||
if (previous !== "" && next !== previous) {
|
||||
this.plugin.settings.searchSpaceId = null;
|
||||
this.plugin.settings.workspaceId = null;
|
||||
this.plugin.settings.connectorId = null;
|
||||
}
|
||||
this.plugin.settings.apiToken = next;
|
||||
|
|
@ -102,7 +102,7 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
|||
await this.plugin.api.verifyToken();
|
||||
new Notice("Surfsense: token verified.");
|
||||
this.plugin.engine.refreshStatus({ force: true });
|
||||
await this.refreshSearchSpaces();
|
||||
await this.refreshWorkspaces();
|
||||
this.display();
|
||||
} catch (err) {
|
||||
this.handleApiError(err);
|
||||
|
|
@ -115,21 +115,21 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
|||
new Setting(containerEl)
|
||||
.setName("Search space")
|
||||
.setDesc(
|
||||
"Which Surfsense search space this vault syncs into. Reload after changing your token.",
|
||||
"Which Surfsense workspace this vault syncs into. Reload after changing your token.",
|
||||
)
|
||||
.addDropdown((drop) => {
|
||||
drop.addOption("", this.loadingSpaces ? "Loading…" : "Select a search space");
|
||||
for (const space of this.searchSpaces) {
|
||||
drop.addOption("", this.loadingSpaces ? "Loading…" : "Select a workspace");
|
||||
for (const space of this.workspaces) {
|
||||
drop.addOption(String(space.id), space.name);
|
||||
}
|
||||
if (settings.searchSpaceId !== null) {
|
||||
drop.setValue(String(settings.searchSpaceId));
|
||||
if (settings.workspaceId !== null) {
|
||||
drop.setValue(String(settings.workspaceId));
|
||||
}
|
||||
drop.onChange(async (value) => {
|
||||
this.plugin.settings.searchSpaceId = value ? Number(value) : null;
|
||||
this.plugin.settings.workspaceId = value ? Number(value) : null;
|
||||
this.plugin.settings.connectorId = null;
|
||||
await this.plugin.saveSettings();
|
||||
if (this.plugin.settings.searchSpaceId !== null) {
|
||||
if (this.plugin.settings.workspaceId !== null) {
|
||||
try {
|
||||
await this.plugin.engine.ensureConnected();
|
||||
await this.plugin.engine.maybeReconcile(true);
|
||||
|
|
@ -144,9 +144,9 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
|||
.addExtraButton((btn) =>
|
||||
btn
|
||||
.setIcon("refresh-ccw")
|
||||
.setTooltip("Reload search spaces")
|
||||
.setTooltip("Reload workspaces")
|
||||
.onClick(async () => {
|
||||
await this.refreshSearchSpaces();
|
||||
await this.refreshWorkspaces();
|
||||
this.display();
|
||||
}),
|
||||
);
|
||||
|
|
@ -319,13 +319,13 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
|||
indicator.setAttr("title", visual.label);
|
||||
}
|
||||
|
||||
private async refreshSearchSpaces(): Promise<void> {
|
||||
private async refreshWorkspaces(): Promise<void> {
|
||||
this.loadingSpaces = true;
|
||||
try {
|
||||
this.searchSpaces = await this.plugin.api.listSearchSpaces();
|
||||
this.workspaces = await this.plugin.api.listWorkspaces();
|
||||
} catch (err) {
|
||||
this.handleApiError(err);
|
||||
this.searchSpaces = [];
|
||||
this.workspaces = [];
|
||||
} finally {
|
||||
this.loadingSpaces = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export interface SyncEngineSettings {
|
|||
vaultId: string;
|
||||
apiToken: string;
|
||||
connectorId: number | null;
|
||||
searchSpaceId: number | null;
|
||||
workspaceId: number | null;
|
||||
includeFolders: string[];
|
||||
excludeFolders: string[];
|
||||
excludePatterns: string[];
|
||||
|
|
@ -96,7 +96,7 @@ export class SyncEngine {
|
|||
this.setStatus("syncing", "Connecting to SurfSense…");
|
||||
|
||||
const settings = this.deps.getSettings();
|
||||
if (!settings.searchSpaceId) {
|
||||
if (!settings.workspaceId) {
|
||||
// No target yet — /health still surfaces auth/network errors.
|
||||
try {
|
||||
const health = await this.deps.apiClient.health();
|
||||
|
|
@ -124,7 +124,7 @@ export class SyncEngine {
|
|||
*/
|
||||
async ensureConnected(): Promise<boolean> {
|
||||
const settings = this.deps.getSettings();
|
||||
if (!settings.searchSpaceId) {
|
||||
if (!settings.workspaceId) {
|
||||
this.setStatus("idle");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ export class SyncEngine {
|
|||
try {
|
||||
const fingerprint = await computeVaultFingerprint(this.deps.app);
|
||||
const resp = await this.deps.apiClient.connect({
|
||||
searchSpaceId: settings.searchSpaceId,
|
||||
workspaceId: settings.workspaceId,
|
||||
vaultId: settings.vaultId,
|
||||
vaultName: this.deps.app.vault.getName(),
|
||||
vaultFingerprint: fingerprint,
|
||||
|
|
@ -272,7 +272,7 @@ export class SyncEngine {
|
|||
this.refreshStatus({ force: true });
|
||||
return;
|
||||
}
|
||||
if (!settings.searchSpaceId) {
|
||||
if (!settings.workspaceId) {
|
||||
try {
|
||||
const health = await this.deps.apiClient.health();
|
||||
this.applyHealth(health);
|
||||
|
|
@ -543,7 +543,7 @@ export class SyncEngine {
|
|||
const isError =
|
||||
last === "auth-error" || last === "offline" || last === "error";
|
||||
const s = this.deps.getSettings();
|
||||
const setupComplete = !!(s.apiToken && s.searchSpaceId && s.connectorId);
|
||||
const setupComplete = !!(s.apiToken && s.workspaceId && s.connectorId);
|
||||
if (isError && setupComplete) return;
|
||||
}
|
||||
this.setStatus(this.queueStatusKind(), this.statusDetail());
|
||||
|
|
@ -571,7 +571,7 @@ export class SyncEngine {
|
|||
kind = "needs-setup";
|
||||
detail = this.setupHint(s);
|
||||
} else if (kind !== "auth-error" && kind !== "offline" && kind !== "error") {
|
||||
if (!s.searchSpaceId || !s.connectorId) {
|
||||
if (!s.workspaceId || !s.connectorId) {
|
||||
kind = "needs-setup";
|
||||
detail = this.setupHint(s);
|
||||
}
|
||||
|
|
@ -582,7 +582,7 @@ export class SyncEngine {
|
|||
|
||||
private setupHint(s: SyncEngineSettings): string {
|
||||
if (!s.apiToken) return "Paste your API token in settings.";
|
||||
if (!s.searchSpaceId) return "Pick a search space in settings.";
|
||||
if (!s.workspaceId) return "Pick a workspace in settings.";
|
||||
return "Connecting…";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
export interface SurfsensePluginSettings {
|
||||
serverUrl: string;
|
||||
apiToken: string;
|
||||
searchSpaceId: number | null;
|
||||
workspaceId: number | null;
|
||||
connectorId: number | null;
|
||||
/** UUID for the vault — lives here so Obsidian Sync replicates it across devices. */
|
||||
vaultId: string;
|
||||
|
|
@ -25,7 +25,7 @@ export interface SurfsensePluginSettings {
|
|||
export const DEFAULT_SETTINGS: SurfsensePluginSettings = {
|
||||
serverUrl: "https://surfsense.com",
|
||||
apiToken: "",
|
||||
searchSpaceId: null,
|
||||
workspaceId: null,
|
||||
connectorId: null,
|
||||
vaultId: "",
|
||||
syncIntervalMinutes: 10,
|
||||
|
|
@ -107,7 +107,7 @@ export interface HeadingRef {
|
|||
level: number;
|
||||
}
|
||||
|
||||
export interface SearchSpace {
|
||||
export interface Workspace {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
|
|
@ -117,7 +117,7 @@ export interface SearchSpace {
|
|||
export interface ConnectResponse {
|
||||
connector_id: number;
|
||||
vault_id: string;
|
||||
search_space_id: number;
|
||||
workspace_id: number;
|
||||
capabilities: string[];
|
||||
server_time_utc: string;
|
||||
[key: string]: unknown;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue