Skip to content

Commit a4856a8

Browse files
fix: dojo sidebar collapsing, add theme color when in cpk iframe
1 parent 080435b commit a4856a8

File tree

5 files changed

+97
-21
lines changed

5 files changed

+97
-21
lines changed

typescript-sdk/apps/dojo/src/app/globals.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
--cohere: hsl(0 80% 60%); /* Bright red */ --background: oklch(1 0 0); --foreground: oklch(0.145 0 0);
188188

189189
--cpk-docs-dark-bg: oklch(0.274 0.006 286.033);
190+
--cpk-docs-primary: oklch(0.55 0.25 285);
190191
}
191192

192193
.dark {
@@ -271,6 +272,12 @@
271272
--color-sidebar-border: var(--sidebar-border);
272273
--color-sidebar-ring: var(--sidebar-ring);
273274
--color-cpk-docs-dark-bg: var(--cpk-docs-dark-bg);
275+
--color-cpk-docs-primary: var(--cpk-docs-primary);
276+
277+
/* supporting variables for color-mix (see related color-mix-* utilities below */
278+
--color-mix-from: var(--tw-mix-from);
279+
--color-mix-to: var(--tw-mix-to);
280+
--color-mix: var(--tw-mix);
274281
}
275282

276283
@layer base {
@@ -290,4 +297,56 @@
290297
/* hide scroll decoration on code blocks */
291298
.monaco-editor .overflow-guard .scroll-decoration {
292299
@apply hidden!;
300+
}
301+
302+
@utility mix-from-* {
303+
--tw-mix-from-color: --value(--color-*, color, [color]);
304+
--tw-mix-from-opacity: --modifier(--opacity-*, [percentage]);
305+
--tw-mix-from-opacity: calc(--modifier(number) * 1%);
306+
--tw-mix-from-opacity: calc(--modifier([number]) * 100%);
307+
--tw-mix-from: --alpha(var(--tw-mix-from-color) / var(--tw-mix-from-opacity, 100%));
308+
/* calculate the final color-mix result */
309+
--tw-mix: color-mix(color-mix(in srgb, var(--color-mix-from), var(--color-mix-to) var(--tw-mix-percent, 50%)) var(--tw-mix-opacity, 100%), transparent);
310+
@supports (color: color-mix(in lab, red, red)) {
311+
--tw-mix: color-mix(color-mix(in oklab, var(--color-mix-from), var(--color-mix-to) var(--tw-mix-percent, 50%)) var(--tw-mix-opacity, 100%), transparent);
312+
}
313+
/* redefining the color variables here to ensure they receive the latest values */
314+
--color-mix-from: var(--tw-mix-from);
315+
--color-mix-to: var(--tw-mix-to);
316+
--color-mix: var(--tw-mix);
317+
}
318+
319+
@utility mix-to-* {
320+
--tw-mix-to-color: --value(--color-*, color, [color]);
321+
--tw-mix-to-opacity: --modifier(--opacity-*, [percentage]);
322+
--tw-mix-to-opacity: calc(--modifier(number) * 1%);
323+
--tw-mix-to-opacity: calc(--modifier([number]) * 100%);
324+
--tw-mix-to: --alpha(var(--tw-mix-to-color) / var(--tw-mix-to-opacity, 100%));
325+
/* calculate the final color-mix result */
326+
--tw-mix: color-mix(color-mix(in srgb, var(--color-mix-from), var(--color-mix-to) var(--tw-mix-percent, 50%)) var(--tw-mix-opacity, 100%), transparent);
327+
@supports (color: color-mix(in lab, red, red)) {
328+
--tw-mix: color-mix(color-mix(in oklab, var(--color-mix-from), var(--color-mix-to) var(--tw-mix-percent, 50%)) var(--tw-mix-opacity, 100%), transparent);
329+
}
330+
/* redefining the color variables here to ensure they receive the latest values */
331+
--color-mix-from: var(--tw-mix-from);
332+
--color-mix-to: var(--tw-mix-to);
333+
--color-mix: var(--tw-mix);
334+
}
335+
336+
@utility mix-* {
337+
--tw-mix-percent: --value(--opacity-*, [percentage]);
338+
--tw-mix-percent: calc(--value(number) * 1%);
339+
--tw-mix-percent: calc(--value([number]) * 100%);
340+
--tw-mix-opacity: --modifier(--opacity-*, [percentage]);
341+
--tw-mix-opacity: calc(--modifier(number) * 1%);
342+
--tw-mix-opacity: calc(--modifier([number]) * 100%);
343+
/* calculate the final color-mix result */
344+
--tw-mix: color-mix(in srgb, color-mix(in srgb, var(--color-mix-from), var(--color-mix-to) var(--tw-mix-percent, 50%)) var(--tw-mix-opacity, 100%), transparent);
345+
@supports (color: color-mix(in lab, red, red)) {
346+
--tw-mix: color-mix(in oklab, color-mix(in oklab, var(--color-mix-from), var(--color-mix-to) var(--tw-mix-percent, 50%)) var(--tw-mix-opacity, 100%), transparent);
347+
}
348+
/* redefining the color variables here to ensure they receive the latest values */
349+
--color-mix-from: var(--tw-mix-from);
350+
--color-mix-to: var(--tw-mix-to);
351+
--color-mix: var(--tw-mix);
293352
}

typescript-sdk/apps/dojo/src/components/code-viewer/code-viewer.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ import { CodeEditor } from "./code-editor";
44
import { FeatureFile } from "@/types/feature";
55
import { useURLParams } from "@/contexts/url-params-context";
66
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
7-
import { useIsInsideIframe } from "@/utils/use-in-iframe";
7+
import { useIsInsideCpkFrame } from "@/utils/use-is-inside-iframe";
88
import { cn } from "@/lib/utils";
99

1010
export default function CodeViewer({ codeFiles }: { codeFiles: FeatureFile[] }) {
1111
const { file, setCodeFile, codeLayout } = useURLParams();
12-
const isInsideIframe = useIsInsideIframe();
13-
const isInsideCpkFrame = isInsideIframe && (document.referrer.includes("copilotkit.com") || document.referrer.includes("localhost"));
12+
const isInsideCpkFrame = useIsInsideCpkFrame();
1413

1514
const selectedFile = useMemo(
1615
() => codeFiles.find((f) => f.name === file) ?? codeFiles[0],
@@ -30,7 +29,12 @@ export default function CodeViewer({ codeFiles }: { codeFiles: FeatureFile[] })
3029
<TabsTrigger
3130
key={file.name}
3231
value={file.name}
33-
className="border-0 shadow-none text-gray-600 dark:text-neutral-300 hover:bg-foreground/5 hover:text-gray-900 dark:hover:text-neutral-100 data-[state=active]:bg-foreground/8 data-[state=active]:text-gray-900 dark:data-[state=active]:text-white"
32+
className={cn(
33+
"border-0 shadow-none hover:bg-foreground/5 hover:text-gray-900 dark:hover:text-neutral-100 data-[state=active]:text-gray-900 dark:data-[state=active]:text-white",
34+
isInsideCpkFrame || true
35+
? "mix-from-cpk-docs-primary mix-to-white mix-25 data-[state=active]:bg-mix/15 data-[state=active]:text-cpk-docs-primary data-[state=active]:dark:text-mix"
36+
: "data-[state=active]:bg-foreground/8 text-gray-600 dark:text-neutral-300",
37+
)}
3438
>
3539
{file.name.split("/").pop()}
3640
</TabsTrigger>

typescript-sdk/apps/dojo/src/components/file-tree/file-tree.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from "react";
22
import { ChevronDown, ChevronRight, File, Folder } from "lucide-react";
33
import { cn } from "@/lib/utils";
44
import { FeatureFile } from "@/types/feature";
5+
import { useIsInsideCpkFrame } from "@/utils/use-is-inside-iframe";
56

67
interface FileTreeProps {
78
files: FeatureFile[];
@@ -23,15 +24,19 @@ function FileTreeNode({
2324
const [isOpen, setIsOpen] = React.useState(true);
2425
const isDirectory = entry.type === "directory";
2526
const isSelected = entry.name === selectedFileName;
27+
const isInsideCpkFrame = useIsInsideCpkFrame();
2628

2729
return (
2830
<div className={cn("relative", depth > 0 && "pl-2")}>
2931
{depth > 0 && <div className="absolute left-0 top-0 h-full w-px bg-border" />}
3032
<button
3133
className={cn(
3234
"flex w-full items-center gap-2 rounded-sm px-2 py-1 text-sm text-gray-700 dark:text-gray-200",
35+
"mix-from-cpk-docs-primary mix-to-white mix-25",
3336
!isSelected && "hover:bg-foreground/5 hover:text-gray-900 dark:hover:text-white",
34-
isSelected && "bg-foreground/10 text-gray-900 dark:text-white",
37+
isSelected && (isInsideCpkFrame || true
38+
? "bg-mix/15 text-cpk-docs-primary dark:text-mix"
39+
: "bg-foreground/10 text-gray-900 dark:text-white"),
3540
depth === 1 && "ml-0.5",
3641
depth === 2 && "ml-1",
3742
depth === 3 && "ml-1.5",

typescript-sdk/apps/dojo/src/utils/use-in-iframe.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useEffect, useState } from "react";
2+
3+
export function useIsInsideIframe() {
4+
const [isInside, setIsInside] = useState(false);
5+
6+
useEffect(() => {
7+
const check = () => {
8+
setIsInside(window.self !== window.top);
9+
};
10+
check();
11+
}, []);
12+
13+
return isInside;
14+
}
15+
16+
// returns true if the iframe is inside a copilotkit.com or localhost (for local internal development)
17+
export function useIsInsideCpkFrame(): boolean {
18+
const isInsideIframe = useIsInsideIframe();
19+
if (!isInsideIframe || typeof document === "undefined") return false;
20+
const referrer = document.referrer ?? "";
21+
const isInsideCpkFrame =
22+
referrer.includes("copilotkit.com") || referrer.includes("localhost");
23+
return isInsideCpkFrame;
24+
}

0 commit comments

Comments
 (0)