9a7ad926aa
* WIP on using react-window-splitter
* WIP with new resizable panels and SSR
* Use the cookie package
* Resizable storybook page
* Increase indexing memory limit
* Fixed v2 usage meter displaying when on paid plan (#1255)
* Fixed v2 usage meter displaying when on paid plan
* Show the free usage panel only for v3 projects
* Concurrency page and more accurate tracking (#1252)
* Initial TaskRunConcurrencyTracker implementation
* MARQS calls a subscriber to events
* When enqueuing add the extra required metadata
* Track concurrency per environment for tasks too
* Admin page for global concurrency
* Use the new concurrency tracker on the tasks page
* Useful performance test task
* getAllTaskIdentifiers()
* New page for concurrency
* BackgroundWorkerTask index for quick lookup of task identifiers
* Added a way to get concurrency for environments
* Added upgrade/request more concurrency button
* Queued task column working
* Use defer and suspense
* Added queue column to the concurrency environments table
* Some comments added for clarity
* Fixed bad log message
* Sidemenu: move lower and rename to “Concurrency limits”
* Only show the environments, not tasks. Renamed to “Concurrency limits”
* v3: fix unfreezable state crashes for runs with multiple waits (#1253)
* support named capture groups
* write crash errors to attempt.error
* make restored pod names unique per checkpoint
* use last eight characters of checkpoint id instead
* add more chaos monkey env vars
* Ignore unfreezable states
* prevent excessive queue config parsing errors
* handle dependency resume edge case
* better entry point logging
* ignore checkpoint cancellation timeouts
* add missing idempotency keys to wait for dep replays
* remove checkpoints between attempts
* fix retry container names on kubernetes
* add changeset
* fix types
* bring back internal duration timers
* Added more logging to TaskRunConcurrencyTracker and some more try/catches
* Call subscriber.messageDequeued in dequeueMessageInSharedQueue
* Added messageReplaced to concurrency tracking (when freezing)
* Added depenenciesToBundle guide to bundle all packages
* Include the old message data when replacing, so we get the projectId etc.
* Fix restored container names
* Fix for schedule page not scrolling
* Added a description panel to the Concurrency admin page
* chore: Update version for release (beta) (#1256)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Release 3.0.0-beta.53
* Added a note to use batchTrigger() instead of trigger()
* The latest react-window-splitter fixes the ESM issues
* Set sensible defaults for the run page
* Deployments page
* Test page
* Schedules page
* Latest version of react-window-splitter (0.2.5)
* Updated to the latest version: react-window-splitter
* Callout if runs don’t start right away now has some top margin
* Small padding fix
* styled the handle focus state
* Added isStaticAtRest prop to resizable panel
* Updated resizable storybook
* Inline code blocks behave nicer when text wraps
* Added ElectricSQL to docker-compose, available on 3060
* Extracted some logic out of the eventRepository for getting a trace. This will be used on the frontend
* Use the new util
* More restructuring ready to use the trace summary from the frontend
* Using ElectricSQL for the run page data
* Min size for resizable panel on test page
* Don’t load the trace in the RunPresenter anymore
* Fix for the resizable panels on the run page
* Added overflow hidden to the panel group
* min size for the test page left hand panel
* Updated to latest window-splitter version
* Removed unused const
* One fix for client-server mismatch
* Slight improvement in the loading state
* Restructured the page so the loading is better
* Improvement to the loading states
* Improved the loading behaviour with the inspector
* WIP on auth, having problems with it
* Upgrade Remix to 2.9.1 (same as PR #1096)
* Switched structure around again so we only call the useTrace hook from the client
* Added auth to the sync
* Overscan more rows in the tree view
* Fix for TS error
* Remove duplicate import
* Revert "Upgrade Remix to 2.9.1 (same as PR #1096)"
This reverts commit e63ee9e924.
* save cookie only when id is used
* Deployment table now scrolls
* removed imports
* A lot of changes to make the inspector live too… WIP
* More major overhauls to get the synced version of the run page working…
* If a span is completed show that
* Set the debounce much lower for selecting the span view
* Load the details run inspector data on demand
* Delete the SpanPresenter
* Use the async payload because it deals with superjson
* Fixed weird merge conflict
* Share some inspector timeline components
* A couple of layout tweaks
* Improved the run inspector loading states
* Fix for paragaph errors
* Fix for focusing on a span
* Undefined typre for useSyncedShape
* ELECTRIC_ORIGIN env var doesn’t have a default, added to the examples
* Updated @electric-sql/react package to the latest
* Fix the timeline duration stretching
* Added some better error handling for the electric sync
* More logging
* Better error when there are bad responses
* Turn off resizable snapshots, there’s a bug
* Added getSpan back
* Added SpanPresenter back
* Updated to the new Electric hooks package
* Made a copy so we have the old run page and the new electric one
* Put the main eventRepository back for now
---------
Co-authored-by: nicktrn <55853254+nicktrn@users.noreply.github.com>
Co-authored-by: James Ritchie <james@trigger.dev>
Co-authored-by: James Ritchie <james@jamesritchie.co.uk>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
397 lines
11 KiB
TypeScript
397 lines
11 KiB
TypeScript
import { Clipboard, ClipboardCheck } from "lucide-react";
|
|
import type { Language, PrismTheme } from "prism-react-renderer";
|
|
import { Highlight, Prism } from "prism-react-renderer";
|
|
import { forwardRef, ReactNode, useCallback, useState } from "react";
|
|
import { cn } from "~/utils/cn";
|
|
import { Paragraph } from "../primitives/Paragraph";
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
|
|
|
|
//This is a fork of https://github.com/mantinedev/mantine/blob/master/src/mantine-prism/src/Prism/Prism.tsx
|
|
//it didn't support highlighting lines by dimming the rest of the code, or animations on the highlighting
|
|
|
|
async function setup() {
|
|
(typeof global !== "undefined" ? global : window).Prism = Prism;
|
|
//@ts-ignore
|
|
await import("prismjs/components/prism-json");
|
|
//@ts-ignore
|
|
await import("prismjs/components/prism-typescript");
|
|
}
|
|
setup();
|
|
|
|
type CodeBlockProps = {
|
|
/** Code which will be highlighted */
|
|
code: string;
|
|
|
|
/** Programming language that should be highlighted */
|
|
language?: Language;
|
|
|
|
/** Show copy to clipboard button */
|
|
showCopyButton?: boolean;
|
|
|
|
/** Display line numbers */
|
|
showLineNumbers?: boolean;
|
|
|
|
/** Highlight line at given line number with color from theme.colors */
|
|
highlightedRanges?: [number, number][];
|
|
|
|
/** Add/override classes on the overall element */
|
|
className?: string;
|
|
|
|
/** Add/override code theme */
|
|
theme?: PrismTheme;
|
|
|
|
/** Max lines */
|
|
maxLines?: number;
|
|
|
|
/** Whether to show the chrome, if you provide a string it will be used as the title, */
|
|
showChrome?: boolean;
|
|
|
|
/** filename */
|
|
fileName?: string;
|
|
|
|
/** title text for the Title row */
|
|
rowTitle?: string;
|
|
};
|
|
|
|
const dimAmount = 0.5;
|
|
const extraLinesWhenClipping = 0.35;
|
|
|
|
const defaultTheme: PrismTheme = {
|
|
plain: {
|
|
color: "#9C9AF2",
|
|
backgroundColor: "rgba(0, 0, 0, 0)",
|
|
},
|
|
styles: [
|
|
{
|
|
types: ["comment", "prolog", "doctype", "cdata"],
|
|
style: {
|
|
color: "#5F6570",
|
|
},
|
|
},
|
|
{
|
|
types: ["punctuation"],
|
|
style: {
|
|
color: "#878C99",
|
|
},
|
|
},
|
|
{
|
|
types: ["property", "tag", "boolean", "number", "constant", "symbol", "deleted"],
|
|
style: {
|
|
color: "#9B99FF",
|
|
},
|
|
},
|
|
{
|
|
types: ["selector", "attr-name", "string", "char", "builtin", "inserted"],
|
|
style: {
|
|
color: "#AFEC73",
|
|
},
|
|
},
|
|
{
|
|
types: ["operator", "entity", "url"],
|
|
style: {
|
|
color: "#D4D4D4",
|
|
},
|
|
},
|
|
{
|
|
types: ["variable"],
|
|
style: {
|
|
color: "#CCCBFF",
|
|
},
|
|
},
|
|
{
|
|
types: ["atrule", "attr-value", "keyword"],
|
|
style: {
|
|
color: "#E888F8",
|
|
},
|
|
},
|
|
{
|
|
types: ["function", "class-name"],
|
|
style: {
|
|
color: "#D9F07C",
|
|
},
|
|
},
|
|
{
|
|
types: ["regex"],
|
|
style: {
|
|
color: "#d16969",
|
|
},
|
|
},
|
|
{
|
|
types: ["important", "bold"],
|
|
style: {
|
|
fontWeight: "bold",
|
|
},
|
|
},
|
|
{
|
|
types: ["italic"],
|
|
style: {
|
|
fontStyle: "italic",
|
|
},
|
|
},
|
|
{
|
|
types: ["namespace"],
|
|
style: {
|
|
opacity: 0.7,
|
|
},
|
|
},
|
|
{
|
|
types: ["deleted"],
|
|
style: {
|
|
color: "#F85149",
|
|
},
|
|
},
|
|
{
|
|
types: ["boolean"],
|
|
style: {
|
|
color: "#9B99FF",
|
|
},
|
|
},
|
|
{
|
|
types: ["char"],
|
|
style: {
|
|
color: "#b5cea8",
|
|
},
|
|
},
|
|
{
|
|
types: ["tag"],
|
|
style: {
|
|
color: "#D7BA7D",
|
|
},
|
|
},
|
|
{
|
|
types: ["keyword.operator"],
|
|
style: {
|
|
color: "#8271ED",
|
|
},
|
|
},
|
|
{
|
|
types: ["meta.template.expression"],
|
|
style: {
|
|
color: "#d4d4d4",
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
|
(
|
|
{
|
|
showCopyButton = true,
|
|
showLineNumbers = true,
|
|
highlightedRanges,
|
|
code,
|
|
className,
|
|
language = "typescript",
|
|
theme = defaultTheme,
|
|
maxLines,
|
|
showChrome = false,
|
|
fileName,
|
|
rowTitle,
|
|
...props
|
|
}: CodeBlockProps,
|
|
ref
|
|
) => {
|
|
const [mouseOver, setMouseOver] = useState(false);
|
|
const [copied, setCopied] = useState(false);
|
|
const onCopied = useCallback(
|
|
(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
navigator.clipboard.writeText(code);
|
|
setCopied(true);
|
|
setTimeout(() => {
|
|
setCopied(false);
|
|
}, 1500);
|
|
},
|
|
[code]
|
|
);
|
|
|
|
code = code.trim();
|
|
const lineCount = code.split("\n").length;
|
|
const maxLineWidth = lineCount.toString().length;
|
|
let maxHeight: string | undefined = undefined;
|
|
if (maxLines && lineCount > maxLines) {
|
|
maxHeight = `calc(${(maxLines + extraLinesWhenClipping) * 0.75 * 1.625}rem + 1.5rem )`;
|
|
}
|
|
|
|
const highlightLines = highlightedRanges?.flatMap(([start, end]) =>
|
|
Array.from({ length: end - start + 1 }, (_, i) => start + i)
|
|
);
|
|
|
|
// if there are more than 1000 lines, don't highlight
|
|
const shouldHighlight = lineCount <= 1000;
|
|
|
|
return (
|
|
<div
|
|
className={cn("relative overflow-hidden rounded-md border border-grid-bright", className)}
|
|
style={{
|
|
backgroundColor: theme.plain.backgroundColor,
|
|
}}
|
|
ref={ref}
|
|
{...props}
|
|
translate="no"
|
|
>
|
|
{showChrome && <Chrome title={fileName} />}
|
|
{rowTitle && <TitleRow title={rowTitle} />}
|
|
{showCopyButton && (
|
|
<TooltipProvider>
|
|
<Tooltip open={copied || mouseOver}>
|
|
<TooltipTrigger
|
|
onClick={onCopied}
|
|
onMouseEnter={() => setMouseOver(true)}
|
|
onMouseLeave={() => setMouseOver(false)}
|
|
className={cn(
|
|
"absolute right-3 z-50 transition-colors duration-100 hover:cursor-pointer",
|
|
showChrome ? "top-10" : "top-2.5",
|
|
copied ? "text-emerald-500" : "text-charcoal-500 hover:text-charcoal-300"
|
|
)}
|
|
>
|
|
{copied ? <ClipboardCheck className="size-4" /> : <Clipboard className="size-4" />}
|
|
</TooltipTrigger>
|
|
<TooltipContent side="left" className="text-xs">
|
|
{copied ? "Copied" : "Copy"}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
)}
|
|
|
|
{shouldHighlight ? (
|
|
<Highlight theme={theme} code={code} language={language}>
|
|
{({
|
|
className: inheritedClassName,
|
|
style: inheritedStyle,
|
|
tokens,
|
|
getLineProps,
|
|
getTokenProps,
|
|
}) => (
|
|
<div
|
|
dir="ltr"
|
|
className="overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
|
|
style={{
|
|
maxHeight,
|
|
}}
|
|
>
|
|
<pre
|
|
className={cn(
|
|
"relative mr-2 font-mono text-xs leading-relaxed",
|
|
inheritedClassName
|
|
)}
|
|
style={inheritedStyle}
|
|
dir="ltr"
|
|
>
|
|
{tokens
|
|
.map((line, index) => {
|
|
if (
|
|
index === tokens.length - 1 &&
|
|
line.length === 1 &&
|
|
line[0].content === "\n"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const lineNumber = index + 1;
|
|
const lineProps = getLineProps({ line, key: index });
|
|
|
|
let hasAnyHighlights = highlightLines ? highlightLines.length > 0 : false;
|
|
|
|
let shouldDim = hasAnyHighlights;
|
|
if (hasAnyHighlights && highlightLines?.includes(lineNumber)) {
|
|
shouldDim = false;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
key={lineNumber}
|
|
{...lineProps}
|
|
className={cn(
|
|
"flex w-full justify-start transition-opacity duration-500",
|
|
lineProps.className
|
|
)}
|
|
style={{
|
|
opacity: shouldDim ? dimAmount : undefined,
|
|
...lineProps.style,
|
|
}}
|
|
>
|
|
{showLineNumbers && (
|
|
<div
|
|
className={
|
|
"mr-2 flex-none select-none text-right text-charcoal-500 transition-opacity duration-500"
|
|
}
|
|
style={{
|
|
width: `calc(8 * ${maxLineWidth / 16}rem)`,
|
|
}}
|
|
>
|
|
{lineNumber}
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex-1">
|
|
{line.map((token, key) => {
|
|
const tokenProps = getTokenProps({ token, key });
|
|
return (
|
|
<span
|
|
key={key}
|
|
{...tokenProps}
|
|
style={{
|
|
color: tokenProps?.style?.color as string,
|
|
...tokenProps.style,
|
|
}}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
<div className="w-4 flex-none" />
|
|
</div>
|
|
);
|
|
})
|
|
.filter(Boolean)}
|
|
</pre>
|
|
</div>
|
|
)}
|
|
</Highlight>
|
|
) : (
|
|
<div
|
|
dir="ltr"
|
|
className="overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
|
|
style={{
|
|
maxHeight,
|
|
}}
|
|
>
|
|
<pre className="relative mr-2 p-2 font-mono text-xs leading-relaxed" dir="ltr">
|
|
{code}
|
|
</pre>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
CodeBlock.displayName = "CodeBlock";
|
|
|
|
function Chrome({ title }: { title?: string }) {
|
|
return (
|
|
<div className="grid h-7 grid-cols-[100px_auto_100px] border-b border-charcoal-800 bg-charcoal-900">
|
|
<div className="ml-2 flex items-center gap-2">
|
|
<div className="h-3 w-3 rounded-full bg-charcoal-700" />
|
|
<div className="h-3 w-3 rounded-full bg-charcoal-700" />
|
|
<div className="h-3 w-3 rounded-full bg-charcoal-700" />
|
|
</div>
|
|
<div className="flex items-center justify-center">
|
|
<div className={cn("rounded-sm px-3 py-0.5 text-xs text-charcoal-500")}>{title}</div>
|
|
</div>
|
|
<div></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function TitleRow({ title }: { title: ReactNode }) {
|
|
return (
|
|
<div className="flex items-center justify-between px-3">
|
|
<Paragraph variant="small/bright" className="w-full border-b border-grid-dimmed py-2">
|
|
{title}
|
|
</Paragraph>
|
|
</div>
|
|
);
|
|
}
|