Skip to content

Commit

Permalink
Merge pull request #3180 from quantified-uncertainty/style-updates-ap…
Browse files Browse the repository at this point in the history
…ril-2024

New Model+Variable Card Layouts
  • Loading branch information
OAGr authored Apr 17, 2024
2 parents 1bd4f46 + 5423661 commit fb7c2c1
Show file tree
Hide file tree
Showing 21 changed files with 496 additions and 215 deletions.
3 changes: 3 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export { SquiggleErrorAlert } from "./components/ui/SquiggleErrorAlert.js";

export { RelativeValueCell } from "./widgets/PlotWidget/RelativeValuesGridChart/RelativeValueCell.js";

export { MarkdownViewer } from "./lib/MarkdownViewer.js";
export { CodeSyntaxHighlighter } from "./lib/CodeSyntaxHighlighter.js";

// for use in relative values
export {
type DrawContext,
Expand Down
79 changes: 79 additions & 0 deletions packages/components/src/lib/CodeSyntaxHighlighter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { FC, HTMLAttributes, useEffect, useState } from "react";
import {
type BundledLanguage,
bundledLanguages,
getHighlighter,
type Highlighter,
type LanguageRegistration,
} from "shikiji";

// Import assertion here would be nice, but storybook doesn't support "assert" syntax yet, only "with" syntax,
// and our import sorter auto-replaces with the newer "assert" syntax.
// This will be fixed in storybook eventually, https://github.com/storybookjs/storybook/issues/23599
import squiggleGrammar from "@quri/squiggle-textmate-grammar/dist/squiggle.tmLanguage.json";

type SupportedLanguage = BundledLanguage | "squiggle";

let _shiki: Highlighter; // cached singleton
type Theme = "vitesse-light" | "github-light";

async function codeToHtml(params: {
code: string;
language: SupportedLanguage;
theme?: Theme;
}) {
const _theme = params.theme || "vitesse-light";
if (!_shiki) {
_shiki = await getHighlighter({
themes: ["vitesse-light", "github-light"],
langs: [
{
name: "squiggle",
...squiggleGrammar,
} as unknown as LanguageRegistration, // shikiji requires repository.$self and repository.$base that we don't have
],
});
}
if (
params.language !== "squiggle" &&
!_shiki.getLoadedLanguages().includes(params.language)
) {
await _shiki.loadLanguage(params.language);
await _shiki.loadLanguage(params.language); // somehow repeating it twice fixes the loading issue
}

return _shiki.codeToHtml(params.code, {
theme: _theme, // TODO - write a custom theme that would match Codemirror styles
lang: params.language,
});
}

function isSupportedLanguage(language: string): language is BundledLanguage {
return language === "squiggle" || language in bundledLanguages;
}

export const CodeSyntaxHighlighter: FC<
{ children: string; language: string; theme?: Theme } & Omit<
HTMLAttributes<HTMLElement>,
"children"
>
> = ({ children, language, theme, ...rest }) => {
const [html, setHtml] = useState(children);

// Syntax-highlighted blocks will start unstyled, that's fine.
useEffect(() => {
(async () => {
if (isSupportedLanguage(language)) {
setHtml(await codeToHtml({ code: children, language, theme }));
}
})();
});

return (
<pre
className="*:!bg-inherit" // shiki themes add background color, so we have to override it
dangerouslySetInnerHTML={{ __html: html }}
{...rest}
/>
);
};
89 changes: 12 additions & 77 deletions packages/components/src/lib/MarkdownViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,12 @@
import clsx from "clsx";
import { Element } from "hast";
import React, { FC, HTMLAttributes, useEffect, useState } from "react";
import React from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import {
type BundledLanguage,
bundledLanguages,
getHighlighter,
type Highlighter,
type LanguageRegistration,
} from "shikiji";
import { Node, Parent } from "unist";
import { visitParents } from "unist-util-visit-parents";

// Import assertion here would be nice, but storybook doesn't support "assert" syntax yet, only "with" syntax,
// and our import sorter auto-replaces with the newer "assert" syntax.
// This will be fixed in storybook eventually, https://github.com/storybookjs/storybook/issues/23599
import squiggleGrammar from "@quri/squiggle-textmate-grammar/dist/squiggle.tmLanguage.json";

type SupportedLanguage = BundledLanguage | "squiggle";

let _shiki: Highlighter; // cached singleton
async function codeToHtml(params: {
code: string;
language: SupportedLanguage;
}) {
if (!_shiki) {
_shiki = await getHighlighter({
themes: ["vitesse-light"],
langs: [
{
name: "squiggle",
...squiggleGrammar,
} as unknown as LanguageRegistration, // shikiji requires repository.$self and repository.$base that we don't have
],
});
}
if (
params.language !== "squiggle" &&
!_shiki.getLoadedLanguages().includes(params.language)
) {
await _shiki.loadLanguage(params.language);
await _shiki.loadLanguage(params.language); // somehow repeating it twice fixes the loading issue
}

return _shiki.codeToHtml(params.code, {
theme: "vitesse-light", // TODO - write a custom theme that would match Codemirror styles
lang: params.language,
});
}

function isSupportedLanguage(language: string): language is BundledLanguage {
return language === "squiggle" || language in bundledLanguages;
}
import { CodeSyntaxHighlighter } from "./CodeSyntaxHighlighter.js";

// Adds `inline` property to `code` elements, to distinguish between inline and block code snippets.
function rehypeInlineCodeProperty() {
Expand All @@ -71,43 +25,19 @@ function rehypeInlineCodeProperty() {
};
}

const SyntaxHighlighter: FC<
{ children: string; language: string } & Omit<
HTMLAttributes<HTMLElement>,
"children"
>
> = ({ children, language, ...rest }) => {
const [html, setHtml] = useState(children);

// Syntax-highlighted blocks will start unstyled, that's fine.
useEffect(() => {
(async () => {
if (isSupportedLanguage(language)) {
setHtml(await codeToHtml({ code: children, language }));
}
})();
});

return (
<div
className="*:!bg-inherit" // shiki themes add background color, so we have to override it
dangerouslySetInnerHTML={{ __html: html }}
{...rest}
/>
);
};

type MarkdownViewerProps = {
md: string;
textSize: "sm" | "xs";
textColor?: "prose-stone" | "prose-slate";
className?: string;
backgroundColor?: string;
};
export const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
md,
className,
textColor,
textSize,
backgroundColor = "bg-slate-50",
}) => {
return (
<ReactMarkdown
Expand All @@ -122,7 +52,12 @@ export const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
components={{
pre({ children }) {
return (
<pre className="not-prose my-1 rounded bg-slate-50 p-2 text-[.9em]">
<pre
className={clsx(
"not-prose my-1 rounded p-2 text-[.9em]",
backgroundColor
)}
>
{children}
</pre>
);
Expand All @@ -142,9 +77,9 @@ export const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
);
}
return match ? (
<SyntaxHighlighter {...rest} language={match[1]}>
<CodeSyntaxHighlighter {...rest} language={match[1]}>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
</CodeSyntaxHighlighter>
) : (
<code {...rest}>{children}</code>
);
Expand Down
45 changes: 45 additions & 0 deletions packages/components/src/stories/CodeSyntaxHighlighter.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Meta, StoryObj } from "@storybook/react";

import { CodeSyntaxHighlighter } from "../lib/CodeSyntaxHighlighter.js";

/**
* The number shower is a simple component to display a number.
* It uses the symbols "K", "M", "B", and "T", to represent thousands, millions, billions, and trillions. Outside of that range, it uses scientific notation.
*/
const meta = {
component: CodeSyntaxHighlighter,
} satisfies Meta<typeof CodeSyntaxHighlighter>;
export default meta;
type Story = StoryObj<typeof meta>;

export const Squiggle: Story = {
name: "Squiggle Code",

args: {
language: "squiggle",
children: `/* This is a comment */
const foo = "bar";
normal(5, 1)
normal({ p5: 4, p95: 10 })
normal({ p10: 5, p95: 9 })
normal({ p25: 5, p75: 9 })
normal({ mean: 5, stdev: 2 })
normal(5 to 10, normal(3, 2))
normal({ mean: uniform(5, 9), stdev: 3 })
`,
},
};

export const JS: Story = {
name: "Javascript",

args: {
language: "javascript",
children: `const meta = {
component: CodeSyntaxHighlighter,
} satisfies Meta<typeof CodeSyntaxHighlighter>;
export default meta;
type Story = StoryObj<typeof meta>;
`,
},
};
2 changes: 1 addition & 1 deletion packages/hub/src/app/FrontPageModelList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Fragment = graphql`
fragment FrontPageModelList on Query
@argumentDefinitions(
cursor: { type: "String" }
count: { type: "Int", defaultValue: 20 }
count: { type: "Int", defaultValue: 8 }
)
@refetchable(queryName: "FrontPageModelListPaginationQuery") {
models(first: $count, after: $cursor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useParams, usePathname } from "next/navigation";
import { FC } from "react";

import { CodeBracketIcon, EmptyIcon, ScaleIcon, ShareIcon } from "@quri/ui";
import { CodeBracketSquareIcon, EmptyIcon, ShareIcon } from "@quri/ui";

import { EntityInfo } from "@/components/EntityInfo";
import { type EntityNode } from "@/components/EntityLayout";
Expand Down Expand Up @@ -48,7 +48,7 @@ function entityNodes(
{
slug,
href: modelRoute({ owner: owner.slug, slug }),
icon: CodeBracketIcon,
icon: CodeBracketSquareIcon,
},
];

Expand All @@ -60,7 +60,6 @@ function entityNodes(
slug,
variableName: variable.name,
}),
icon: ScaleIcon,
});
}

Expand Down
8 changes: 6 additions & 2 deletions packages/hub/src/app/models/[owner]/[slug]/ModelLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { FC, PropsWithChildren } from "react";
import { graphql } from "relay-runtime";

import { CodeBracketIcon, RectangleStackIcon, ShareIcon } from "@quri/ui";
import { CodeBracketSquareIcon, RectangleStackIcon, ShareIcon } from "@quri/ui";

import { EntityLayout } from "@/components/EntityLayout";
import { EntityTab } from "@/components/ui/EntityTab";
Expand Down Expand Up @@ -120,7 +120,11 @@ export const ModelLayout: FC<
headerLeft={<ModelAccessControls modelRef={model} />}
headerRight={
<EntityTab.List>
<EntityTab.Link name="Code" icon={CodeBracketIcon} href={modelUrl} />
<EntityTab.Link
name="Code"
icon={CodeBracketSquareIcon}
href={modelUrl}
/>
{Boolean(_totalImportLength) && (
<VariablesDropdown
variableRevisions={variableRevisions}
Expand Down
Loading

0 comments on commit fb7c2c1

Please sign in to comment.