Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/web-app-server-keepalive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inkeep/open-knowledge": patch
---

Keep an open browser tab's `ok start` server alive. The web editor now holds a single, app-lifetime `/collab/keepalive` WebSocket — the same presence-invisible keepalive the desktop app and MCP shim already use — so the server's 30-minute idle-shutdown can no longer fire out from under an open tab when no document is focused or during a brief reconnect. Previously, with no doc open the only liveness signal was the per-document collab connections, so an idle tab could lose its server and every editor/tool call would fail until reload. Closing the tab still lets the server idle-shut-down normally, and the keepalive reconnects across a server restart on a new port. Multiple tabs each hold their own keepalive. The keepalive adds no presence-bar entry.
1 change: 1 addition & 0 deletions docs/scripts/generate-og-wordmark.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';


const root = path.resolve(import.meta.dirname, '..');
const svg = readFileSync(path.join(root, 'public', 'ok-wordmark.svg'));
const dataUrl = `data:image/svg+xml;base64,${svg.toString('base64')}`;
Expand Down
1 change: 1 addition & 0 deletions docs/src/app/(home)/sections/eng-specs-graphic.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Code, Sparkles } from 'lucide-react';


const RINGS = [56, 82, 108] as const;
const RING_CENTER = { x: 80, y: 45 };

Expand Down
1 change: 1 addition & 0 deletions docs/src/app/(home)/sections/knowledge-base-graphic.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Sparkles } from 'lucide-react';
import type { CSSProperties } from 'react';


const RINGS = [56, 82, 108] as const;
const RING_CENTER = { x: 80, y: 45 };

Expand Down
3 changes: 2 additions & 1 deletion docs/src/components/copy-prompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export function CopyPrompt({ children }: CopyPromptProps) {
await navigator.clipboard.writeText(text);
setCopied(true);
window.setTimeout(() => setCopied(false), 2000);
} catch {}
} catch {
}
};

return (
Expand Down
3 changes: 2 additions & 1 deletion docs/src/components/ok-editor/bubble-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ export function OkBubbleMenu({ editor }: { editor: Editor }) {
popup.style.left = `${x}px`;
popup.style.top = `${y}px`;
})
.catch(() => {});
.catch(() => {
});
});
};

Expand Down
3 changes: 2 additions & 1 deletion docs/src/components/ok-editor/drag-handle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export const BlockDragHandle = Extension.create({
if (curPos < 0) return;
try {
editor.chain().focus().setNodeSelection(curPos).run();
} catch {}
} catch {
}
});

