@kortix/ui 0.14.10 → 0.14.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_vendor/shared/runtime-versions.d.ts +9 -0
- package/dist/browser/browser-panel.d.ts +2 -1
- package/dist/browser/browser-panel.js +12 -2
- package/dist/customize/settings/tabs/sandbox-template-form.js +8 -1
- package/dist/files/renderers/show-content-renderer.d.ts +2 -2
- package/dist/files/renderers/show-content-renderer.js +2 -1
- package/dist/i18n/en.d.ts +18 -0
- package/dist/i18n/en.js +30 -12
- package/dist/i18n/ui-catalog/remaining-ui-translation-keys.generated.js +3 -3
- package/dist/internal/branding/branding-provider.js +3 -3
- package/dist/internal/hooks/use-notification-center.d.ts +10 -6
- package/dist/internal/hooks/use-notification-center.js +32 -10
- package/dist/internal/lib/browser-noise/rules/extensions.js +3 -0
- package/dist/internal/lib/site-metadata.js +4 -4
- package/dist/internal/notifications/notification-bell.js +4 -3
- package/dist/internal/notifications/notification-rows.d.ts +1 -1
- package/dist/internal/notifications/notification-rows.js +2 -0
- package/dist/internal/stores/kortix-computer-store.d.ts +3 -2
- package/dist/internal/stores/kortix-computer-store.js +5 -3
- package/dist/session/composer/index.d.ts +1 -0
- package/dist/session/composer/index.js +1 -0
- package/dist/session/composer/send-stop-control.js +8 -3
- package/dist/session/header/session-site-header.js +3 -1
- package/dist/session/panel/easy/detail-view.js +11 -0
- package/dist/session/panel/mobile-tool-drawer.js +10 -4
- package/dist/session/panel/open-session-quick-view.js +3 -3
- package/dist/session/panel/panel-fallback.js +10 -7
- package/dist/session/panel/session-desktop.d.ts +6 -0
- package/dist/session/panel/session-desktop.js +74 -0
- package/dist/session/panel/session-panel-provider.d.ts +1 -0
- package/dist/session/panel/session-panel-provider.js +17 -1
- package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
- package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
- package/dist/session/panel/use-session-panel-layout.js +8 -0
- package/dist/session/status/session-tab-title.d.ts +1 -1
- package/dist/session/status/session-tab-title.js +1 -1
- package/dist/session/thread/session-chat.js +12 -0
- package/dist/session/thread/session-context-sub-sessions.js +1 -1
- package/dist/session/tool/tools/image-gen-tool.d.ts +1 -1
- package/dist/session/tool/tools/image-gen-tool.js +17 -8
- package/dist/session/turn/user-message.d.ts +5 -4
- package/dist/session/turn/user-message.js +7 -5
- package/dist/styles.css +1 -1
- package/dist/workspace/session-workspace.js +24 -5
- package/package.json +3 -3
- package/skills/kortix-ui/references/modules.md +1 -1
|
@@ -19,6 +19,10 @@ export type RuntimeVersions = {
|
|
|
19
19
|
agentBrowser: string;
|
|
20
20
|
playwright: string;
|
|
21
21
|
anydoc: string;
|
|
22
|
+
/** tembo/voxl commit the sandbox desktop streamer is built from (apps/sandbox/voxl/release.sh). */
|
|
23
|
+
voxl: string;
|
|
24
|
+
voxlSha256Amd64: string;
|
|
25
|
+
voxlSha256Arm64: string;
|
|
22
26
|
/** pi packages every pi session loads, as pi sources: `npm:<name>@<exact version>`. */
|
|
23
27
|
piSystemPackages: string[];
|
|
24
28
|
};
|
|
@@ -45,6 +49,11 @@ export declare const OPENCODE_USER_AGENT: string;
|
|
|
45
49
|
export declare const AGENT_BROWSER_VERSION: string;
|
|
46
50
|
export declare const PLAYWRIGHT_VERSION: string;
|
|
47
51
|
export declare const ANYDOC_VERSION: string;
|
|
52
|
+
export declare const VOXL_VERSION: string;
|
|
53
|
+
export declare const VOXL_SHA256_AMD64: string;
|
|
54
|
+
export declare const VOXL_SHA256_ARM64: string;
|
|
55
|
+
/** The GitHub release apps/sandbox/voxl/release.sh publishes for VOXL_VERSION. */
|
|
56
|
+
export declare const VOXL_RELEASE_URL: string;
|
|
48
57
|
/** Throws unless `source` is an exact `npm:<name>@<x.y.z>` pin. */
|
|
49
58
|
export declare function assertPiSystemPackage(source: string): void;
|
|
50
59
|
/**
|
|
@@ -2,6 +2,7 @@ interface PreviewTabContentProps {
|
|
|
2
2
|
tabId: string;
|
|
3
3
|
projectId?: string;
|
|
4
4
|
projectSessionId?: string;
|
|
5
|
+
targetUrl?: string;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
8
|
* Preview tab content — renders a proxied sandbox URL in an iframe
|
|
@@ -11,5 +12,5 @@ interface PreviewTabContentProps {
|
|
|
11
12
|
* any localhost:PORT address to navigate within the sandbox. Visited URLs are
|
|
12
13
|
* recorded and offered back as "Recents" on the empty landing state.
|
|
13
14
|
*/
|
|
14
|
-
export declare function BrowserPanel({ tabId, projectId, projectSessionId }: PreviewTabContentProps): import("react").JSX.Element;
|
|
15
|
+
export declare function BrowserPanel({ tabId, projectId, projectSessionId, targetUrl }: PreviewTabContentProps): import("react").JSX.Element;
|
|
15
16
|
export {};
|
|
@@ -43,7 +43,7 @@ const subscribeNoop = () => () => { };
|
|
|
43
43
|
* any localhost:PORT address to navigate within the sandbox. Visited URLs are
|
|
44
44
|
* recorded and offered back as "Recents" on the empty landing state.
|
|
45
45
|
*/
|
|
46
|
-
export function BrowserPanel({ tabId, projectId, projectSessionId }) {
|
|
46
|
+
export function BrowserPanel({ tabId, projectId, projectSessionId, targetUrl }) {
|
|
47
47
|
const tHardcodedUi = useTranslations('hardcodedUi');
|
|
48
48
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
49
49
|
const appPreviewTitle = tI18nComplete.raw('text6026c1d8c92e');
|
|
@@ -69,7 +69,7 @@ export function BrowserPanel({ tabId, projectId, projectSessionId }) {
|
|
|
69
69
|
// reveal scroll shoves a permanent sideways offset onto the panel's
|
|
70
70
|
// overflow-hidden ancestors. Mount-only on purpose (`autoFocus` semantics —
|
|
71
71
|
// a later navigation clearing the URL must not steal the cursor).
|
|
72
|
-
const autoFocusAddressRef = useRef(!rawPreviewUrl);
|
|
72
|
+
const autoFocusAddressRef = useRef(!rawPreviewUrl && !targetUrl);
|
|
73
73
|
useEffect(() => {
|
|
74
74
|
if (autoFocusAddressRef.current)
|
|
75
75
|
focusWithoutScroll(addressInputRef.current);
|
|
@@ -200,6 +200,16 @@ export function BrowserPanel({ tabId, projectId, projectSessionId }) {
|
|
|
200
200
|
pushHistory(newProxyUrl);
|
|
201
201
|
handleRefresh();
|
|
202
202
|
}, [subdomainOpts, rewritePortPath, tabId, appPreviewTitle, updateTabMetadata, pushHistory, handleRefresh]);
|
|
203
|
+
const appliedTarget = useRef(null);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!targetUrl || (appliedTarget.current?.tabId === tabId && appliedTarget.current.url === targetUrl))
|
|
206
|
+
return;
|
|
207
|
+
appliedTarget.current = { tabId, url: targetUrl };
|
|
208
|
+
// An external quick-view intent synchronizes the Browser tab and its navigation history.
|
|
209
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
210
|
+
if (targetUrl !== rawPreviewUrl && targetUrl !== originalUrl)
|
|
211
|
+
navigateTo(targetUrl);
|
|
212
|
+
}, [tabId, targetUrl, rawPreviewUrl, originalUrl, navigateTo]);
|
|
203
213
|
const canGoBack = historyIndex > 0;
|
|
204
214
|
const canGoForward = historyIndex < history.length - 1;
|
|
205
215
|
/** Walk the history stack in either direction; the two directions were two
|
|
@@ -13,6 +13,7 @@ import { ShippingContainerIcon as Container, FileCodeIcon as FileCode, PackageIc
|
|
|
13
13
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
14
14
|
import { useMemo, useState } from 'react';
|
|
15
15
|
import { Button } from '../../../primitives/button.js';
|
|
16
|
+
import { Checkbox } from '../../../primitives/checkbox.js';
|
|
16
17
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '../../../primitives/dialog.js';
|
|
17
18
|
import { Input } from '../../../primitives/input.js';
|
|
18
19
|
import { Label } from '../../../primitives/label.js';
|
|
@@ -48,6 +49,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
|
|
|
48
49
|
const [cpu, setCpu] = useState('2');
|
|
49
50
|
const [memoryGb, setMemoryGb] = useState('4');
|
|
50
51
|
const [diskGb, setDiskGb] = useState('20');
|
|
52
|
+
const [residentialProxy, setResidentialProxy] = useState(false);
|
|
51
53
|
// Reset / hydrate when opening
|
|
52
54
|
const [hydratedFor, setHydratedFor] = useState(null);
|
|
53
55
|
if (!hydratedFor || hydratedFor[0] !== open || hydratedFor[1] !== template) {
|
|
@@ -70,6 +72,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
|
|
|
70
72
|
setCpu(String(template.cpu));
|
|
71
73
|
setMemoryGb(String(template.memory_gb));
|
|
72
74
|
setDiskGb(String(template.disk_gb));
|
|
75
|
+
setResidentialProxy(!!template.residential_proxy);
|
|
73
76
|
}
|
|
74
77
|
else if (open) {
|
|
75
78
|
setName('');
|
|
@@ -82,6 +85,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
|
|
|
82
85
|
setCpu('2');
|
|
83
86
|
setMemoryGb('6');
|
|
84
87
|
setDiskGb('20');
|
|
88
|
+
setResidentialProxy(false);
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
91
|
// Auto-slug from name when the user hasn't typed a slug manually yet.
|
|
@@ -117,6 +121,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
|
|
|
117
121
|
cpu: parsePosInt(cpu),
|
|
118
122
|
memory_gb: parsePosInt(memoryGb),
|
|
119
123
|
disk_gb: parsePosInt(diskGb),
|
|
124
|
+
...(residentialProxy ? { residential_proxy: true } : {}),
|
|
120
125
|
}),
|
|
121
126
|
onSuccess: () => {
|
|
122
127
|
successToast(t('toasts.created'));
|
|
@@ -135,6 +140,8 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
|
|
|
135
140
|
cpu: parsePosInt(cpu) ?? null,
|
|
136
141
|
memory_gb: parsePosInt(memoryGb) ?? null,
|
|
137
142
|
disk_gb: parsePosInt(diskGb) ?? null,
|
|
143
|
+
// Keep a country set through kortix.yaml or the API while it stays on.
|
|
144
|
+
residential_proxy: residentialProxy ? (template?.residential_proxy ?? true) : null,
|
|
138
145
|
}),
|
|
139
146
|
onSuccess: () => {
|
|
140
147
|
successToast(t('toasts.updated'));
|
|
@@ -148,7 +155,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
|
|
|
148
155
|
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-xl", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(Container, { className: "size-4" }), isEdit ? t('editTitle', { name: template?.name ?? '' }) : t('newTitle')] }), _jsx(DialogDescription, { children: t('description') })] }), _jsxs("div", { className: "space-y-4 py-2", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "tpl-name", children: t('name') }), _jsx(Input, { id: "tpl-name", placeholder: t('namePlaceholder'), value: name, onChange: (e) => setName(e.target.value) })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "tpl-slug", children: t('slug') }), _jsx(Input, { id: "tpl-slug", placeholder: tI18nComplete.raw('text5d58d41913d9'), value: slug, onChange: (e) => {
|
|
149
156
|
setSlugManuallyEdited(true);
|
|
150
157
|
setSlug(e.target.value.toLowerCase());
|
|
151
|
-
}, disabled: isEdit, "aria-invalid": !!slugError }), slugError && _jsx("p", { className: "text-destructive mt-1 text-xs", children: slugError })] })] }), _jsxs("div", { children: [_jsx(Label, { children: t('imageSource') }), _jsxs("div", { className: "mt-2 grid grid-cols-2 gap-2", children: [_jsxs("button", { type: "button", onClick: () => setMode('image'), className: cn('border-border/60 flex flex-col items-start gap-1 rounded-md border p-3 text-left text-sm transition-colors', mode === 'image' && 'border-foreground/30 bg-muted/40'), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Package, { className: "size-4" }), _jsx("span", { className: "font-medium", children: t('publicImage') })] }), _jsxs("span", { className: "text-muted-foreground text-xs", children: [t('example'), " ", _jsx("code", { className: "font-mono", children: "python:3.12-slim" })] })] }), _jsxs("button", { type: "button", onClick: () => setMode('dockerfile'), className: cn('border-border/60 flex flex-col items-start gap-1 rounded-md border p-3 text-left text-sm transition-colors', mode === 'dockerfile' && 'border-foreground/30 bg-muted/40'), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileCode, { className: "size-4" }), _jsx("span", { className: "font-medium", children: tI18nComplete.raw('textdd2c0eb6ea5c') })] }), _jsx("span", { className: "text-muted-foreground text-xs", children: t('pathInsideRepo') })] })] }), _jsxs("div", { className: "mt-3", children: [mode === 'image' ? (_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "tpl-image", children: t('image') }), _jsx(Input, { id: "tpl-image", placeholder: tI18nComplete.raw('text1d0447543fac'), value: image, onChange: (e) => setImage(e.target.value) }), _jsxs("p", { className: "text-muted-foreground mt-1 text-xs", children: [t('specificTag'), " (", _jsx("code", { className: "font-mono", children: "latest" }), ")."] })] })) : (_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "tpl-df", children: t('dockerfilePath') }), _jsx(Input, { id: "tpl-df", placeholder: tI18nComplete.raw('text1bc7a47dada1'), value: dockerfilePath, onChange: (e) => setDockerfilePath(e.target.value) }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: t('relativeToRoot') })] })), sourceError && _jsx("p", { className: "text-destructive mt-1 text-xs", children: sourceError })] })] }), _jsxs("div", { children: [_jsx(Label, { children: t('resources') }), _jsxs("div", { className: "mt-2 grid grid-cols-3 gap-3", children: [_jsx(NumericField, { id: "cpu", label: tI18nComplete.raw('text082591a16602'), value: cpu, onChange: setCpu, min: 1, max: 32 }), _jsx(NumericField, { id: "mem", label: t('memory'), value: memoryGb, onChange: setMemoryGb, min: 1, max: 128 }), _jsx(NumericField, { id: "disk", label: t('disk'), value: diskGb, onChange: setDiskGb, min: 1, max: 500 })] })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: "tpl-entry", children: [t('entrypoint'), " ", _jsxs("span", { className: "text-muted-foreground", children: ["(", t('optional'), ")"] })] }), _jsx(Textarea, { id: "tpl-entry", placeholder: t('entrypointPlaceholder'), value: entrypoint, onChange: (e) => setEntrypoint(e.target.value), className: "font-mono text-xs", rows: 2 })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: submitting, children: t('cancel') }), _jsxs(Button, { onClick: () => (isEdit ? editMut.mutate() : createMut.mutate()), disabled: !canSubmit || submitting, children: [submitting && _jsx(Loading, { className: "mr-2 size-4" }), isEdit ? t('save') : t('create')] })] })] }) }));
|
|
158
|
+
}, disabled: isEdit, "aria-invalid": !!slugError }), slugError && _jsx("p", { className: "text-destructive mt-1 text-xs", children: slugError })] })] }), _jsxs("div", { children: [_jsx(Label, { children: t('imageSource') }), _jsxs("div", { className: "mt-2 grid grid-cols-2 gap-2", children: [_jsxs("button", { type: "button", onClick: () => setMode('image'), className: cn('border-border/60 flex flex-col items-start gap-1 rounded-md border p-3 text-left text-sm transition-colors', mode === 'image' && 'border-foreground/30 bg-muted/40'), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Package, { className: "size-4" }), _jsx("span", { className: "font-medium", children: t('publicImage') })] }), _jsxs("span", { className: "text-muted-foreground text-xs", children: [t('example'), " ", _jsx("code", { className: "font-mono", children: "python:3.12-slim" })] })] }), _jsxs("button", { type: "button", onClick: () => setMode('dockerfile'), className: cn('border-border/60 flex flex-col items-start gap-1 rounded-md border p-3 text-left text-sm transition-colors', mode === 'dockerfile' && 'border-foreground/30 bg-muted/40'), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileCode, { className: "size-4" }), _jsx("span", { className: "font-medium", children: tI18nComplete.raw('textdd2c0eb6ea5c') })] }), _jsx("span", { className: "text-muted-foreground text-xs", children: t('pathInsideRepo') })] })] }), _jsxs("div", { className: "mt-3", children: [mode === 'image' ? (_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "tpl-image", children: t('image') }), _jsx(Input, { id: "tpl-image", placeholder: tI18nComplete.raw('text1d0447543fac'), value: image, onChange: (e) => setImage(e.target.value) }), _jsxs("p", { className: "text-muted-foreground mt-1 text-xs", children: [t('specificTag'), " (", _jsx("code", { className: "font-mono", children: "latest" }), ")."] })] })) : (_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "tpl-df", children: t('dockerfilePath') }), _jsx(Input, { id: "tpl-df", placeholder: tI18nComplete.raw('text1bc7a47dada1'), value: dockerfilePath, onChange: (e) => setDockerfilePath(e.target.value) }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: t('relativeToRoot') })] })), sourceError && _jsx("p", { className: "text-destructive mt-1 text-xs", children: sourceError })] })] }), _jsxs("div", { children: [_jsx(Label, { children: t('resources') }), _jsxs("div", { className: "mt-2 grid grid-cols-3 gap-3", children: [_jsx(NumericField, { id: "cpu", label: tI18nComplete.raw('text082591a16602'), value: cpu, onChange: setCpu, min: 1, max: 32 }), _jsx(NumericField, { id: "mem", label: t('memory'), value: memoryGb, onChange: setMemoryGb, min: 1, max: 128 }), _jsx(NumericField, { id: "disk", label: t('disk'), value: diskGb, onChange: setDiskGb, min: 1, max: 500 })] })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: "tpl-entry", children: [t('entrypoint'), " ", _jsxs("span", { className: "text-muted-foreground", children: ["(", t('optional'), ")"] })] }), _jsx(Textarea, { id: "tpl-entry", placeholder: t('entrypointPlaceholder'), value: entrypoint, onChange: (e) => setEntrypoint(e.target.value), className: "font-mono text-xs", rows: 2 })] }), _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Checkbox, { id: "tpl-residential-ip", className: "mt-0.5", checked: residentialProxy, onCheckedChange: (checked) => setResidentialProxy(checked === true) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "tpl-residential-ip", children: t('residentialIp') }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: t('residentialIpHint') })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: submitting, children: t('cancel') }), _jsxs(Button, { onClick: () => (isEdit ? editMut.mutate() : createMut.mutate()), disabled: !canSubmit || submitting, children: [submitting && _jsx(Loading, { className: "mr-2 size-4" }), isEdit ? t('save') : t('create')] })] })] }) }));
|
|
152
159
|
}
|
|
153
160
|
function NumericField({ id, label, value, onChange, min, max, }) {
|
|
154
161
|
return (_jsxs("div", { children: [_jsx(Label, { htmlFor: id, className: "text-xs", children: label }), _jsx(Input, { id: id, type: "number", min: min, max: max, value: value, onChange: (e) => onChange(e.target.value) })] }));
|
|
@@ -30,7 +30,7 @@ export interface ShowContentProps {
|
|
|
30
30
|
type: string;
|
|
31
31
|
title?: string;
|
|
32
32
|
description?: string;
|
|
33
|
-
path?: string;
|
|
33
|
+
path?: string | null;
|
|
34
34
|
url?: string;
|
|
35
35
|
content?: string;
|
|
36
36
|
language?: string;
|
|
@@ -80,7 +80,7 @@ export interface ShowContentProps {
|
|
|
80
80
|
*/
|
|
81
81
|
toolbarActions?: React.ReactNode;
|
|
82
82
|
}
|
|
83
|
-
export declare function ShowContentRenderer({ type, title, description, path, url, content, language, aspectRatio, attachment, sha256, LocalhostPreview, fill, onStatusChange, toolbarActions, }: ShowContentProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
|
|
83
|
+
export declare function ShowContentRenderer({ type, title, description, path: inputPath, url, content, language, aspectRatio, attachment, sha256, LocalhostPreview, fill, onStatusChange, toolbarActions, }: ShowContentProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
|
|
84
84
|
export interface ShowCarouselItem {
|
|
85
85
|
type: string;
|
|
86
86
|
title?: string;
|
|
@@ -172,7 +172,8 @@ function FileCard({ title, fileName, path }) {
|
|
|
172
172
|
return (_jsxs("div", { className: "flex items-center gap-4 px-5 py-5", children: [_jsx("div", { className: "bg-muted/20 flex size-12 items-center justify-center rounded-xl", children: _jsx(FileText, { className: "text-muted-foreground/40 size-6" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-foreground truncate text-sm font-medium", children: title || fileName }), _jsx("div", { className: "text-muted-foreground/50 mt-0.5 truncate font-mono text-xs", children: path })] })] }));
|
|
173
173
|
}
|
|
174
174
|
// ── Component ──────────────────────────────────────────────────────────────
|
|
175
|
-
export function ShowContentRenderer({ type, title = '', description = '', path
|
|
175
|
+
export function ShowContentRenderer({ type, title = '', description = '', path: inputPath, url = '', content = '', language = '', aspectRatio = '', attachment, sha256, LocalhostPreview, fill = false, onStatusChange, toolbarActions, }) {
|
|
176
|
+
const path = inputPath ?? '';
|
|
176
177
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
177
178
|
const tImageGen = useTranslations('imageGen');
|
|
178
179
|
const arCSS = showAspectRatioToCSS(aspectRatio);
|
package/dist/i18n/en.d.ts
CHANGED
|
@@ -577,6 +577,8 @@ declare const catalog: {
|
|
|
577
577
|
entrypoint: string;
|
|
578
578
|
optional: string;
|
|
579
579
|
entrypointPlaceholder: string;
|
|
580
|
+
residentialIp: string;
|
|
581
|
+
residentialIpHint: string;
|
|
580
582
|
cancel: string;
|
|
581
583
|
save: string;
|
|
582
584
|
create: string;
|
|
@@ -1930,6 +1932,7 @@ declare const catalog: {
|
|
|
1930
1932
|
capture_device_down: string;
|
|
1931
1933
|
capture_device_recovered: string;
|
|
1932
1934
|
message: string;
|
|
1935
|
+
artifact_comment: string;
|
|
1933
1936
|
};
|
|
1934
1937
|
kindDescription: {
|
|
1935
1938
|
turn_done: string;
|
|
@@ -1942,6 +1945,7 @@ declare const catalog: {
|
|
|
1942
1945
|
capture_device_down: string;
|
|
1943
1946
|
capture_device_recovered: string;
|
|
1944
1947
|
message: string;
|
|
1948
|
+
artifact_comment: string;
|
|
1945
1949
|
};
|
|
1946
1950
|
bell: {
|
|
1947
1951
|
label: string;
|
|
@@ -1966,6 +1970,7 @@ declare const catalog: {
|
|
|
1966
1970
|
openReminders: string;
|
|
1967
1971
|
openTriggers: string;
|
|
1968
1972
|
openConversation: string;
|
|
1973
|
+
openArtifact: string;
|
|
1969
1974
|
};
|
|
1970
1975
|
session: {
|
|
1971
1976
|
mute: string;
|
|
@@ -4873,6 +4878,7 @@ declare const catalog: {
|
|
|
4873
4878
|
textce1e609b7bf5: string;
|
|
4874
4879
|
textce34af36d804: string;
|
|
4875
4880
|
textce4683e7013a: string;
|
|
4881
|
+
text68971283841a: string;
|
|
4876
4882
|
textce49e021df99: string;
|
|
4877
4883
|
textce4c96225f63: string;
|
|
4878
4884
|
textce5e9df4a78f: string;
|
|
@@ -8276,5 +8282,17 @@ declare const catalog: {
|
|
|
8276
8282
|
copyFailed: string;
|
|
8277
8283
|
};
|
|
8278
8284
|
};
|
|
8285
|
+
sessionDesktop: {
|
|
8286
|
+
title: string;
|
|
8287
|
+
openTab: string;
|
|
8288
|
+
offline: string;
|
|
8289
|
+
start: string;
|
|
8290
|
+
retry: string;
|
|
8291
|
+
takeOver: string;
|
|
8292
|
+
userControl: string;
|
|
8293
|
+
agentControl: string;
|
|
8294
|
+
returnControl: string;
|
|
8295
|
+
escape: string;
|
|
8296
|
+
};
|
|
8279
8297
|
};
|
|
8280
8298
|
export default catalog;
|
package/dist/i18n/en.js
CHANGED
|
@@ -578,6 +578,8 @@ const catalog = {
|
|
|
578
578
|
"entrypoint": "Entrypoint",
|
|
579
579
|
"optional": "optional",
|
|
580
580
|
"entrypointPlaceholder": "Leave blank to use the Kortix default (recommended).",
|
|
581
|
+
"residentialIp": "Residential IP",
|
|
582
|
+
"residentialIpHint": "Send this sandbox's web traffic through a residential IP address, billed per GB from your credits. Sessions start only where the deployment offers it.",
|
|
581
583
|
"cancel": "Cancel",
|
|
582
584
|
"save": "Save changes",
|
|
583
585
|
"create": "Create template",
|
|
@@ -1676,8 +1678,8 @@ const catalog = {
|
|
|
1676
1678
|
"sending": "Sending..."
|
|
1677
1679
|
},
|
|
1678
1680
|
"unified": {
|
|
1679
|
-
"welcome": "Welcome to Kortix",
|
|
1680
|
-
"tagline": "The open-source
|
|
1681
|
+
"welcome": "Welcome to Kortix CompanyOS",
|
|
1682
|
+
"tagline": "The open-source CompanyOS",
|
|
1681
1683
|
"email": "Email",
|
|
1682
1684
|
"emailPlaceholder": "Your email address",
|
|
1683
1685
|
"password": "Password",
|
|
@@ -1930,7 +1932,8 @@ const catalog = {
|
|
|
1930
1932
|
"automation_recovered": "Recovery alert",
|
|
1931
1933
|
"capture_device_down": "Capture computer stopped",
|
|
1932
1934
|
"capture_device_recovered": "Capture computer recording again",
|
|
1933
|
-
"message": "Message"
|
|
1935
|
+
"message": "Message",
|
|
1936
|
+
"artifact_comment": "Artifact comments"
|
|
1934
1937
|
},
|
|
1935
1938
|
"kindDescription": {
|
|
1936
1939
|
"turn_done": "A turn ends.",
|
|
@@ -1942,7 +1945,8 @@ const catalog = {
|
|
|
1942
1945
|
"automation_recovered": "A failing trigger or reminder works again.",
|
|
1943
1946
|
"capture_device_down": "A Kortix Capture computer is not recording. Sent to people who manage Capture.",
|
|
1944
1947
|
"capture_device_recovered": "A Kortix Capture computer that stopped records again.",
|
|
1945
|
-
"message": "Someone @-mentions you, or replies in a thread you posted in, in a room."
|
|
1948
|
+
"message": "Someone @-mentions you, or replies in a thread you posted in, in a room.",
|
|
1949
|
+
"artifact_comment": "Feedback, mentions, and replies on Artifacts you can view."
|
|
1946
1950
|
},
|
|
1947
1951
|
"bell": {
|
|
1948
1952
|
"label": "Notifications",
|
|
@@ -1966,7 +1970,8 @@ const catalog = {
|
|
|
1966
1970
|
"openSession": "Open session",
|
|
1967
1971
|
"openReminders": "Open reminders",
|
|
1968
1972
|
"openTriggers": "Open triggers",
|
|
1969
|
-
"openConversation": "Open conversation"
|
|
1973
|
+
"openConversation": "Open conversation",
|
|
1974
|
+
"openArtifact": "Open Artifact"
|
|
1970
1975
|
},
|
|
1971
1976
|
"session": {
|
|
1972
1977
|
"mute": "Mute notifications",
|
|
@@ -2720,7 +2725,7 @@ const catalog = {
|
|
|
2720
2725
|
"text18885f27b5af": "Python",
|
|
2721
2726
|
"text1898b198a7e6": "Nobody present",
|
|
2722
2727
|
"text18a4e711991b": "Enriches leads from the CRM, researches each account and drafts tailored outreach.",
|
|
2723
|
-
"text18f31a325716": "About Kortix –
|
|
2728
|
+
"text18f31a325716": "About Kortix CompanyOS – the open-source operating system for your company",
|
|
2724
2729
|
"text18fb4a5618b9": "The same parameter view used for live approvals.",
|
|
2725
2730
|
"text18fdd549b2ed": "New",
|
|
2726
2731
|
"text192173c3beda": "billed to your own provider keys",
|
|
@@ -2760,7 +2765,7 @@ const catalog = {
|
|
|
2760
2765
|
"text1d55ff7c387c": "View options",
|
|
2761
2766
|
"text1d59a5e09714": "Approval rules",
|
|
2762
2767
|
"text1d6d3a460fb1": "DOCX document",
|
|
2763
|
-
"text1d7a4d1f09bb": "We are building the open-source
|
|
2768
|
+
"text1d7a4d1f09bb": "We are building the open-source CompanyOS. Every company should own all of it.",
|
|
2764
2769
|
"text1d826553e961": "Review changes proposed by your agents",
|
|
2765
2770
|
"text1d96669797aa": "sentBody",
|
|
2766
2771
|
"text1de222b90941": "Add another project",
|
|
@@ -2926,7 +2931,7 @@ const catalog = {
|
|
|
2926
2931
|
"text2bcb43cbc8f6": "view",
|
|
2927
2932
|
"text2bd873d6c734": "Breadcrumb",
|
|
2928
2933
|
"text2bdb956493fd": "Read the webhook handler",
|
|
2929
|
-
"text2bf70270bfde": "Open-source
|
|
2934
|
+
"text2bf70270bfde": "Open-source CompanyOS: your agents, skills, memory, and connectors in one repo you own. Any model. Self-host or cloud.",
|
|
2930
2935
|
"text2bfc3471571e": "Approvals",
|
|
2931
2936
|
"text2c014f8f8986": "Cursor",
|
|
2932
2937
|
"text2c063e7ab5f6": "The recipe for the machine a session runs on, and the record of every time Kortix prepared one.",
|
|
@@ -3213,7 +3218,7 @@ const catalog = {
|
|
|
3213
3218
|
"text44160e26b787": "Not connected yet — the agent and your triggers cannot call this connector.",
|
|
3214
3219
|
"text442e47369852": "List Kortix public documentation and marketing pages.",
|
|
3215
3220
|
"text44368adad746": "Postgres",
|
|
3216
|
-
"text445cf0a2abfa": "Kortix is building the open-source
|
|
3221
|
+
"text445cf0a2abfa": "Kortix is building the open-source CompanyOS. A company owns all of it — every agent, all of their data, every skill, every connector, the memory, the whole configuration, on their own infrastructure.",
|
|
3217
3222
|
"text446f4eabf99f": "What this agent is for",
|
|
3218
3223
|
"text44dedd0f97fe": "New file",
|
|
3219
3224
|
"text44dfa6d7ebf7": "Could not revoke",
|
|
@@ -4872,8 +4877,9 @@ const catalog = {
|
|
|
4872
4877
|
"textce0faee27592": "billing payment credit card subscription manage wallet tier plan limits overview spend",
|
|
4873
4878
|
"textce18e358bf01": "Try a different term.",
|
|
4874
4879
|
"textce1e609b7bf5": "Sandbox ports only",
|
|
4875
|
-
"textce34af36d804": "Kortix –
|
|
4880
|
+
"textce34af36d804": "Kortix CompanyOS – the open-source operating system for your company",
|
|
4876
4881
|
"textce4683e7013a": "Channel",
|
|
4882
|
+
"text68971283841a": "Subject",
|
|
4877
4883
|
"textce49e021df99": "The session owner or a project manager can reload it.",
|
|
4878
4884
|
"textce4c96225f63": "1 attempt",
|
|
4879
4885
|
"textce5e9df4a78f": "Service account",
|
|
@@ -5635,7 +5641,7 @@ const catalog = {
|
|
|
5635
5641
|
"text2bbd1ec2197d": "Connected app",
|
|
5636
5642
|
"textda3d3fb8f598": "Agent session",
|
|
5637
5643
|
"text4dcb7e3b10a9": "SCIM token",
|
|
5638
|
-
"text5f58426c214e": "We are building the open-source
|
|
5644
|
+
"text5f58426c214e": "We are building the open-source CompanyOS. Every company should own all of it.",
|
|
5639
5645
|
"text80043b03898d": "Update the Kortix app in Teams",
|
|
5640
5646
|
"textdf757dbe33eb": "Your Teams catalog has version {value0} of the Kortix app. Version {value1} has every command and permission your agent uses in Teams. A Teams admin publishes the update. Then a team owner updates the app in each team where Teams offers it: the team → ⋯ → Manage team → Apps → Update.",
|
|
5641
5647
|
"textcd2c4b26eedd": "Kortix has no record of the app version in your Teams catalog. Version {value0} has every command and permission your agent uses in Teams. A Teams admin publishes the update. Then a team owner updates the app in each team where Teams offers it: the team → ⋯ → Manage team → Apps → Update.",
|
|
@@ -6285,7 +6291,7 @@ const catalog = {
|
|
|
6285
6291
|
"createProjectInstead": "Create a new project instead"
|
|
6286
6292
|
},
|
|
6287
6293
|
"projectSelector": {
|
|
6288
|
-
"welcome": "Welcome to Kortix",
|
|
6294
|
+
"welcome": "Welcome to Kortix CompanyOS",
|
|
6289
6295
|
"welcomeBack": "Welcome back",
|
|
6290
6296
|
"descriptionNew": "Create your first project, or join one your team shared.",
|
|
6291
6297
|
"descriptionReturning": "Open a project, or create a new one.",
|
|
@@ -8276,6 +8282,18 @@ const catalog = {
|
|
|
8276
8282
|
"connectLabel": "Connect MCP",
|
|
8277
8283
|
"copyFailed": "Could not copy the command. Try again."
|
|
8278
8284
|
}
|
|
8285
|
+
},
|
|
8286
|
+
"sessionDesktop": {
|
|
8287
|
+
"title": "Desktop",
|
|
8288
|
+
"openTab": "Open in new tab",
|
|
8289
|
+
"offline": "The desktop is not running on this computer.",
|
|
8290
|
+
"start": "Ask Kortix to run",
|
|
8291
|
+
"retry": "Retry",
|
|
8292
|
+
"takeOver": "Take over",
|
|
8293
|
+
"userControl": "You have control",
|
|
8294
|
+
"agentControl": "Kortix has control",
|
|
8295
|
+
"returnControl": "Return control",
|
|
8296
|
+
"escape": "Esc"
|
|
8279
8297
|
}
|
|
8280
8298
|
};
|
|
8281
8299
|
export default catalog;
|
|
@@ -262,7 +262,7 @@ export const REMAINING_UI_TRANSLATION_KEYS = {
|
|
|
262
262
|
'Just you': 'textaeed20a96ee3',
|
|
263
263
|
'Keep model billing with your provider': 'textcd996e5c44e9',
|
|
264
264
|
'Keys are resolved on our side of the wall. The machine the model drives never holds them.': 'texte6229e214e88',
|
|
265
|
-
'Kortix –
|
|
265
|
+
'Kortix CompanyOS – the open-source operating system for your company': 'textce34af36d804',
|
|
266
266
|
'Kortix is building the open AGI platform. A company owns all of it — every agent, all of their data, every skill, every connector, the memory, the whole configuration, on their own infrastructure.': 'text445cf0a2abfa',
|
|
267
267
|
'Kortix — Presentations': 'textd84b8be91e7b',
|
|
268
268
|
'Kortix API documentation': 'text7b3c138b383d',
|
|
@@ -466,7 +466,7 @@ export const REMAINING_UI_TRANSLATION_KEYS = {
|
|
|
466
466
|
'The gate': 'textb46788a7c320',
|
|
467
467
|
'The in-depth platform walkthrough — the full Kortix surface, from projects and sessions to connectors, channels and change requests.': 'text7484ed6d3897',
|
|
468
468
|
'The Kortix team': 'text49e7ba9f095d',
|
|
469
|
-
'The open-source
|
|
469
|
+
'The open-source CompanyOS. Every agent, skill, and memory is a file in one versioned repo you own — a workforce of AI agents that does real work, shared across your whole team from Slack, Teams, the web, or the CLI. Self-hostable, any model, your keys.': 'text2bf70270bfde',
|
|
470
470
|
'The questions people ask before the first session.': 'text2b7df1bf48b2',
|
|
471
471
|
'The record': 'textd9fa05c93112',
|
|
472
472
|
'This session is mid-turn': 'textc116f723c599',
|
|
@@ -814,7 +814,7 @@ export const REMAINING_UI_TRANSLATION_KEYS = {
|
|
|
814
814
|
'per session': 'textf92898c93eec',
|
|
815
815
|
'per message': 'text83d0be53d592',
|
|
816
816
|
answers: 'text677fe21b65f4',
|
|
817
|
-
'We are building the open-source
|
|
817
|
+
'We are building the open-source CompanyOS. Every company should own all of it.': 'text5f58426c214e',
|
|
818
818
|
'One kortix.yaml.': 'text52867b778586',
|
|
819
819
|
'Your AI workforce, as code.': 'textd0dfd435b2f4',
|
|
820
820
|
'Agents, skills, tools, connectors and schedules are files in a repo. Edit them in your IDE, ship them with one command.': 'text6dc0b331852f',
|
|
@@ -90,8 +90,8 @@ const originals = new WeakMap();
|
|
|
90
90
|
* trade-off (host-based tenancy would be the way to remove it).
|
|
91
91
|
*
|
|
92
92
|
* Title: Next writes `<title>` on every navigation from the route's metadata:
|
|
93
|
-
* the site default (`Kortix –
|
|
94
|
-
* routes with no title of their own, `<page> | Kortix` elsewhere. A
|
|
93
|
+
* the site default (`Kortix CompanyOS – the open-source operating system for your company`) on
|
|
94
|
+
* routes with no title of their own, `<page> | Kortix CompanyOS` elsewhere. A
|
|
95
95
|
* `MutationObserver` on `<head>` rewrites whatever Next just wrote, so the
|
|
96
96
|
* swap survives navigation without touching every page's metadata: the site
|
|
97
97
|
* default collapses to the org name alone (its tagline is Kortix's, not
|
|
@@ -190,7 +190,7 @@ function BrandingDocumentEffect({ branding }) {
|
|
|
190
190
|
const current = document.title;
|
|
191
191
|
if (!current.includes('Kortix'))
|
|
192
192
|
return;
|
|
193
|
-
const next = current === siteMetadata.title ? appName : current.replace(/\bKortix
|
|
193
|
+
const next = current === siteMetadata.title ? appName : current.replace(/\bKortix(?: CompanyOS)?\b/g, appName);
|
|
194
194
|
if (next !== current)
|
|
195
195
|
document.title = next;
|
|
196
196
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/** @internal Kortix apps only. No semver: it may change in any release. */
|
|
2
2
|
/**
|
|
3
|
-
* The `notification_center`
|
|
4
|
-
*
|
|
5
|
-
* center: no bell, no inbox request, no Web Push subscription, no mute item,
|
|
6
|
-
* and the four per-browser kind switches in Settings > Notifications.
|
|
3
|
+
* The strict `notification_center` gate controls session mute/watch actions.
|
|
4
|
+
* Inbox surfaces also work when artifacts and artifact_comments are both on.
|
|
7
5
|
*
|
|
8
6
|
* - With a project id: `useFeatureFlag`, fail-closed, on the shared
|
|
9
7
|
* `qk.project.detail(id)` entry that the project shell reads.
|
|
@@ -17,22 +15,28 @@
|
|
|
17
15
|
import type { KortixProject } from '@kortix/sdk';
|
|
18
16
|
import { type QueryClient } from '@tanstack/react-query';
|
|
19
17
|
export declare const NOTIFICATION_CENTER_FLAG = "notification_center";
|
|
20
|
-
type FlagHolder =
|
|
18
|
+
type FlagHolder = {
|
|
19
|
+
experimental?: Partial<NonNullable<KortixProject['experimental']>>;
|
|
20
|
+
} | null | undefined;
|
|
21
21
|
/** The predicate of `useFeatureFlag`: the server said exactly `true`. */
|
|
22
22
|
export declare function notificationCenterOn(project: FlagHolder): boolean;
|
|
23
|
+
export declare function notificationInboxOn(project: FlagHolder): boolean;
|
|
23
24
|
/**
|
|
24
25
|
* The flag for one project from the query cache: its detail, else a cached
|
|
25
26
|
* project list that holds it. Undefined when neither holds it. Sends no request.
|
|
26
27
|
*/
|
|
27
28
|
export declare function cachedNotificationCenterAnswer(client: QueryClient | null, projectId: string | null | undefined): boolean | undefined;
|
|
29
|
+
export declare function cachedNotificationInboxAnswer(client: QueryClient | null, projectId: string | null | undefined): boolean | undefined;
|
|
28
30
|
/** The flag for one project from the query cache. False when nothing cached holds it. */
|
|
29
31
|
export declare function cachedNotificationCenter(client: QueryClient | null, projectId: string | null | undefined): boolean;
|
|
30
32
|
/** True when any project in the query cache has the flag on. */
|
|
31
33
|
export declare function anyCachedNotificationCenter(client: QueryClient): boolean;
|
|
34
|
+
export declare function anyCachedNotificationInbox(client: QueryClient): boolean;
|
|
32
35
|
/** Is the notification center on for `projectId`, or, without one, for any cached project. */
|
|
33
36
|
export declare function useNotificationCenter(projectId?: string | null): boolean;
|
|
37
|
+
export declare function useNotificationInboxEnabled(projectId?: string | null): boolean;
|
|
34
38
|
/**
|
|
35
|
-
* The gate of `NotificationHost
|
|
39
|
+
* The inbox gate of `NotificationHost`, except while the
|
|
36
40
|
* project's detail loads. Then a cached project list answers, else the
|
|
37
41
|
* previous answer stays. So entering a flag-on project from `/projects` or
|
|
38
42
|
* from another flag-on project does not unmount the host and subscribe Web
|
|
@@ -7,25 +7,34 @@ export const NOTIFICATION_CENTER_FLAG = 'notification_center';
|
|
|
7
7
|
export function notificationCenterOn(project) {
|
|
8
8
|
return project?.experimental?.[NOTIFICATION_CENTER_FLAG] === true;
|
|
9
9
|
}
|
|
10
|
+
export function notificationInboxOn(project) {
|
|
11
|
+
return notificationCenterOn(project) || (project?.experimental?.artifacts === true && project.experimental.artifact_comments === true);
|
|
12
|
+
}
|
|
10
13
|
/**
|
|
11
14
|
* The flag for one project from the query cache: its detail, else a cached
|
|
12
15
|
* project list that holds it. Undefined when neither holds it. Sends no request.
|
|
13
16
|
*/
|
|
14
17
|
export function cachedNotificationCenterAnswer(client, projectId) {
|
|
18
|
+
return cachedFlagAnswer(client, projectId, notificationCenterOn);
|
|
19
|
+
}
|
|
20
|
+
function cachedFlagAnswer(client, projectId, enabled) {
|
|
15
21
|
if (!client || !projectId)
|
|
16
22
|
return undefined;
|
|
17
23
|
const detail = client.getQueryData(qk.project.detail(projectId));
|
|
18
24
|
if (detail?.project)
|
|
19
|
-
return
|
|
25
|
+
return enabled(detail.project);
|
|
20
26
|
for (const [, data] of client.getQueriesData({ queryKey: qk.projects.scope() })) {
|
|
21
27
|
if (!Array.isArray(data))
|
|
22
28
|
continue;
|
|
23
29
|
const project = data.find((row) => row?.project_id === projectId);
|
|
24
30
|
if (project)
|
|
25
|
-
return
|
|
31
|
+
return enabled(project);
|
|
26
32
|
}
|
|
27
33
|
return undefined;
|
|
28
34
|
}
|
|
35
|
+
export function cachedNotificationInboxAnswer(client, projectId) {
|
|
36
|
+
return cachedFlagAnswer(client, projectId, notificationInboxOn);
|
|
37
|
+
}
|
|
29
38
|
/** The flag for one project from the query cache. False when nothing cached holds it. */
|
|
30
39
|
export function cachedNotificationCenter(client, projectId) {
|
|
31
40
|
return cachedNotificationCenterAnswer(client, projectId) === true;
|
|
@@ -33,21 +42,24 @@ export function cachedNotificationCenter(client, projectId) {
|
|
|
33
42
|
const [ROOT, LIST_SCOPE] = qk.projects.scope();
|
|
34
43
|
const [, DETAIL_SCOPE, , DETAIL] = qk.project.detail('');
|
|
35
44
|
/** A project-list entry or a project-detail entry that holds a project with the flag on. */
|
|
36
|
-
function holdsFlagOn(query) {
|
|
45
|
+
function holdsFlagOn(query, enabled) {
|
|
37
46
|
const key = query.queryKey;
|
|
38
47
|
const data = query.state.data;
|
|
39
48
|
if (key[0] !== ROOT)
|
|
40
49
|
return false;
|
|
41
50
|
if (key[1] === LIST_SCOPE)
|
|
42
|
-
return Array.isArray(data) && data.some(
|
|
51
|
+
return Array.isArray(data) && data.some(enabled);
|
|
43
52
|
if (key[1] === DETAIL_SCOPE && key.length === 4 && key[3] === DETAIL) {
|
|
44
|
-
return
|
|
53
|
+
return enabled(data?.project);
|
|
45
54
|
}
|
|
46
55
|
return false;
|
|
47
56
|
}
|
|
48
57
|
/** True when any project in the query cache has the flag on. */
|
|
49
58
|
export function anyCachedNotificationCenter(client) {
|
|
50
|
-
return client.getQueryCache().getAll().some(holdsFlagOn);
|
|
59
|
+
return client.getQueryCache().getAll().some((query) => holdsFlagOn(query, notificationCenterOn));
|
|
60
|
+
}
|
|
61
|
+
export function anyCachedNotificationInbox(client) {
|
|
62
|
+
return client.getQueryCache().getAll().some((query) => holdsFlagOn(query, notificationInboxOn));
|
|
51
63
|
}
|
|
52
64
|
/**
|
|
53
65
|
* A `useSyncExternalStore` subscription to the query cache, live only while
|
|
@@ -76,14 +88,24 @@ function useCacheSubscription(client, active) {
|
|
|
76
88
|
function useAnyCachedNotificationCenter(client, scan) {
|
|
77
89
|
return useSyncExternalStore(useCacheSubscription(client, scan), () => scan && anyCachedNotificationCenter(client), () => false);
|
|
78
90
|
}
|
|
91
|
+
function useAnyCachedNotificationInbox(client, scan) {
|
|
92
|
+
return useSyncExternalStore(useCacheSubscription(client, scan), () => scan && anyCachedNotificationInbox(client), () => false);
|
|
93
|
+
}
|
|
79
94
|
/** Is the notification center on for `projectId`, or, without one, for any cached project. */
|
|
80
95
|
export function useNotificationCenter(projectId) {
|
|
81
96
|
const project = useFeatureFlag(projectId, NOTIFICATION_CENTER_FLAG).enabled;
|
|
82
97
|
const anyProject = useAnyCachedNotificationCenter(useQueryClient(), !projectId);
|
|
83
98
|
return projectId ? project : anyProject;
|
|
84
99
|
}
|
|
100
|
+
export function useNotificationInboxEnabled(projectId) {
|
|
101
|
+
const center = useFeatureFlag(projectId, NOTIFICATION_CENTER_FLAG).enabled;
|
|
102
|
+
const artifacts = useFeatureFlag(projectId, 'artifacts').enabled;
|
|
103
|
+
const comments = useFeatureFlag(projectId, 'artifact_comments').enabled;
|
|
104
|
+
const anyProject = useAnyCachedNotificationInbox(useQueryClient(), !projectId);
|
|
105
|
+
return projectId ? center || (artifacts && comments) : anyProject;
|
|
106
|
+
}
|
|
85
107
|
/**
|
|
86
|
-
* The gate of `NotificationHost
|
|
108
|
+
* The inbox gate of `NotificationHost`, except while the
|
|
87
109
|
* project's detail loads. Then a cached project list answers, else the
|
|
88
110
|
* previous answer stays. So entering a flag-on project from `/projects` or
|
|
89
111
|
* from another flag-on project does not unmount the host and subscribe Web
|
|
@@ -100,14 +122,14 @@ export function useNotificationHostGate(projectId) {
|
|
|
100
122
|
const detail = useSyncExternalStore(useCacheSubscription(client, !!projectId), () => (projectId ? client.getQueryState(qk.project.detail(projectId)) : undefined), () => undefined);
|
|
101
123
|
const flag = {
|
|
102
124
|
isLoading: !detail || detail.status === 'pending',
|
|
103
|
-
enabled:
|
|
125
|
+
enabled: notificationInboxOn(detail?.data?.project),
|
|
104
126
|
};
|
|
105
|
-
const anyProject =
|
|
127
|
+
const anyProject = useAnyCachedNotificationInbox(client, !projectId);
|
|
106
128
|
const [previous, setPrevious] = useState(false);
|
|
107
129
|
let answer = anyProject;
|
|
108
130
|
if (projectId) {
|
|
109
131
|
answer = flag.isLoading
|
|
110
|
-
? (
|
|
132
|
+
? (cachedNotificationInboxAnswer(client, projectId) ?? previous)
|
|
111
133
|
: flag.enabled;
|
|
112
134
|
}
|
|
113
135
|
// The answer from an earlier render: React's derived-state pattern.
|
|
@@ -24,6 +24,9 @@ const EXTENSION_PROTOCOL_PREFIXES = [
|
|
|
24
24
|
];
|
|
25
25
|
const INJECTED_APP_SOURCE_PATTERNS = [
|
|
26
26
|
/^app:\/\/\/scripts\/inpage\.js$/,
|
|
27
|
+
// Injected extension content script; KRTX-3704's onMessage TypeError has
|
|
28
|
+
// only this source, never a first-party _next bundle or resolved source.
|
|
29
|
+
/^app:\/\/\/js\/content\.js$/,
|
|
27
30
|
// CookieYes (https://www.cookieyes.com) cookie-consent SDK runtime, served
|
|
28
31
|
// from its own CDN workspace directory
|
|
29
32
|
// `https://cdn-cookieyes.com/client_data/<workspace-id>/…`: `script.js` is
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export const CANONICAL_ORIGIN = 'https://kortix.com';
|
|
10
10
|
export const siteMetadata = {
|
|
11
|
-
name: 'Kortix',
|
|
12
|
-
title: 'Kortix –
|
|
13
|
-
description: 'Open-source
|
|
11
|
+
name: 'Kortix CompanyOS',
|
|
12
|
+
title: 'Kortix CompanyOS – the open-source operating system for your company',
|
|
13
|
+
description: 'Open-source CompanyOS: your agents, skills, memory, and connectors in one repo you own. Any model. Self-host or cloud.',
|
|
14
14
|
url: CANONICAL_ORIGIN,
|
|
15
|
-
keywords: 'Kortix,
|
|
15
|
+
keywords: 'Kortix, Kortix CompanyOS, CompanyOS, open-source CompanyOS, company operating system, what is a CompanyOS, open-source alternative to Claude Cowork, ChatGPT Work alternative, company as a git repo, agents skills and memory as files, shared AI agents, scoped access, self-hosted AI agents, connect 3000 tools, agent orchestration, AI-native company, AI operations',
|
|
16
16
|
};
|