fix: fixed bookmarklet for cold start finally, hopefully, maybe
This commit is contained in:
@@ -428,6 +428,49 @@ function registerBlogmarkProtocolClient(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function initializeActiveProjectContext(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const { getProjectEngine } = await import('./engine/ProjectEngine');
|
||||||
|
const projectEngine = getProjectEngine();
|
||||||
|
const project = await projectEngine.getActiveProject();
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataDir = projectEngine.getDataDir(project.id, project.dataPath);
|
||||||
|
const postEngine = getPostEngine() as {
|
||||||
|
setProjectContext?: (projectId: string, dataDir?: string) => void;
|
||||||
|
setSearchLanguage?: (language: string) => void;
|
||||||
|
};
|
||||||
|
const mediaEngine = getMediaEngine() as {
|
||||||
|
setProjectContext?: (projectId: string, dataDir?: string, internalDir?: string) => void;
|
||||||
|
setSearchLanguage?: (language: string) => void;
|
||||||
|
};
|
||||||
|
const metaEngine = getMetaEngine() as {
|
||||||
|
setProjectContext?: (projectId: string, dataDir?: string) => void;
|
||||||
|
syncOnStartup?: () => Promise<void>;
|
||||||
|
getProjectMetadata?: () => Promise<{ mainLanguage?: string } | null>;
|
||||||
|
};
|
||||||
|
|
||||||
|
postEngine.setProjectContext?.(project.id, dataDir);
|
||||||
|
mediaEngine.setProjectContext?.(project.id, dataDir, dataDir);
|
||||||
|
metaEngine.setProjectContext?.(project.id, dataDir);
|
||||||
|
|
||||||
|
await metaEngine.syncOnStartup?.();
|
||||||
|
|
||||||
|
const metadata = await metaEngine.getProjectMetadata?.();
|
||||||
|
if (metadata?.mainLanguage) {
|
||||||
|
const { isoToStemmerLanguage } = await import('./engine/stemmer');
|
||||||
|
const stemmerLang = isoToStemmerLanguage(metadata.mainLanguage);
|
||||||
|
postEngine.setSearchLanguage?.(stemmerLang);
|
||||||
|
mediaEngine.setSearchLanguage?.(stemmerLang);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize active project context:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createApplicationMenu(): Menu {
|
function createApplicationMenu(): Menu {
|
||||||
const systemLocale = typeof app.getLocale === 'function' ? app.getLocale() : 'en';
|
const systemLocale = typeof app.getLocale === 'function' ? app.getLocale() : 'en';
|
||||||
const uiLanguage = resolveUiLanguageFromSystemLocale(systemLocale);
|
const uiLanguage = resolveUiLanguageFromSystemLocale(systemLocale);
|
||||||
@@ -764,7 +807,7 @@ app.on('open-url', (event, deepLink) => {
|
|||||||
// App lifecycle
|
// App lifecycle
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
await initialize();
|
await initialize();
|
||||||
appInitialized = true;
|
const activeProjectContextReady = initializeActiveProjectContext();
|
||||||
registerBlogmarkProtocolClient();
|
registerBlogmarkProtocolClient();
|
||||||
try {
|
try {
|
||||||
await startPreviewServerOnAppStart();
|
await startPreviewServerOnAppStart();
|
||||||
@@ -773,6 +816,9 @@ app.whenReady().then(async () => {
|
|||||||
}
|
}
|
||||||
createWindow();
|
createWindow();
|
||||||
|
|
||||||
|
await activeProjectContextReady;
|
||||||
|
appInitialized = true;
|
||||||
|
|
||||||
const startupDeepLinks = extractBlogmarkDeepLinks(process.argv);
|
const startupDeepLinks = extractBlogmarkDeepLinks(process.argv);
|
||||||
for (const deepLink of startupDeepLinks) {
|
for (const deepLink of startupDeepLinks) {
|
||||||
enqueueBlogmarkDeepLink(deepLink);
|
enqueueBlogmarkDeepLink(deepLink);
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ export function extractBlogmarkPayloadFromDeepLink(rawDeepLink: string): Blogmar
|
|||||||
|
|
||||||
export function buildBlogmarkMarkdownLink(title: string, url: string): string {
|
export function buildBlogmarkMarkdownLink(title: string, url: string): string {
|
||||||
const safeTitle = escapeMarkdownLinkText(title.trim());
|
const safeTitle = escapeMarkdownLinkText(title.trim());
|
||||||
return `[${safeTitle}](<${url}>)`;
|
return `[${safeTitle}](${url})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateBlogmarkBookmarkletSource(): string {
|
export function generateBlogmarkBookmarkletSource(): string {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ describe('blogmark deep-link payload', () => {
|
|||||||
|
|
||||||
it('builds safe markdown source link', () => {
|
it('builds safe markdown source link', () => {
|
||||||
const markdown = buildBlogmarkMarkdownLink('A [title] (test)', 'https://example.com/x?y=1');
|
const markdown = buildBlogmarkMarkdownLink('A [title] (test)', 'https://example.com/x?y=1');
|
||||||
expect(markdown).toBe('[A \\[title\\] \\(test\\)](<https://example.com/x?y=1>)');
|
expect(markdown).toBe('[A \\[title\\] \\(test\\)](https://example.com/x?y=1)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates bookmarklet that targets bds protocol', () => {
|
it('generates bookmarklet that targets bds protocol', () => {
|
||||||
|
|||||||
@@ -719,7 +719,7 @@ describe('main bootstrap preview behavior', () => {
|
|||||||
const createPost = vi.fn().mockResolvedValue({
|
const createPost = vi.fn().mockResolvedValue({
|
||||||
id: 'new-post-id',
|
id: 'new-post-id',
|
||||||
title: 'Example title',
|
title: 'Example title',
|
||||||
content: '[Example title](<https://example.com/>)',
|
content: '[Example title](https://example.com/)',
|
||||||
categories: ['article'],
|
categories: ['article'],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -792,7 +792,7 @@ describe('main bootstrap preview behavior', () => {
|
|||||||
expect(createPost).toHaveBeenCalledWith(
|
expect(createPost).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: 'Example title',
|
title: 'Example title',
|
||||||
content: '[Example title](<https://example.com/>)',
|
content: '[Example title](https://example.com/)',
|
||||||
categories: ['article'],
|
categories: ['article'],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -882,7 +882,7 @@ describe('main bootstrap preview behavior', () => {
|
|||||||
const createPost = vi.fn().mockResolvedValue({
|
const createPost = vi.fn().mockResolvedValue({
|
||||||
id: 'queued-post-id',
|
id: 'queued-post-id',
|
||||||
title: 'Queued title',
|
title: 'Queued title',
|
||||||
content: '[Queued title](<https://example.com/>)',
|
content: '[Queued title](https://example.com/)',
|
||||||
categories: ['article'],
|
categories: ['article'],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -974,4 +974,161 @@ describe('main bootstrap preview behavior', () => {
|
|||||||
expect.objectContaining({ id: 'queued-post-id' }),
|
expect.objectContaining({ id: 'queued-post-id' }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses active project blogmark category during cold-start deep-link processing', async () => {
|
||||||
|
const originalArgv = process.argv;
|
||||||
|
process.argv = [
|
||||||
|
'bds',
|
||||||
|
'bds://new-post?title=Cold%20start&url=https%3A%2F%2Fexample.com%2Fcold',
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockApp = {
|
||||||
|
name: 'bDS',
|
||||||
|
whenReady: vi.fn(() => Promise.resolve()),
|
||||||
|
on: vi.fn(),
|
||||||
|
quit: vi.fn(),
|
||||||
|
requestSingleInstanceLock: vi.fn(() => true),
|
||||||
|
setAsDefaultProtocolClient: vi.fn(() => true),
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockBrowserWindow {
|
||||||
|
static getAllWindows = vi.fn(() => [{ id: 1 }]);
|
||||||
|
|
||||||
|
loadURL = vi.fn();
|
||||||
|
loadFile = vi.fn();
|
||||||
|
on = vi.fn();
|
||||||
|
isDestroyed = vi.fn(() => false);
|
||||||
|
webContents = {
|
||||||
|
on: vi.fn(),
|
||||||
|
send: vi.fn(),
|
||||||
|
openDevTools: vi.fn(),
|
||||||
|
toggleDevTools: vi.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.doMock('electron', () => ({
|
||||||
|
app: mockApp,
|
||||||
|
BrowserWindow: MockBrowserWindow,
|
||||||
|
Menu: {
|
||||||
|
buildFromTemplate: vi.fn(() => ({})),
|
||||||
|
setApplicationMenu: vi.fn(),
|
||||||
|
},
|
||||||
|
ipcMain: {
|
||||||
|
on: vi.fn(),
|
||||||
|
handle: vi.fn(),
|
||||||
|
removeHandler: vi.fn(),
|
||||||
|
},
|
||||||
|
protocol: {
|
||||||
|
registerSchemesAsPrivileged: vi.fn(),
|
||||||
|
handle: vi.fn(),
|
||||||
|
},
|
||||||
|
net: {
|
||||||
|
fetch: vi.fn(),
|
||||||
|
},
|
||||||
|
shell: {
|
||||||
|
openExternal: vi.fn(),
|
||||||
|
openPath: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
class MockPreviewServer {
|
||||||
|
start = vi.fn().mockResolvedValue(4123);
|
||||||
|
stop = vi.fn().mockResolvedValue(undefined);
|
||||||
|
getBaseUrl = vi.fn(() => 'http://127.0.0.1:4123');
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPost = vi.fn().mockResolvedValue({
|
||||||
|
id: 'cold-start-id',
|
||||||
|
title: 'Cold start',
|
||||||
|
content: '[Cold start](https://example.com/cold)',
|
||||||
|
categories: ['aside'],
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/engine/PreviewServer', () => ({
|
||||||
|
PreviewServer: MockPreviewServer,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/engine/PostEngine', () => ({
|
||||||
|
getPostEngine: vi.fn(() => ({
|
||||||
|
getPost: vi.fn().mockResolvedValue(null),
|
||||||
|
createPost,
|
||||||
|
setProjectContext: vi.fn(),
|
||||||
|
setSearchLanguage: vi.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let currentProjectId = 'default';
|
||||||
|
vi.doMock('../../src/main/engine/MetaEngine', () => ({
|
||||||
|
getMetaEngine: vi.fn(() => ({
|
||||||
|
setProjectContext: vi.fn((projectId: string) => {
|
||||||
|
currentProjectId = projectId;
|
||||||
|
}),
|
||||||
|
syncOnStartup: vi.fn().mockResolvedValue(undefined),
|
||||||
|
getProjectMetadata: vi.fn(async () => ({
|
||||||
|
blogmarkCategory: currentProjectId === 'project-2' ? 'aside' : 'article',
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/engine/ProjectEngine', () => ({
|
||||||
|
getProjectEngine: vi.fn(() => ({
|
||||||
|
getActiveProject: vi.fn().mockResolvedValue({
|
||||||
|
id: 'project-2',
|
||||||
|
dataPath: '/tmp/project-2',
|
||||||
|
}),
|
||||||
|
getDataDir: vi.fn(() => '/tmp/project-2'),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/database', () => ({
|
||||||
|
getDatabase: vi.fn(() => ({
|
||||||
|
initializeLocal: vi.fn().mockResolvedValue(undefined),
|
||||||
|
close: vi.fn().mockResolvedValue(undefined),
|
||||||
|
getLocal: vi.fn(() => ({
|
||||||
|
select: vi.fn(() => ({
|
||||||
|
from: vi.fn(() => ({
|
||||||
|
where: vi.fn(() => ({
|
||||||
|
get: vi.fn().mockResolvedValue(null),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
getDataPaths: vi.fn(() => ({ database: '/tmp/mock.db' })),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/ipc', () => ({
|
||||||
|
registerIpcHandlers: vi.fn(),
|
||||||
|
registerChatHandlers: vi.fn(),
|
||||||
|
initializeChatHandlers: vi.fn(),
|
||||||
|
cleanupChatHandlers: vi.fn().mockResolvedValue(undefined),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/database/schema', () => ({
|
||||||
|
media: {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('drizzle-orm', () => ({
|
||||||
|
eq: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock('../../src/main/engine/MediaEngine', () => ({
|
||||||
|
getMediaEngine: vi.fn(() => ({
|
||||||
|
getThumbnailPaths: vi.fn().mockResolvedValue({ small: null }),
|
||||||
|
setProjectContext: vi.fn(),
|
||||||
|
setSearchLanguage: vi.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
await import('../../src/main/main');
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
|
||||||
|
expect(createPost).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
categories: ['aside'],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
process.argv = originalArgv;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user