return [
Expand Down
1 change: 1 addition & 0 deletions docs/src/components/ok-editor/preview-code-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@tiptap/react';
import { Trash2 } from 'lucide-react';


const THEME_VARS = PREVIEW_THEME_TOKENS.map((t) => `${t.name}:${t.light}`).join(';');

const CHART_PALETTE = [
Expand Down
1 change: 1 addition & 0 deletions docs/src/components/overview-blocks.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ArrowRight, Bot, Database, type LucideIcon, NotebookPen } from 'lucide-react';
import type { ReactNode } from 'react';


interface Layer {
k: string;
Icon: LucideIcon;
Expand Down
3 changes: 2 additions & 1 deletion docs/src/components/page-markdown-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export function PageMarkdownActions({
await navigator.clipboard.writeText(await res.text());
setCopied(true);
window.setTimeout(() => setCopied(false), 2000);
} catch {}
} catch {
}
};

const prompt = `Read ${markdownUrl} so I can ask questions about it.`;
Expand Down
1 change: 1 addition & 0 deletions docs/src/components/tabs.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { describe, expect, test } from 'bun:test';
import { composeTabId, slugifyTabId } from './tabs.tsx';

Expand Down
1 change: 1 addition & 0 deletions docs/src/components/tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';


import {
Tab as FumadocsTab,
Tabs as FumadocsTabs,
Expand Down
1 change: 1 addition & 0 deletions docs/src/lib/deferred-share.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

export const PENDING_SHARE_COOKIE = 'ok_pending_share';

export const PENDING_SHARE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
Expand Down
1 change: 1 addition & 0 deletions docs/src/lib/share-splash.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { SITE_NAME } from './site';

const SHARE_URL_VERSION_V1 = 0x01;
Expand Down
1 change: 1 addition & 0 deletions docs/src/lib/track.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { after } from 'next/server';


const POSTHOG_CAPTURE_URL = 'https://us.i.posthog.com/capture/';
const CAPTURE_TIMEOUT_MS = 3_000;

Expand Down
1 change: 1 addition & 0 deletions packages/app/playwright.a11y.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineConfig } from '@playwright/test';


const isCI = !!process.env.CI;

export default defineConfig({
Expand Down
1 change: 1 addition & 0 deletions packages/app/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineConfig } from '@playwright/test';


const isCI = !!process.env.CI;

export default defineConfig({
Expand Down
1 change: 1 addition & 0 deletions packages/app/playwright.visual.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineConfig } from '@playwright/test';


const isCI = !!process.env.CI;

export default defineConfig({
Expand Down
4 changes: 4 additions & 0 deletions packages/app/src/App.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ mock.module('@/lib/api-config', () => ({
fetchApiConfig: (...args: Parameters<typeof fetchApiConfigMock>) => fetchApiConfigMock(...args),
}));

mock.module('@/lib/use-server-keepalive', () => ({
useServerKeepalive: () => {},
}));

mock.module('@/lib/single-file-mode', () => ({
SingleFileModeProvider: ({ children }: { children: ReactNode }) => (
<div data-testid="single-file-mode-provider">{children}</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
} from '@/lib/doc-hash';
import { mark, ProfilerBoundary } from '@/lib/perf';
import { SingleFileModeProvider, useSingleFileMode } from '@/lib/single-file-mode';
import { useServerKeepalive } from '@/lib/use-server-keepalive';
import { isSettingsShortcut, SETTINGS_OPEN_HASH } from '@/lib/use-settings-route';

const INSTALL_DIALOG_HASH = '#install-claude-desktop';
Expand Down Expand Up @@ -319,6 +320,7 @@ function NewItemShortcutHandler() {

function ConfigProviderHost({ children }: { children: ReactNode }) {
const { collabUrl } = useDocumentContext();
useServerKeepalive(collabUrl);
return <ConfigProvider collabUrl={collabUrl}>{children}</ConfigProvider>;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/build/app-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export function resolveAppVersion(): string {
if (typeof pkg.version === 'string' && pkg.version.length > 0) {
return pkg.version;
}
} catch {}
} catch {
}
return '0.0.0-unknown';
}

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/build/electron-mode-class.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
Expand Down
5 changes: 5 additions & 0 deletions packages/app/src/components/ActivityModeContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AgentIcon } from './icons/AgentIcon';
import { Button } from './ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';


async function postAgentUndo(body: {
connectionId: string;
docName: string;
Expand All @@ -31,6 +32,7 @@ async function postAgentUndo(body: {
}
}


function hashFromDocName(docName: string): string {
return `#/${docName
.split('/')
Expand All @@ -43,6 +45,7 @@ function navigateToDoc(docName: string): void {
window.location.hash = hashFromDocName(docName);
}


function LoadingState(): React.JSX.Element {
return (
<div
Expand Down Expand Up @@ -196,6 +199,7 @@ function AgentAvatar({
);
}


interface ActivityModeBodyProps {
data: ReturnType<typeof useActivityPanel>['data'];
status: ReturnType<typeof useActivityPanel>['status'];
Expand Down Expand Up @@ -304,6 +308,7 @@ function ActivityModeBody({
);
}


export function ActivityModeContent({
showBackButton = true,
}: {
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/ArrayOfObjectsWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import {
closestCenter,
DndContext,
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/AuthModal.dom.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { afterEach, describe, expect, jest, test } from 'bun:test';
import { act, cleanup, render, screen } from '@testing-library/react';
import type { OkLocalOpAuthEvent, OkLocalOpAuthStatusResponse } from '@/lib/desktop-bridge-types';
Expand Down
10 changes: 8 additions & 2 deletions packages/app/src/components/AuthModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import {
} from './ui/dialog';
import { Input } from './ui/input';


async function copyToClipboard(text: string): Promise<void> {
try {
await navigator.clipboard.writeText(text);
} catch {}
} catch {
}
}

interface AuthSuccessResult {
Expand All @@ -42,6 +44,7 @@ interface AuthModalProps {
queryTransport?: AuthQueryTransport;
}


interface DeviceFlowPanelProps {
onSuccess: (result: AuthSuccessResult) => void;
transport: AuthTransport;
Expand Down Expand Up @@ -202,6 +205,7 @@ function DeviceFlowPanel({ onSuccess, transport }: DeviceFlowPanelProps) {
);
}


interface IdentityBodyProps {
login: string;
name: string;
Expand Down Expand Up @@ -237,6 +241,7 @@ function IdentityBody({ login, name, onNameChange, email, onEmailChange }: Ident
);
}


type AuthStep = 'checking' | 'auth' | 'identity' | 'done';

const IDENTITY_PROBE_TIMEOUT_MS = 10_000;
Expand Down Expand Up @@ -323,7 +328,8 @@ export function AuthModal({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email }),
}).catch(() => {});
}).catch(() => {
});

const result = { ...(authResult ?? { login: '' }), name, email };
setStep('done');
Expand Down
5 changes: 4 additions & 1 deletion packages/app/src/components/AutoSyncOnboardingDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export function AutoSyncOnboardingDialog({ open, onResolved }: AutoSyncOnboardin
}

return (
<DialogRoot open={open} onOpenChange={() => {}}>
<DialogRoot
open={open}
onOpenChange={() => {}}
>
<DialogContent className="sm:max-w-lg" showCloseButton={false}>
<DialogHeader>
<AutoSyncEnableDialogIntro />
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/BetaBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { Trans, useLingui } from '@lingui/react/macro';
import { useUpdateChannel } from '@/hooks/use-update-channel';
import { Badge } from './ui/badge';
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/components/BottomComposer.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@ beforeEach(() => {
toastErrors.length = 0;
try {
window.localStorage.clear();
} catch {}
} catch {
}
});

afterEach(() => {
Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/components/BottomComposer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import {
type TargetData,
TERMINAL_CLI_IDS,
Expand Down Expand Up @@ -185,7 +186,8 @@ export function BottomComposer({
if (overlap <= 0) return;
const scroller = view.dom.closest('.editor-doc-scroll');
if (scroller instanceof HTMLElement) scroller.scrollTop += overlap;
} catch {}
} catch {
}
});
};
const card = cardRef.current;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import type { ClaudeReadiness, OkDesktopBridge } from '@/lib/desktop-bridge-types';
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/CloneDialog.dom.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { cleanup, render, screen } from '@testing-library/react';
import { getLastKnownSignedIn, setLastKnownSignedIn } from '@/lib/auth-state-cache';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { afterEach, describe, expect, mock, test } from 'bun:test';
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import type { ReactNode } from 'react';
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/ComposerContextChips.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { useLingui } from '@lingui/react/macro';
import { X } from 'lucide-react';
import type { ReactNode } from 'react';
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/ConsentDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { lazy, Suspense, useSyncExternalStore } from 'react';
import { consentStore } from '@/lib/consent-store';

Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { useLingui } from '@lingui/react/macro';
import { Check, Copy } from 'lucide-react';
import { useEffect, useState } from 'react';
Expand Down Expand Up @@ -39,7 +40,8 @@ export function CopyButton({
.then(() => clipboardWrite(copyContent))
.then(
() => setCopyTick((n) => n + 1),
() => {},
() => {
},
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ describe('CreateProjectDialog cascade staleness (Tier-3 mount)', () => {
});

test('PRD-6649: 5 s polling skips probeNonce bump while a probe is in-flight (race-prevention gate)', async () => {

const stub = makeStubBridge(FIRST_GIT_RESULT, PARENT);

const setIntervalSpy = spyOn(globalThis, 'setInterval');
Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/components/CreateProjectDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import {
ALL_EDITOR_IDS,
CREATE_NEW_PROJECT_FAILURE_REASONS,
Expand Down Expand Up @@ -308,7 +309,8 @@ export function CreateProjectDialog({ open, onOpenChange, bridge }: CreateProjec
if (banner === null) return;
if (firedBanners.current.has(banner)) return;
firedBanners.current.add(banner);
bridge.project.recordCreateNewBannerShown(banner).catch(() => {});
bridge.project.recordCreateNewBannerShown(banner).catch(() => {
});
}, [open, cascade, bridge]);

const rawName = name;
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/CreateProjectMenuTrigger.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { useEffect, useState } from 'react';
import { CreateProjectDialog } from '@/components/CreateProjectDialog';
import type { OkDesktopBridge } from '@/lib/desktop-bridge-types';
Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/components/DeleteConfirmationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export function DeleteConfirmationDialog({
<DialogContent>
<DialogHeader>
<DialogTitle>{customTitle ?? t`Delete ${itemName}`}</DialogTitle>
<DialogDescription className="whitespace-pre-wrap">
<DialogDescription
className="whitespace-pre-wrap"
>
{customDescription ??
t`Are you sure you want to delete ${itemName}? This action cannot be undone.`}
</DialogDescription>
Expand Down
Loading