@kortix/ui 0.14.10 → 0.14.11
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/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 +17 -0
- package/dist/i18n/en.js +29 -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/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 +1 -1
- 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 +4 -0
- package/dist/session/panel/mobile-tool-drawer.js +5 -3
- package/dist/session/panel/open-session-quick-view.js +2 -2
- 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/styles.css +1 -1
- package/dist/workspace/session-workspace.js +24 -5
- package/package.json +3 -3
|
@@ -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
|
/**
|
|
@@ -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;
|
|
@@ -8276,5 +8281,17 @@ declare const catalog: {
|
|
|
8276
8281
|
copyFailed: string;
|
|
8277
8282
|
};
|
|
8278
8283
|
};
|
|
8284
|
+
sessionDesktop: {
|
|
8285
|
+
title: string;
|
|
8286
|
+
openTab: string;
|
|
8287
|
+
offline: string;
|
|
8288
|
+
start: string;
|
|
8289
|
+
retry: string;
|
|
8290
|
+
takeOver: string;
|
|
8291
|
+
userControl: string;
|
|
8292
|
+
agentControl: string;
|
|
8293
|
+
returnControl: string;
|
|
8294
|
+
escape: string;
|
|
8295
|
+
};
|
|
8279
8296
|
};
|
|
8280
8297
|
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,7 +4877,7 @@ 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",
|
|
4877
4882
|
"textce49e021df99": "The session owner or a project manager can reload it.",
|
|
4878
4883
|
"textce4c96225f63": "1 attempt",
|
|
@@ -5635,7 +5640,7 @@ const catalog = {
|
|
|
5635
5640
|
"text2bbd1ec2197d": "Connected app",
|
|
5636
5641
|
"textda3d3fb8f598": "Agent session",
|
|
5637
5642
|
"text4dcb7e3b10a9": "SCIM token",
|
|
5638
|
-
"text5f58426c214e": "We are building the open-source
|
|
5643
|
+
"text5f58426c214e": "We are building the open-source CompanyOS. Every company should own all of it.",
|
|
5639
5644
|
"text80043b03898d": "Update the Kortix app in Teams",
|
|
5640
5645
|
"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
5646
|
"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 +6290,7 @@ const catalog = {
|
|
|
6285
6290
|
"createProjectInstead": "Create a new project instead"
|
|
6286
6291
|
},
|
|
6287
6292
|
"projectSelector": {
|
|
6288
|
-
"welcome": "Welcome to Kortix",
|
|
6293
|
+
"welcome": "Welcome to Kortix CompanyOS",
|
|
6289
6294
|
"welcomeBack": "Welcome back",
|
|
6290
6295
|
"descriptionNew": "Create your first project, or join one your team shared.",
|
|
6291
6296
|
"descriptionReturning": "Open a project, or create a new one.",
|
|
@@ -8276,6 +8281,18 @@ const catalog = {
|
|
|
8276
8281
|
"connectLabel": "Connect MCP",
|
|
8277
8282
|
"copyFailed": "Could not copy the command. Try again."
|
|
8278
8283
|
}
|
|
8284
|
+
},
|
|
8285
|
+
"sessionDesktop": {
|
|
8286
|
+
"title": "Desktop",
|
|
8287
|
+
"openTab": "Open in new tab",
|
|
8288
|
+
"offline": "The desktop is not running on this computer.",
|
|
8289
|
+
"start": "Ask Kortix to run",
|
|
8290
|
+
"retry": "Retry",
|
|
8291
|
+
"takeOver": "Take over",
|
|
8292
|
+
"userControl": "You have control",
|
|
8293
|
+
"agentControl": "Kortix has control",
|
|
8294
|
+
"returnControl": "Return control",
|
|
8295
|
+
"escape": "Esc"
|
|
8279
8296
|
}
|
|
8280
8297
|
};
|
|
8281
8298
|
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.
|
|
@@ -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
|
};
|
|
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
* The bell (KRTX-1742): the person's notification inbox, across projects.
|
|
5
5
|
*
|
|
6
6
|
* It sits in the project sidebar header and in `AccountTopBar`, only while the
|
|
7
|
-
*
|
|
7
|
+
* inbox gate is on (`useNotificationInboxEnabled`). The badge is
|
|
8
8
|
* the server's unread count, clamped at 99+. A row opens its session (or the
|
|
9
9
|
* Triggers page for an alert) and marks itself read. When this browser shows
|
|
10
10
|
* no notifications yet, the panel's footer offers to turn them on.
|
|
@@ -33,7 +33,7 @@ import { useNotificationInbox } from '@kortix/sdk/react';
|
|
|
33
33
|
import { BellIcon, ChatCircleIcon, QuestionIcon, ShareIcon, ShieldWarningIcon, WarningIcon, XCircleIcon, XIcon, } from '@phosphor-icons/react';
|
|
34
34
|
import { useState } from 'react';
|
|
35
35
|
import { badgeCount, tabRows, tabUnreadCount, withoutNotificationParam, } from './notification-rows.js';
|
|
36
|
-
import {
|
|
36
|
+
import { useNotificationInboxEnabled } from '../hooks/use-notification-center.js';
|
|
37
37
|
import { webPushSupported } from './web-push.js';
|
|
38
38
|
/** Every kind glyph is solid: Phosphor at `weight="fill"`, the done check is `SolidCheckIcon`. */
|
|
39
39
|
const solid = (Icon) => function SolidKindIcon(props) {
|
|
@@ -51,6 +51,7 @@ const KIND_MARK = {
|
|
|
51
51
|
capture_device_down: { icon: solid(WarningIcon), className: 'text-kortix-red' },
|
|
52
52
|
capture_device_recovered: { icon: SolidCheckIcon, className: 'text-kortix-green' },
|
|
53
53
|
message: { icon: solid(ChatCircleIcon), className: 'text-muted-foreground' },
|
|
54
|
+
artifact_comment: { icon: solid(ChatCircleIcon), className: 'text-muted-foreground' },
|
|
54
55
|
};
|
|
55
56
|
/** Props only: the header, the All / Needs you / Done tabs, the rows, and the footer offer. */
|
|
56
57
|
export function NotificationPanel({ rows, state, unreadCount, prompt, onOpenRow, onMarkAllRead, onRetry, onTurnOn, onDismissPrompt, }) {
|
|
@@ -96,7 +97,7 @@ export function NotificationBell({ projectId, onNavigate, surface = 'bg-backgrou
|
|
|
96
97
|
const t = useTranslations('notifications');
|
|
97
98
|
const viewerId = useUiViewerId();
|
|
98
99
|
const [open, setOpen] = useState(false);
|
|
99
|
-
const notificationCenter =
|
|
100
|
+
const notificationCenter = useNotificationInboxEnabled(projectId);
|
|
100
101
|
const inbox = useNotificationInbox({ userId: viewerId, enabled: notificationCenter });
|
|
101
102
|
const notificationsOn = useWebNotificationStore((s) => s.preferences.enabled);
|
|
102
103
|
const permission = useWebNotificationStore((s) => s.permission);
|
|
@@ -55,7 +55,7 @@ export declare function notificationTag(row: InboxNotification): string;
|
|
|
55
55
|
* conversation (a message), its session, the Reminders page (a reminder
|
|
56
56
|
* alert), or the Triggers page.
|
|
57
57
|
*/
|
|
58
|
-
export declare function rowDestination(row: InboxNotification): 'room' | 'session' | 'reminders' | 'triggers';
|
|
58
|
+
export declare function rowDestination(row: InboxNotification): 'artifact' | 'room' | 'session' | 'reminders' | 'triggers';
|
|
59
59
|
/** The message `public/sw.js` posts to a tab already on a clicked notification's page. */
|
|
60
60
|
export declare const NOTIFICATION_OPEN_MESSAGE = "kortix:notification-open";
|
|
61
61
|
/** The notification id a service-worker message says this tab opened, or null. */
|
|
@@ -12,7 +12,7 @@ export interface ReadyChipState {
|
|
|
12
12
|
primaryName?: string;
|
|
13
13
|
}
|
|
14
14
|
/** The panel surfaces reachable from outside the panel. */
|
|
15
|
-
export type QuickView = 'terminal' | 'audit' | 'browser' | 'files' | 'reminders';
|
|
15
|
+
export type QuickView = 'terminal' | 'audit' | 'browser' | 'files' | 'reminders' | 'desktop';
|
|
16
16
|
/**
|
|
17
17
|
* Extra aim for a quick-view request — "open the browser" vs "open the browser
|
|
18
18
|
* ON THIS URL".
|