This is a feature branch containing the progress for the website

URL: https://tspwebsitepr.z22.web.core.windows.net/prs/2418/
This commit is contained in:
Timothee Guerin 2024-01-19 09:42:25 -08:00 коммит произвёл GitHub
Родитель e0b77ab128
Коммит 5af94f0762
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
176 изменённых файлов: 3797 добавлений и 175 удалений

Просмотреть файл

@ -2,6 +2,7 @@
**/dist-dev/
**/test/output/
**/bin/
**/out/
**/obj/
**/.vs/
**/.docusaurus/
@ -48,3 +49,7 @@ packages/compiler/templates/__snapshots__/
#.tsp init template
eng/feeds/
# Skip formatting tsp files
*.noformat.tsp

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/playground",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/playground"
}

14
common/config/rush/pnpm-lock.yaml сгенерированный
Просмотреть файл

@ -670,7 +670,7 @@ importers:
specifier: ~9.42.0
version: 9.42.0(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.20.2)
'@fluentui/react-icons':
specifier: ~2.0.217
specifier: ^2.0.221
version: 2.0.224(react@18.2.0)
'@typespec/bundler':
specifier: workspace:~0.1.0-alpha.4
@ -700,7 +700,7 @@ importers:
specifier: workspace:~0.51.0
version: link:../versioning
clsx:
specifier: ~2.0.0
specifier: ^2.0.0
version: 2.0.0
debounce:
specifier: ~2.0.0
@ -1287,6 +1287,9 @@ importers:
'@docusaurus/preset-classic':
specifier: ^3.0.0
version: 3.0.1(@algolia/client-search@4.22.0)(@swc/core@1.3.101)(@types/react@18.2.45)(eslint@8.56.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.3.3)
'@docusaurus/theme-classic':
specifier: ~3.0.0
version: 3.0.1(@swc/core@1.3.101)(@types/react@18.2.45)(eslint@8.56.0)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
'@docusaurus/theme-common':
specifier: ~3.0.0
version: 3.0.1(@docusaurus/types@3.0.1)(@swc/core@1.3.101)(eslint@8.56.0)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
@ -1296,12 +1299,18 @@ importers:
'@fluentui/react-components':
specifier: ~9.42.0
version: 9.42.0(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.20.2)
'@fluentui/react-icons':
specifier: ^2.0.221
version: 2.0.224(react@18.2.0)
'@mdx-js/react':
specifier: ^3.0.0
version: 3.0.0(@types/react@18.2.45)(react@18.2.0)
'@typespec/playground':
specifier: workspace:~0.1.0-alpha.4
version: link:../playground
clsx:
specifier: ^2.0.0
version: 2.0.0
es-module-shims:
specifier: ~1.8.0
version: 1.8.2
@ -6672,6 +6681,7 @@ packages:
/@swagger-api/apidom-ns-json-schema-draft-4@0.89.0:
resolution: {integrity: sha512-7gXy3BPLkS7p7dmz9Hbf7ia4lH0NAaW2i7GcQdpX48pAUTR0/7Y+BPd38sgRxIOpebReWxnoAcKAfkak/KCQ3A==}
requiresBuild: true
dependencies:
'@babel/runtime-corejs3': 7.23.6
'@swagger-api/apidom-ast': 0.89.0

Просмотреть файл

@ -40,6 +40,7 @@ words:
- multis
- munge
- mylib
- noformat
- noopener
- noreferrer
- nostdlib
@ -80,6 +81,7 @@ words:
- vsix
- vswhere
- westus
- xlarge
- xplat
ignorePaths:
- "**/node_modules/**"

Просмотреть файл

@ -1,6 +1,6 @@
---
id: usage
title: Usage
id: cli
title: Cli usage
---
# Usage

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,16 +0,0 @@
---
id: introduction
title: Introduction
slug: /
---
# Introduction to TypeSpec
TypeSpec is a language for describing cloud service APIs and generating other API description languages, client and service code, documentation, and other assets. TypeSpec provides highly extensible core language primitives that can describe API shapes common among REST, GraphQL, gRPC, and other protocols.
## Try TypeSpec
You can try TypeSpec on the web without installing anything.
- [TypeSpec playground](https://cadlplayground.z22.web.core.windows.net)
- [TypeSpec playground for Azure services](https://azure.github.io/typespec-azure/playground)

Просмотреть файл

@ -30,7 +30,7 @@ scalar uuid extends string;
## Emitter options interpolation and standardization
Emitter options are not able to interpolate other values. See [details](../introduction/configuration/configuration.md#variable-interpolation)
Emitter options are not able to interpolate other values. See [details](../handbook/configuration/configuration.md#variable-interpolation)
```yaml
output-dir: {cwd}/generated

Просмотреть файл

@ -70,7 +70,7 @@
],
"dependencies": {
"@fluentui/react-components": "~9.42.0",
"@fluentui/react-icons": "~2.0.217",
"@fluentui/react-icons": "^2.0.221",
"@typespec/bundler": "workspace:~0.1.0-alpha.4",
"@typespec/compiler": "workspace:~0.51.0",
"@typespec/html-program-viewer": "workspace:~0.51.0",
@ -88,7 +88,7 @@
"swagger-ui-react": "~5.10.3",
"vscode-languageserver-textdocument": "~1.0.8",
"vscode-languageserver": "~9.0.0",
"clsx": "~2.0.0"
"clsx": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.22.20",

Просмотреть файл

@ -1,5 +1,5 @@
export { createBrowserHost } from "./browser-host.js";
export { registerMonacoDefaultWorkersForVite } from "./monaco-worker.js";
export { registerMonacoLanguage } from "./services.js";
export { StateStorage, createUrlStateStorage } from "./state-storage.js";
export { StateStorage, UrlStateStorage, createUrlStateStorage } from "./state-storage.js";
export type { BrowserHost, PlaygroundSample } from "./types.js";

Просмотреть файл

@ -14,6 +14,7 @@ export type { PlaygroundProps, PlaygroundSaveData } from "./playground.js";
export {
StandalonePlayground,
createReactPlayground,
createStandalonePlaygroundStateStorage,
renderReactPlayground,
} from "./standalone.js";
export type * from "./types.js";

Просмотреть файл

@ -20,7 +20,7 @@ import { createRoot } from "react-dom/client";
import { createBrowserHost } from "../browser-host.js";
import { LibraryImportOptions } from "../core.js";
import { registerMonacoLanguage } from "../services.js";
import { StateStorage, createUrlStateStorage } from "../state-storage.js";
import { StateStorage, UrlStateStorage, createUrlStateStorage } from "../state-storage.js";
import { BrowserHost } from "../types.js";
import { Playground, PlaygroundProps, PlaygroundSaveData } from "./playground.js";
@ -122,7 +122,7 @@ export async function renderReactPlayground(config: ReactPlaygroundConfig) {
);
}
export function createStandalonePlaygroundStateStorage(): StateStorage<PlaygroundSaveData> {
export function createStandalonePlaygroundStateStorage(): UrlStateStorage<PlaygroundSaveData> {
const stateStorage = createUrlStateStorage<PlaygroundSaveData>({
content: {
queryParam: "c",
@ -142,6 +142,7 @@ export function createStandalonePlaygroundStateStorage(): StateStorage<Playgroun
return {
load: stateStorage.load,
resolveSearchParams: stateStorage.resolveSearchParams,
save(data: PlaygroundSaveData) {
stateStorage.save(
data.sampleName ? { sampleName: data.sampleName, options: data.options } : data

Просмотреть файл

@ -5,6 +5,10 @@ export interface StateStorage<T extends object> {
save(t: Partial<T>): void;
}
export interface UrlStateStorage<T extends object> extends StateStorage<T> {
resolveSearchParams(t: Partial<T>): URLSearchParams;
}
export type UrlStorageSchema<T> = {
[key in keyof T]: UrlStorageItem;
};
@ -26,8 +30,8 @@ export interface UrlStorageItem {
*/
export function createUrlStateStorage<const T extends object>(
schema: UrlStorageSchema<T>
): StateStorage<T> {
return { load, save };
): UrlStateStorage<T> {
return { load, save, resolveSearchParams };
function load(): Partial<T> {
const result: Record<string, string> = {};
@ -78,7 +82,12 @@ export function createUrlStateStorage<const T extends object>(
}
function save(data: T) {
const params = new URLSearchParams(location.search);
const params = resolveSearchParams(data, true);
history.pushState(null, "", window.location.pathname + "?" + params.toString());
}
function resolveSearchParams(data: T, mergeWithExisting = false): URLSearchParams {
const params = new URLSearchParams(mergeWithExisting ? location.search : undefined);
for (const [key, item] of Object.entries<UrlStorageItem>(schema)) {
const value = (data as any)[key];
@ -90,7 +99,7 @@ export function createUrlStateStorage<const T extends object>(
params.delete(item.queryParam);
}
}
history.pushState(null, "", window.location.pathname + "?" + params.toString());
return params;
}
function compress(item: UrlStorageItem, value: string): string {

6
packages/website/definitions/index.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
declare module "*.png";
declare module "*.json";
declare module "!!raw-loader!@site/static/*" {
const contents: string;
export = contents;
}

Просмотреть файл

@ -97,7 +97,6 @@ const config: Config = {
/** @type {import('@docusaurus/preset-classic').Options} */
{
docs: {
routeBasePath: "/",
sidebarPath: require.resolve("./sidebars.js"),
path: "../../docs",
versions: getVersionLabels(),
@ -113,7 +112,7 @@ const config: Config = {
],
],
staticDirectories: [
resolve(__dirname, "./node_modules/@typespec/spec/dist"),
resolve(__dirname, "static"),
resolve(__dirname, "./node_modules/es-module-shims/dist"),
],
@ -182,18 +181,39 @@ const config: Config = {
navbar: {
title: "TypeSpec",
items: [
{
type: "dropdown",
label: "Use cases",
items: [
{
label: "OpenAPI",
to: "/openapi",
},
{
label: "Data validation and type consistency",
to: "/data-validation",
},
{
label: "Tooling support",
to: "/tooling",
},
],
},
{
type: "doc",
docId: "introduction/introduction",
docId: "introduction/installation",
position: "left",
label: "Docs",
},
{
to: "/specification",
position: "left",
label: "Specification",
},
{ to: "/playground", label: "Playground", position: "left" },
{
label: "Community",
to: "/community",
},
{
label: "Getting started",
to: "/docs/introduction/installation",
},
{
type: "docsVersionDropdown",
position: "right",
@ -208,40 +228,27 @@ const config: Config = {
},
footer: {
style: "dark",
// links: [
// {
// title: "Docs",
// items: [
// {
// label: "Introduction",
// to: "/",
// },
// {
// label: "Language basics",
// to: "/language-basics/overview",
// },
// ],
// },
// {
// title: "Community & Support",
// items: [
// {
// label: "Stack Overflow",
// href: "https://stackoverflow.microsoft.com/search?q=typespec",
// },
// {
// label: "Microsoft Teams Channel",
// href: "http://aka.ms/typespec/discussions",
// },
// ],
// },
// ],
copyright: `Copyright © ${new Date().getFullYear()} Microsoft Corp.`,
links: [
{
title: "Docs",
items: [
{
label: "Introduction",
to: "/docs",
},
{
label: "Language basics",
to: "/docs/language-basics/overview",
},
],
},
],
copyright: `© ${new Date().getFullYear()} Microsoft`,
},
prism: {
theme: themes.oneLight,
darkTheme: themes.oneDark,
additionalLanguages: ["http"],
additionalLanguages: ["http", "shell-session", "protobuf"],
},
mermaid: {},
algolia: {

Просмотреть файл

@ -25,18 +25,21 @@
"license": "ISC",
"dependencies": {
"@docusaurus/core": "^3.0.0",
"@docusaurus/preset-classic": "^3.0.0",
"@docusaurus/theme-mermaid": "^3.0.0",
"@docusaurus/plugin-content-docs": "~3.0.0",
"@mdx-js/react": "^3.0.0",
"prism-react-renderer": "^2.1.0",
"react": "~18.2.0",
"react-dom": "~18.2.0",
"@typespec/playground": "workspace:~0.1.0-alpha.4",
"@fluentui/react-components": "~9.42.0",
"es-module-shims": "~1.8.0",
"@docusaurus/preset-classic": "^3.0.0",
"@docusaurus/theme-classic": "~3.0.0",
"@docusaurus/theme-common": "~3.0.0",
"prismjs": "~1.29.0"
"@docusaurus/theme-mermaid": "^3.0.0",
"@fluentui/react-components": "~9.42.0",
"@fluentui/react-icons": "^2.0.221",
"@mdx-js/react": "^3.0.0",
"@typespec/playground": "workspace:~0.1.0-alpha.4",
"clsx": "^2.0.0",
"es-module-shims": "~1.8.0",
"prism-react-renderer": "^2.1.0",
"prismjs": "~1.29.0",
"react-dom": "~18.2.0",
"react": "~18.2.0"
},
"devDependencies": {
"@swc/core": "^1.3.62",

Просмотреть файл

@ -39,26 +39,14 @@ const sidebars: SidebarsConfig = {
docsSidebar: [
{
type: "category",
label: "Introduction",
label: "Getting started",
items: [
"introduction/introduction",
"introduction/installation",
{
type: "category",
label: "Editor",
items: ["introduction/editor/vscode", "introduction/editor/vs"],
},
"introduction/usage",
"introduction/style-guide",
"introduction/formatter",
"introduction/reproducibility",
{
type: "category",
label: "Configuration",
items: ["introduction/configuration/configuration", "introduction/configuration/tracing"],
},
"introduction/releases",
"introduction/faq",
],
},
{
@ -70,6 +58,23 @@ const sidebars: SidebarsConfig = {
"getting-started/typespec-for-openapi-dev",
],
},
{
type: "category",
label: "Handbook",
items: [
"handbook/cli",
"handbook/style-guide",
"handbook/formatter",
"handbook/reproducibility",
{
type: "category",
label: "Configuration",
items: ["handbook/configuration/configuration", "handbook/configuration/tracing"],
},
"handbook/releases",
"handbook/faq",
],
},
{
type: "category",
label: "📐 Language Basics",

Просмотреть файл

@ -0,0 +1,3 @@
.img {
display: block;
}

Просмотреть файл

@ -0,0 +1,15 @@
import useBaseUrl from "@docusaurus/useBaseUrl";
import style from "./asset-img.module.css";
export interface AssetImgProps {
src: string;
className?: string;
}
/**
* Component for rendering an image resolving the relative path.
*/
export const AssetImg = ({ src, ...props }: AssetImgProps) => {
const fullSrc = useBaseUrl(`/img/${src}`);
return <img className={style["img"]} src={fullSrc} {...props} />;
};

Просмотреть файл

@ -0,0 +1,47 @@
.button {
align-items: center;
box-sizing: border-box;
display: inline-flex;
justify-content: center;
text-decoration-line: none;
vertical-align: middle;
margin: 0px;
overflow: hidden;
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
font-family: var(--fontFamilyBase);
outline-style: none;
padding: 5px var(--spacingHorizontalM);
min-width: 96px;
border-radius: var(--borderRadiusMedium);
font-size: var(--fontSizeBase300);
font-weight: var(--fontWeightSemibold);
line-height: var(--lineHeightBase300);
transition-duration: var(--durationFaster);
transition-property: background, border, color;
transition-timing-function: var(--curveEasyEase);
}
.button:hover {
color: var(--colorNeutralForeground1);
}
.appearance-primary {
color: var(--colorNeutralForegroundOnBrand);
background-color: var(--colorBrandBackground);
border-color: transparent;
}
.appearance-primary:hover {
color: var(--colorNeutralForegroundOnBrand);
}
.appearance-outline {
background-color: transparent;
}
.appearance-outline:hover {
border-color: var(--colorNeutralStroke1Hover);
text-decoration: none;
}

Просмотреть файл

@ -0,0 +1,21 @@
import clsx from "clsx";
import { ReactNode } from "react";
import { Link } from "../link/link";
import style from "./button.module.css";
export interface ButtonProps {
appearance?: "primary" | "outline";
as?: "a";
href: string;
children?: ReactNode;
className?: string;
}
export const Button = ({ href, children, className, appearance }: ButtonProps) => {
const appearanceCls = style[`appearance-${appearance ?? "primary"}`];
return (
<Link className={clsx(style["button"], appearanceCls, className)} href={href}>
{children}
</Link>
);
};

Просмотреть файл

@ -0,0 +1,26 @@
.card {
border-radius: 22px;
position: relative;
}
.bg {
position: absolute;
width: 100%;
height: 100%;
border-radius: 22px;
background-color: var(--colorNeutralBackground1);
}
.content {
position: relative;
z-index: 100;
padding: 20px;
}
.no-padding .content {
padding: 0px;
}
.blend > .bg {
opacity: 0.2;
}

Просмотреть файл

@ -0,0 +1,28 @@
import clsx from "clsx";
import style from "./card.module.css";
export interface CardProps {
/** Should skip padding. */
noPadding?: boolean;
/** Should the background blend. */
blend?: boolean;
children?: React.ReactNode;
className?: string;
}
export const Card = ({ children, className, noPadding, blend }: CardProps) => {
return (
<div
className={clsx(
style["card"],
noPadding && style["no-padding"],
blend && style["blend"],
className
)}
>
<div className={style["bg"]}></div>
<div className={style["content"]}>{children}</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,3 @@
.code-block {
margin: 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
import CodeBlockDocusaurus, { Props } from "@theme/CodeBlock";
import clsx from "clsx";
import style from "./code-block.module.css";
export interface CodeBlockProps extends Props {}
export const CodeBlock = (props: CodeBlockProps) => {
return <CodeBlockDocusaurus {...props} className={clsx(style["code-block"], props.className)} />;
};

Просмотреть файл

@ -0,0 +1,23 @@
.items-list {
width: 100%;
display: flex;
flex-direction: column;
gap: 40px;
}
.item {
display: flex;
gap: 12px;
}
.item-image {
width: 64px;
height: 64px;
max-width: 64px;
}
.item-content {
display: flex;
flex-direction: column;
gap: 12px;
}

Просмотреть файл

@ -0,0 +1,44 @@
import clsx from "clsx";
import { FluentImageName, FluentImg } from "../fluent-img";
import { Link } from "../link/link";
import { DescriptionText, NeutralText, Text } from "../text/text";
import style from "./feature-list.module.css";
interface FeatureListItem {
title: string;
description?: string;
image?: FluentImageName;
link?: string;
}
export interface FeatureListProps {
items?: FeatureListItem[];
}
export const FeatureList = ({ items }: FeatureListProps) => {
if (items === undefined) {
return null;
}
const content = items.map((x, i) => <FeatureListItemEl key={i} {...x} />);
return <div className={clsx(style["items-list"])}>{content}</div>;
};
interface FeatureListItemElProps extends FeatureListItem {}
const FeatureListItemEl = ({ title, description, image, link }: FeatureListItemElProps) => {
return (
<div className={style["item"]}>
{image && <FluentImg className={style["item-image"]} name={image} />}
<div className={style["item-content"]}>
<NeutralText size={"large"}>{title}</NeutralText>
<DescriptionText>{description}</DescriptionText>
{link && (
<Link href={link}>
<Text>Learn more </Text>
</Link>
)}
</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,36 @@
import { useColorMode } from "@docusaurus/theme-common";
import useBaseUrl from "@docusaurus/useBaseUrl";
export interface FluentImgProps {
name: FluentImageName;
className?: string;
}
export type FluentImageName =
| "book-pencil"
| "chat"
| "checkmark"
| "data-trending"
| "design-layout"
| "design"
| "devices-multiple"
| "document-add"
| "document-cloud"
| "editor"
| "eye-dev"
| "firework"
| "people-shield"
| "shield-blue"
| "shield-settings"
| "tasks"
| "text-edit";
/**
* Component for rendering a Fluent image.
*/
export const FluentImg = ({ name, ...props }: FluentImgProps) => {
const { colorMode } = useColorMode();
const colorKey = colorMode === "dark" ? "d" : "l";
const src = useBaseUrl(`/img/fluent/${name}-${colorKey}-standard-128x128.png`);
return <img src={src} {...props} />;
};

Просмотреть файл

@ -0,0 +1,3 @@
.illustration-card {
padding: 0;
}

Просмотреть файл

@ -0,0 +1,36 @@
import { SelectTabData, SelectTabEvent, Tab, TabList } from "@fluentui/react-components";
import { ReactNode, useCallback, useState } from "react";
import style from "./hero-tabs.module.css";
export interface HeroProps {
tabs: HeroTab[];
}
interface HeroTab {
value: string;
content: ReactNode;
}
export const HeroTabs = ({ tabs }: HeroProps) => {
const [selected, setSelected] = useState<string>(tabs[0].value);
const handleTabSelection = useCallback(
(event: SelectTabEvent, data: SelectTabData) => {
setSelected(data.value as any);
},
[setSelected]
);
const content = tabs.find((tab) => tab.value === selected)?.content;
return (
<>
<div className={style["illustration-card"]}>{content}</div>
<TabList selectedValue={selected} onTabSelect={handleTabSelection}>
{tabs.map((tab) => {
return (
<Tab key={tab.value} value={tab.value}>
{tab.value}
</Tab>
);
})}
</TabList>
</>
);
};

Просмотреть файл

@ -0,0 +1,26 @@
.hero-illustration {
width: 1200px;
}
.split-windows {
height: 700px;
display: flex;
flex-direction: row;
align-items: stretch;
gap: 2px;
}
.split-window {
flex: 1;
min-height: 100%;
overflow-y: auto;
}
.output {
opacity: 90%;
font-size: 90%;
}
.hero-illustration :global(.theme-code-block) {
border-radius: 0;
}

Просмотреть файл

@ -0,0 +1,85 @@
import openapiTsp from "!!raw-loader!@site/static/tsp-samples/openapi3/hero/main.tsp";
import openapiYaml from "!!raw-loader!@site/static/tsp-samples/openapi3/hero/out/openapi.yaml";
import jsonSchemaTsp from "!!raw-loader!@site/static/tsp-samples/json-schema/hero/main.tsp";
import jsonSchemaOutput from "!!raw-loader!@site/static/tsp-samples/json-schema/hero/out/schema.yaml";
import protobufTsp from "!!raw-loader!@site/static/tsp-samples/protobuf/hero/main.tsp";
import protobufOutput from "!!raw-loader!@site/static/tsp-samples/protobuf/hero/out/addressbook.proto";
import CodeBlock from "@theme/CodeBlock";
import { HeroTabs } from "../../hero-tabs/hero-tabs";
import { Window } from "../../window/window";
import clsx from "clsx";
import style from "./hero-illustration.module.css";
export const HeroIllustration = () => {
return (
<HeroTabs
tabs={[
{
value: "Http",
content: <OpenAPI3Illustration />,
},
{ value: "Json Schema", content: <JsonSchemaIllustration /> },
{ value: "Protobuf", content: <ProtobufIllustration /> },
]}
></HeroTabs>
);
};
const OpenAPI3Illustration = () => {
return (
<Window className={style["hero-illustration"]}>
<div className={style["split-windows"]}>
<CodeBlock className={style["split-window"]} language="tsp" title="main.tsp">
{openapiTsp}
</CodeBlock>
<CodeBlock
className={clsx(style["split-window"], style["output"])}
language="yaml"
title="openapi.yaml"
>
{openapiYaml}
</CodeBlock>
</div>
</Window>
);
};
const JsonSchemaIllustration = () => {
return (
<Window className={style["hero-illustration"]}>
<div className={style["split-windows"]}>
<CodeBlock className={style["split-window"]} language="tsp" title="main.tsp">
{jsonSchemaTsp}
</CodeBlock>
<CodeBlock
className={clsx(style["split-window"], style["output"])}
language="yaml"
title="schema.yaml"
>
{jsonSchemaOutput}
</CodeBlock>
</div>
</Window>
);
};
const ProtobufIllustration = () => {
return (
<Window className={style["hero-illustration"]}>
<div className={style["split-windows"]}>
<CodeBlock className={style["split-window"]} language="tsp" title="main.tsp">
{protobufTsp}
</CodeBlock>
<CodeBlock
className={clsx(style["split-window"], style["output"])}
language="protobuf"
title="addressbook.proto"
>
{protobufOutput}
</CodeBlock>
</div>
</Window>
);
};

Просмотреть файл

@ -0,0 +1,197 @@
.hero-divider {
height: 2px;
align-self: stretch;
background: radial-gradient(circle, var(--colorNeutralStroke2) 0%, transparent 80%);
}
.hero-container {
background-repeat: no-repeat;
width: 100%;
background-size: 100vw 94%;
display: flex;
padding: 120px 80px 52px 80px;
flex-direction: column;
gap: 56px;
align-self: stretch;
}
[data-theme="light"] .hero-container {
background: radial-gradient(
46.56% 45.08% at 56.04% 55.33%,
rgba(0, 50, 255, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(
46.69% 41.74% at 69.64% 60.81%,
rgba(192, 59, 196, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(
59.78% 45.73% at 30.42% 58.68%,
rgba(0, 120, 212, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(32.53% 31.57% at 50% 66.82%, rgba(70, 54, 104, 0.1) 0%, rgba(0, 0, 0, 0) 100%);
}
[data-theme="dark"] .hero-container {
background: radial-gradient(
46.56% 45.08% at 56.04% 55.33%,
rgba(0, 50, 255, 0.2) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(
46.69% 41.74% at 69.64% 60.81%,
rgba(192, 59, 196, 0.2) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(
59.78% 45.73% at 30.42% 58.68%,
rgba(0, 120, 212, 0.2) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(32.53% 31.57% at 50% 66.82%, rgba(70, 54, 104, 0.2) 0%, rgba(0, 0, 0, 0) 100%);
}
.hero-title {
text-align: center;
font-size: 24px;
font-style: normal;
font-weight: 600;
line-height: 100%; /* 24px */
letter-spacing: -0.24px;
}
.hero-subtitle {
text-align: center;
font-size: 96px;
font-style: normal;
font-weight: 600;
line-height: 100%; /* 96px */
letter-spacing: -1px;
color: transparent;
background: linear-gradient(
127deg,
#6dc2b1 -17.91%,
#9ac6e5 19.34%,
#3579d8 56.65%,
#b145bd 96.75%
);
background-clip: text;
}
@media only screen and (max-width: 1024px) {
.hero-subtitle {
font-size: 68px;
}
}
@media only screen and (max-width: 728px) {
.hero-subtitle {
font-size: 48px;
}
}
.hero-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 32px;
align-self: stretch;
}
.hero-description,
.overview-description {
max-width: 600px;
padding: 20px;
text-align: center;
}
.hero-buttons {
display: flex;
justify-content: center;
gap: 8px;
align-self: stretch;
flex-wrap: wrap;
}
.hero-demo {
display: flex;
padding: 0px 102px;
flex-direction: column;
align-items: center;
gap: 10px;
flex: 1 0 0;
align-self: stretch;
}
@media only screen and (max-width: 728px) {
.hero-demo {
display: none;
}
}
.overview {
display: flex;
flex-direction: column;
gap: 128px;
width: 100%;
max-width: 100%;
padding: 60px;
}
@media only screen and (max-width: 728px) {
.overview {
padding: 20px;
}
}
.overview-subtitle {
text-align: center;
font-size: 40px;
font-weight: 600;
line-height: 48px;
}
.overview-summary {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
align-self: stretch;
}
.closing {
display: flex;
padding: 80px 60px;
flex-direction: column;
gap: 16px;
align-items: flex-start;
align-self: baseline;
}
.closing-title {
font-size: 40px;
font-weight: 600;
line-height: 48px;
}
@media only screen and (max-width: 728px) {
.closing-title,
.overview-subtitle {
font-size: 32px;
line-height: 40px;
}
}
.closing-buttons {
display: flex;
gap: 8px;
}
.codeblock-seperator {
height: 10px;
}

Просмотреть файл

@ -0,0 +1,207 @@
import useBaseUrl from "@docusaurus/useBaseUrl";
import { Links } from "@site/src/constants";
import { DataValidationHeroIllustration } from "@site/src/pages/data-validation";
import { OpenAPI3HeroIllustration } from "@site/src/pages/openapi";
import { Button } from "../button/button";
import { CodeBlock } from "../code-block/code-block";
import { FeatureList } from "../feature-list/feature-list";
import { LearnMoreCard } from "../learn-more-card/learn-more-card";
import { LightDarkImg } from "../light-dark-img/light-dark-img";
import { Section } from "../section/section";
import { SectionedLayout } from "../sectioned-layout/sectioned-layout";
import { DescriptionText, PrimaryText } from "../text/text";
import style from "./homepage.module.css";
import { OverviewIllustration } from "./overview-illustration/overview-illustration";
export const HomeContent = () => {
return (
<>
<Hero />
<div className={style["hero-divider"]}></div>
<SectionedLayout>
<Overview />
<OpenAPISection />
<DataValidationSection />
<EditorSection />
<ExtensibilitySection />
<Closing />
</SectionedLayout>
</>
);
};
const Hero = () => {
return (
<>
<div className={style["hero-container"]}>
<div className={style["hero-content"]}>
<h1 className={style["hero-title"]}>TypeSpec</h1>
<div className={style["hero-subtitle"]}>Describe APIs</div>
<DescriptionText size="large" className={style["hero-description"]}>
Describe your data up front and generate schemas, API specifications, client / server
code, docs, and more.
</DescriptionText>
<div className={style["hero-buttons"]}>
<Button as="a" appearance="primary" href={useBaseUrl(Links.docs)}>
Get Started
</Button>
<Button as="a" appearance="outline" href={useBaseUrl(Links.playground)}>
Try it out
</Button>
</div>
</div>
<div className={style["hero-demo"]}>
<HeroIllustration />
</div>
</div>
</>
);
};
const Overview = () => {
return (
<>
<div className={style["overview"]}>
<div className={style["overview-summary"]}>
<PrimaryText>Why TypeSpec</PrimaryText>
<div className={style["overview-subtitle"]}>API-First for developers</div>
<DescriptionText size="large" className={style["overview-description"]}>
With TypeSpec, remove the handwritten files that slow you down, and generate
standards-compliant API schemas in seconds.
</DescriptionText>
</div>
<Section layout="text-right" illustration={<OverviewIllustration />} itemStyle="plain">
<FeatureList
items={[
{
title: "Lightweight language for defining APIs",
description:
"Inspired by TypeScript, TypeSpec is a minimal language that helps developers describe API shapes in a familiar way.",
image: "book-pencil",
link: Links.gettingStartedOpenAPI,
},
{
title: "Easy integration with your toolchain",
description:
"Write TypeSpec, emit to various formats and integrate with their ecosystems.",
image: "document-add",
},
{
title: "Multi-protocol support",
description:
"TypeSpec's standard library includes support for OpenAPI 3.0, JSON Schema 2020-12, Protobuf, and JSON RPC.",
image: "tasks",
link: "/multi-protocol",
},
]}
/>
</Section>
</div>
</>
);
};
const OpenAPISection = () => {
return (
<Section
header="Productivity"
title="Streamline your OpenAPI workflow"
description="Benefit from a huge ecosystem of OpenAPI tools for configuring API gateways, generating code, and validating your data."
illustration={<OpenAPI3HeroIllustration />}
>
<LearnMoreCard
title="Generate OpenAPI from TypeSpec"
image="design"
link={Links.useCases.openapi}
/>
</Section>
);
};
const DataValidationSection = () => {
return (
<Section
header="Ecosystem"
title="Ensure data consistency"
description="Defined common models to use across your APIs, use the json schema emitter to get the json schema for your types and use them to validate your data."
illustration={<DataValidationHeroIllustration />}
>
<LearnMoreCard
title="Json schema emitter reference"
image="people-shield"
link={Links.useCases.dataValidation}
/>
</Section>
);
};
const EditorSection = () => {
return (
<Section
header="Tooling"
title="First party support for code editor"
description="Typespec provide built-in support for many common editor features such as syntax highlighting, code completion, and more."
illustration={<LightDarkImg src="illustrations/ide-hero" />}
>
<LearnMoreCard
title="Check out our available tooling"
image="data-trending"
link={Links.useCases.tooling}
/>
</Section>
);
};
const ExtensibilitySection = () => {
return (
<Section
header="Extensibility"
title="Generate assets in many formats"
description="Typespec is built around extensibility, one can write and plugin their own emitter or add custom metadata using a new decorator."
illustration={<ExtensibilityIllustration />}
>
<LearnMoreCard
title="Getting started with writing a library"
image="data-trending"
link={Links.extensibility.gettingStarted}
/>
</Section>
);
};
import extensibilityTs from "!!raw-loader!@site/static/tsp-samples/extensibility/custom-lib.ts";
import extensibilityTsp from "!!raw-loader!@site/static/tsp-samples/extensibility/custom-lib.tsp";
import { HeroIllustration } from "./hero-illustration/hero-illustration";
const ExtensibilityIllustration = () => {
return (
<div>
<CodeBlock language="tsp" title="lib.tsp">
{extensibilityTsp}
</CodeBlock>
<div className={style["codeblock-seperator"]}></div>
<CodeBlock language="ts" title="lib.ts">
{extensibilityTs}
</CodeBlock>
</div>
);
};
const Closing = () => {
return (
<div className={style["closing"]}>
<div className={style["closing-title"]}>Start your TypeSpec journey</div>
<DescriptionText>
Install the TypeSpec CLI or check out the playground to get started.
</DescriptionText>
<div className={style["closing-buttons"]}>
<Button as="a" appearance="primary" href={useBaseUrl(Links.docs)}>
Get Started
</Button>
<Button as="a" appearance="outline" href={useBaseUrl(Links.playground)}>
Try it out
</Button>
</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,19 @@
.card {
width: 723px;
height: 542px;
position: relative;
}
.terminal {
position: absolute;
top: 60px;
left: 50px;
width: 420px;
height: 320px;
}
.ide {
position: absolute;
top: 226px;
left: 183px;
}

Просмотреть файл

@ -0,0 +1,24 @@
import { IllustrationCard } from "../../illustration-card/illustration-card";
import { LightDarkImg } from "../../light-dark-img/light-dark-img";
import { P } from "../../painter/painter";
import { Terminal } from "../../terminal/terminal";
import { Window } from "../../window/window";
import style from "./overview-illustration.module.css";
export const OverviewIllustration = () => {
return (
<IllustrationCard blend className={style["card"]}>
<Terminal className={style["terminal"]}>
{P.line(P.secondary("~ /my-project"), " tsp init")}
{P.line(" ")}
{P.brand("? ")}
{"Select a template"}
{P.line(" Empty project")}
{P.line(P.brand("> "), P.brand.underline("REST API"))}
</Terminal>
<Window className={style["ide"]}>
<LightDarkImg src="illustrations/overview-ide" />
</Window>
</IllustrationCard>
);
};

Просмотреть файл

@ -0,0 +1,11 @@
.illustration :global(.tabs-container) {
margin-bottom: 0;
}
.illustration :global(.tabs-container) :global(.margin-top--md) {
margin-top: 0 !important;
}
.illustration :global(img) {
display: block;
}

Просмотреть файл

@ -0,0 +1,14 @@
import clsx from "clsx";
import { Card, CardProps } from "../card/card";
import style from "./illustration-card.module.css";
export interface IllustrationCardProps extends CardProps {}
export const IllustrationCard = ({ className, noPadding, ...props }: IllustrationCardProps) => {
return (
<Card
className={clsx(className, style["illustration"])}
noPadding={noPadding ?? true}
{...props}
/>
);
};

Просмотреть файл

@ -0,0 +1,19 @@
.illustration-main {
display: grid;
grid-template-areas:
"tsp openapi"
"swagger-ui openapi"
"spectral spectral";
gap: 5px;
background-color: var(--colorNeutralBackground1);
}
.tsp {
grid-area: tsp;
}
.openapi {
grid-area: openapi;
}
.spectral {
grid-area: spectral;
}

Просмотреть файл

@ -0,0 +1,32 @@
import interoperateTsp from "!!raw-loader!@site/static/tsp-samples/openapi3/interoperate/main.tsp";
import interoperateOpenapi from "!!raw-loader!@site/static/tsp-samples/openapi3/interoperate/openapi.yaml";
import interoperateSpectral from "!!raw-loader!@site/static/tsp-samples/openapi3/interoperate/spectral.txt";
import { Painter } from "@site/src/components/painter/painter";
import { Terminal } from "@site/src/components/terminal/terminal";
import { AssetImg } from "../asset-img/asset-img";
import { CodeBlock } from "../code-block/code-block";
import { WindowCarousel, WindowCarouselItem } from "../window-carousel/window-carousel";
import style from "./interoperate-illustration.module.css";
export const OpenAPI3InteroperateIllustration = () => {
return (
<WindowCarousel>
<WindowCarouselItem value="TypeSpec">
<div className={style["illustration-main"]}>
<div className={style["tsp"]}>
<CodeBlock language="tsp">{interoperateTsp}</CodeBlock>
</div>
<div className={style["openapi"]}>
<CodeBlock language="yaml">{interoperateOpenapi}</CodeBlock>
</div>
<Terminal className={style["spectral"]} hideHeader>
<Painter content={interoperateSpectral} />
</Terminal>
</div>
</WindowCarouselItem>
<WindowCarouselItem value="Swagger UI">
<AssetImg className={style["swagger-ui"]} src="illustrations/swagger-ui.png" />
</WindowCarouselItem>
</WindowCarousel>
);
};

Просмотреть файл

@ -1,6 +1,8 @@
import BrowserOnly from "@docusaurus/BrowserOnly";
import { useColorMode } from "@docusaurus/theme-common";
import { FluentProvider, webDarkTheme, webLightTheme } from "@fluentui/react-components";
import Layout from "@theme/Layout";
import style from "./layouts.module.css";
export const FluentLayout = ({ children }) => {
return (
@ -10,6 +12,19 @@ export const FluentLayout = ({ children }) => {
);
};
export const ShowcaseLayout = ({ children }) => {
return (
// Need to do this because fluentui can't do SSR simply...
<BrowserOnly
children={() => (
<FluentLayout>
<div className={style["showcase-layout"]}>{children}</div>
</FluentLayout>
)}
/>
);
};
const FluentWrapper = ({ children }) => {
const { colorMode } = useColorMode();

Просмотреть файл

@ -0,0 +1,3 @@
.showcase-layout {
background-color: var(--colorNeutralBackground3);
}

Просмотреть файл

@ -0,0 +1,32 @@
.item {
width: 100%;
text-decoration: none;
border-radius: 14px;
background-color: var(--colorNeutralBackground1);
padding: 20px;
display: flex;
gap: 12px;
border: 2px solid transparent;
}
.item:hover {
text-decoration: none;
border-color: var(--colorBrandForeground1);
}
.item:active {
text-decoration: none;
background-color: var(--colorNeutralBackground2);
}
.item-image {
width: 52px;
height: 52px;
max-width: 64px;
}
.item-content {
display: flex;
flex-direction: column;
gap: 12px;
}

Просмотреть файл

@ -0,0 +1,24 @@
import { FluentImageName, FluentImg } from "../fluent-img";
import { Link } from "../link/link";
import { DescriptionText, NeutralText, Text } from "../text/text";
import style from "./learn-more-card.module.css";
interface LearnMoreCardProps {
title: string;
description?: string;
image?: FluentImageName;
link: string;
}
export const LearnMoreCard = ({ title, description, image, link }: LearnMoreCardProps) => {
return (
<Link href={link} className={style["item"]}>
{image && <FluentImg className={style["item-image"]} name={image} />}
<div className={style["item-content"]}>
<NeutralText>{title}</NeutralText>
<DescriptionText>{description}</DescriptionText>
<Text>Learn more </Text>
</div>
</Link>
);
};

Просмотреть файл

@ -0,0 +1,12 @@
:global(html[data-theme="light"]) .dark {
display: none;
}
:global(html[data-theme="dark"]) .light {
display: none;
}
.light,
.dark {
display: block;
}

Просмотреть файл

@ -0,0 +1,11 @@
import { AssetImg } from "../asset-img/asset-img";
import style from "./light-dark-img.module.css";
export const LightDarkImg = ({ src }: { src: string }) => {
return (
<div>
<AssetImg className={style["dark"]} src={`${src}.dark.png`} />
<AssetImg className={style["light"]} src={`${src}.light.png`} />
</div>
);
};

Просмотреть файл

@ -0,0 +1,13 @@
import DocusaurusLink from "@docusaurus/Link";
import useBaseUrl from "@docusaurus/useBaseUrl";
import { ComponentProps } from "react";
export interface LinkProps extends ComponentProps<"a"> {}
export const Link = ({ href, children, ...props }: LinkProps) => {
return (
<DocusaurusLink href={useBaseUrl(href)} {...props}>
{children}
</DocusaurusLink>
);
};

Просмотреть файл

@ -0,0 +1,16 @@
.color-brand {
color: var(--colorBrandForeground1);
}
.color-secondary {
color: var(--colorNeutralForeground3);
}
.color-warning {
color: var(--colorStatusWarningForeground1);
}
.color-danger {
color: var(--colorStatusDangerForeground1);
}
.mod-underline {
text-decoration: underline;
}

Просмотреть файл

@ -0,0 +1,102 @@
import clsx from "clsx";
import { Fragment, ReactNode } from "react";
import style from "./painter.module.css";
export interface Painter {
(...content: ReactNode[]): React.JSX.Element;
readonly line: this;
readonly brand: this;
readonly secondary: this;
readonly underline: this;
}
interface PainterOptions {
color?: "brand" | "secondary" | "warning";
line?: boolean;
underline?: boolean;
}
const colors = ["brand", "secondary", "warning"] as const;
const modifiers = ["line", "underline"] as const;
function createPainter(): Painter {
return painterFactory({});
}
function painterFactory(options: PainterOptions): Painter {
const fn = (...children: ReactNode[]) => {
const cls = clsx(
style[`color-${options.color}`],
...modifiers.map((x) => (options[x] ? style[`mod-${x}`] : undefined))
);
const content = (
<span className={cls}>
{children.map((x, i) => (
<Fragment key={i}>{x}</Fragment>
))}
</span>
);
return options.line ? <div>{content}</div> : content;
};
const styles = {};
for (const color of colors) {
styles[color] = {
get() {
return painterFactory({ ...options, color });
},
};
}
for (const modifier of modifiers) {
styles[modifier] = {
get() {
return painterFactory({ ...options, [modifier]: true });
},
};
}
Object.defineProperties(fn, styles);
return fn as any;
}
/**
* Helper to highlight custom code.
*
* @example
* ```tsx
* [
* P.line("Hello ", P.brand("world")),
* P.line(P.secondary.underline("Hello back"))
* ];
* ```
*/
export const P = createPainter();
export interface PainterProps {
content: string;
}
const painterTagRegex = /\[([a-zA-Z.]+)\](.*)\[\/([a-zA-Z.]+)]/g;
export const Painter = ({ content }: PainterProps) => {
const replaced = content.matchAll(painterTagRegex);
const result = [];
let lastIndex = 0;
for (const item of replaced) {
const [fullMatch, openTag, matchContent, closeTag] = item;
result.push(content.substring(lastIndex, item.index));
lastIndex = item.index + fullMatch.length;
if (openTag === closeTag) {
const segments = openTag.split(".");
let p = P;
for (const segment of segments) {
p = p[segment];
}
result.push(p(matchContent));
} else {
result.push(fullMatch);
}
}
result.push(content.substring(lastIndex));
return result;
};

Просмотреть файл

@ -0,0 +1,118 @@
.section {
display: flex;
gap: 10px 60px;
width: 100%;
max-width: 100%;
align-items: center;
align-content: center;
flex-direction: column;
}
@media only screen and (min-width: 1200px) {
.section {
padding: 0 40px;
gap: 3vw;
}
.text-left {
flex-direction: row;
}
.text-right {
flex-direction: row-reverse;
}
}
@media only screen and (min-width: 1500px) {
.section {
gap: 170px;
}
}
.info-container {
display: flex;
max-width: 100%;
padding: 20px;
justify-content: center;
}
.info {
display: flex;
width: 520px;
flex-direction: column;
align-items: flex-start;
gap: 56px;
}
.info-heading {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--spacing-vertical-spacing-xxl, 24px);
align-self: stretch;
}
.info-title {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--vertical-xl, 20px);
align-self: stretch;
}
.info-description {
font-size: 20px;
font-style: normal;
font-weight: 400;
line-height: 28px;
}
.illustration {
flex: 1 0 50%;
max-width: calc(100% - 40px);
overflow: hidden;
}
.illustration :global(.tabs-container) {
margin-bottom: 0;
}
.illustration :global(.tabs-container) :global(.margin-top--md) {
margin-top: 0 !important;
}
.items-list {
width: 100%;
display: flex;
flex-direction: column;
}
.item {
display: flex;
gap: 12px;
}
.items-list-card .item-image {
width: 52px;
height: 52px;
max-width: 52px;
}
.items-list-plain {
gap: 40px;
}
.items-list-card {
gap: 12px;
}
.items-list-plain .item-image {
width: 64px;
height: 64px;
max-width: 64px;
}
.item-content {
display: flex;
flex-direction: column;
gap: 12px;
}

Просмотреть файл

@ -0,0 +1,56 @@
import clsx from "clsx";
import React, { ReactNode } from "react";
import { AssetImg } from "../asset-img/asset-img";
import { DescriptionText, NeutralText, PrimaryText } from "../text/text";
import style from "./section.module.css";
export interface SectionProps {
header?: string;
title?: string;
description?: string;
illustration: string | React.ReactNode;
children?: ReactNode;
itemStyle?: "card" | "plain";
layout?: "text-left" | "text-right";
}
export const Section = ({
header,
title,
description,
children,
layout,
illustration,
itemStyle: itemsCard,
}: SectionProps) => {
const heading =
header || title || description ? (
<div className={style["info-heading"]}>
<div className={style["info-title"]}>
<PrimaryText>{header}</PrimaryText>
<NeutralText size="xlarge">{title}</NeutralText>
</div>
<DescriptionText size="large" className={style["info-description"]}>
{description}
</DescriptionText>
</div>
) : undefined;
return (
<div
className={clsx(
style["section"],
style[layout === "text-right" ? "text-right" : "text-left"]
)}
>
<div className={style["info-container"]}>
<div className={style["info"]}>
{heading}
{children}
</div>
</div>
<div className={style["illustration"]}>
{typeof illustration === "string" ? <AssetImg src={illustration} /> : illustration}
</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,42 @@
.layout {
overflow-x: hidden;
}
[data-theme="light"] .layout {
background: radial-gradient(
154.41% 34.41% at 18.39% 93.33%,
rgba(0, 117, 255, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(
83.89% 52.66% at 92.5% 77.61%,
rgba(192, 59, 196, 0.07) 0%,
rgba(0, 0, 0, 0) 82.67%
),
linear-gradient(328deg, rgba(0, 120, 212, 0.1) -34.57%, rgba(0, 0, 0, 0) 111.7%);
}
[data-theme="dark"] .layout {
background: radial-gradient(
154.41% 34.41% at 18.39% 93.33%,
rgba(0, 117, 255, 0.2) 0%,
rgba(0, 0, 0, 0) 100%
),
radial-gradient(
83.89% 52.66% at 92.5% 77.61%,
rgba(192, 59, 196, 0.14) 0%,
rgba(0, 0, 0, 0) 82.67%
),
linear-gradient(328deg, rgba(0, 120, 212, 0.2) -34.57%, rgba(0, 0, 0, 0) 111.7%);
}
.layout-inner {
display: flex;
padding: 48px 0px 60px 0px;
margin: auto;
max-width: 1464px;
flex-direction: column;
align-items: center;
gap: 128px;
align-self: stretch;
}

Просмотреть файл

@ -0,0 +1,9 @@
import style from "./sectioned-layout.module.css";
export const SectionedLayout = ({ children }) => {
return (
<div className={style["layout"]}>
<div className={style["layout-inner"]}>{children}</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,12 @@
.terminal-code {
background-color: var(--window-color);
margin: 0;
height: 100%;
}
[data-theme="light"] .terminal {
--window-color: var(--colorNeutralBackground2);
}
[data-theme="dark"] .terminal {
--window-color: var(--colorNeutralBackground4);
}

Просмотреть файл

@ -0,0 +1,13 @@
import clsx from "clsx";
import { Window, WindowProps } from "../window/window";
import style from "./terminal.module.css";
export interface TerminalProps extends WindowProps {}
export const Terminal = ({ className, children, ...props }: WindowProps) => {
return (
<Window className={clsx(style["terminal"], className)} {...props}>
<pre className={style["terminal-code"]}>{children}</pre>
</Window>
);
};

Просмотреть файл

@ -0,0 +1,30 @@
.neutral-text {
color: var(--colorNeutralForeground1);
font-weight: 400;
}
.description-text {
color: var(--colorNeutralForeground3);
font-weight: 400;
}
.primary-text {
color: var(--colorBrandForeground1);
font-weight: 400;
}
.text {
line-height: 140%;
}
.text-size-standard {
font-size: 20px;
}
.text-size-large {
font-size: 24px;
}
.text-size-xlarge {
font-size: 28px;
font-weight: 600;
}

Просмотреть файл

@ -0,0 +1,27 @@
import clsx from "clsx";
import { FunctionComponent } from "react";
import style from "./text.module.css";
export interface TextProps {
children: React.ReactNode;
size?: "standard" | "large" | "xlarge";
className?: string;
}
export const Text = ({ children, className, size }: TextProps) => {
return (
<div className={clsx(style["text"], style[`text-size-${size ?? "standard"}`], className)}>
{children}
</div>
);
};
function makeTextComp(clsName: string): FunctionComponent<TextProps> {
return ({ className, ...props }: TextProps) => {
return <Text {...props} className={clsx(style[clsName], className)} />;
};
}
export const NeutralText = makeTextComp("neutral-text");
export const PrimaryText = makeTextComp("primary-text");
export const DescriptionText = makeTextComp("description-text");

Просмотреть файл

@ -0,0 +1,20 @@
.tryit-codeblock-container {
position: relative;
}
.tryit-container {
display: flex;
cursor: pointer;
align-items: center;
justify-content: space-between;
background-color: var(--colorBrandBackground);
padding: 0.4rem;
text-decoration: none;
color: var(--colorNeutralForeground1);
}
.tryit-container:hover {
text-decoration: none;
color: var(--colorNeutralForeground1);
background-color: var(--colorBrandBackgroundHover);
}

Просмотреть файл

@ -0,0 +1,102 @@
import BrowserOnly from "@docusaurus/BrowserOnly";
import useBaseUrl from "@docusaurus/useBaseUrl";
import { ArrowCircleRight16Filled } from "@fluentui/react-icons";
import type CodeBlockDocusaurus from "@theme-original/CodeBlock";
import { Props } from "@theme/CodeBlock";
import type { CompilerOptions } from "@typespec/compiler";
import type { UrlStateStorage } from "@typespec/playground";
import type { PlaygroundSaveData } from "@typespec/playground/react";
import { AnchorHTMLAttributes, useEffect, useMemo, useState } from "react";
import style from "./tsp-tryit.module.css";
export const withTspPlayground = (Component: typeof CodeBlockDocusaurus) => {
function WrappedComponent(props: Props) {
if (props.className === "language-tsp" && props.metastring) {
const config = findTryitConfig(props.metastring);
if (config) {
return (
<div className={style["tryit-codeblock-container"]}>
<PlaygroundTryItLink
content={props.children as string}
compilerOptions={config}
className={style["tryit-container"]}
target="_blank"
rel="noopener noreferrer"
>
<span>Try it</span>
<ArrowCircleRight16Filled />
</PlaygroundTryItLink>
<Component {...props} />
</div>
);
}
}
return <Component {...props} />;
}
return WrappedComponent;
};
const tryItRegex = /tryit(=".*")?/;
function findTryitConfig(metastring: string): CompilerOptions | undefined {
const match = metastring.match(tryItRegex);
if (!match) {
return undefined;
}
const optionsString = match[0].split("tryit=")[1]?.slice(1, -1);
if (!optionsString) {
return {};
}
try {
return JSON.parse(optionsString);
} catch (e) {
throw new Error(`Invalid tryit tsp config: ${optionsString}`);
}
}
export interface CodeBlockWithTryItLink extends Props {
component: typeof CodeBlockDocusaurus;
}
interface PlaygroundTryItLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
content: string;
compilerOptions?: CompilerOptions;
}
const PlaygroundTryItLink = (props: PlaygroundTryItLinkProps) => {
return <BrowserOnly children={() => <PlaygroundTryItLinkInternal {...props} />} />;
};
const PlaygroundTryItLinkInternal = ({
content,
compilerOptions,
...props
}: PlaygroundTryItLinkProps) => {
const [storage, setStorage] = useState<UrlStateStorage<PlaygroundSaveData>>();
useEffect(() => {
import("@typespec/playground/react")
.then(({ createStandalonePlaygroundStateStorage }) => {
setStorage(createStandalonePlaygroundStateStorage());
})
// eslint-disable-next-line no-console
.catch((x) => console.error(x));
}, []);
const baseUrl = useBaseUrl("/playground");
const playgroundLink = useMemo(() => {
return (
storage &&
baseUrl +
"?" +
storage.resolveSearchParams({
content: content,
options: compilerOptions,
emitter: compilerOptions.emit?.[0],
})
);
}, [storage, compilerOptions, content]);
return <a {...props} href={playgroundLink}></a>;
};

Просмотреть файл

@ -0,0 +1,49 @@
.feature-group {
display: flex;
align-items: stretch;
align-self: center;
flex-direction: column;
padding: 0 80px;
}
.feature {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
max-width: 320px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.image {
width: 64px;
height: 64px;
}
@media only screen and (min-width: 1200px) {
.feature-card {
width: 350px;
}
}
@media only screen and (min-width: 1024px) {
.feature-group {
width: 100%;
flex-direction: row;
justify-content: space-between;
}
.feature {
align-items: baseline;
}
.content {
align-items: baseline;
}
}

Просмотреть файл

@ -0,0 +1,27 @@
import Link from "@docusaurus/Link";
import { FluentImageName, FluentImg } from "../fluent-img";
import { DescriptionText, NeutralText } from "../text/text";
import style from "./use-case-feature.module.css";
export interface UseCaseFeatureProps {
image: FluentImageName;
title: string;
subtitle: string;
link: string;
}
export const UseCaseFeature = ({ image, title, subtitle, link }: UseCaseFeatureProps) => {
return (
<div className={style["feature"]}>
<FluentImg name={image} className={style["image"]} />
<div className={style["content"]}>
<NeutralText size="large">{title}</NeutralText>
<DescriptionText>{subtitle}</DescriptionText>
<Link href={link}>Learn more </Link>
</div>
</div>
);
};
export const UseCaseFeatureGroup = ({ children }) => {
return <div className={style["feature-group"]}>{children}</div>;
};

Просмотреть файл

@ -0,0 +1,58 @@
.container {
padding-top: 100px;
padding-bottom: 80px;
}
.overview {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
align-content: center;
margin: auto;
}
.content {
flex: 1 1 50%;
padding: 80px;
}
.spacer {
height: 32px;
}
.title {
max-width: 522px;
}
.subtitle {
max-width: 522px;
}
.illustration {
width: 40vw;
max-height: 100%;
padding: 0;
overflow: hidden;
}
@media only screen and (min-width: 1024px) {
.overview {
flex-direction: row;
}
.illustration {
width: 50vw;
}
}
@media only screen and (min-width: 1200px) {
.overview {
max-width: 1464px;
}
}
@media only screen and (max-width: 1023px) {
.illustration {
display: none;
}
}

Просмотреть файл

@ -0,0 +1,34 @@
import { Button } from "../button/button";
import { DescriptionText, NeutralText } from "../text/text";
import style from "./use-case-overview.module.css";
export interface UseCaseOverviewProps {
title: string;
subtitle: string;
link: string;
illustration?: React.ReactNode;
}
export const UseCaseOverview = (props: UseCaseOverviewProps) => {
return (
<div className={style["container"]}>
<div className={style["overview"]}>
<div className={style["content"]}>
<NeutralText size="xlarge" className={style["title"]}>
{props.title}
</NeutralText>
<div className={style["spacer"]} />
<DescriptionText size="large" className={style["subtitle"]}>
{props.subtitle}
</DescriptionText>
<div className={style["spacer"]} />
<Button as="a" appearance="primary" href={props.link}>
Get started
</Button>
</div>
<div className={style["illustration"]}>{props.illustration}</div>
</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,51 @@
import { Children, ReactElement, ReactNode, isValidElement, useMemo } from "react";
import { WindowCarouselItemProps } from "./window-carousel";
export interface CarouselData {
items: {
value: string;
label: string;
content: ReactNode;
}[];
}
export interface UseCarouselOptions {
children: ReactNode;
}
export function useCarousel({ children }: UseCarouselOptions): CarouselData {
const items = useMemo(() => {
const result = sanitizeTabsChildren(children);
return result.map((child) => {
return {
value: child.props.value,
label: child.props.value,
content: child.props.children,
};
});
}, [children]);
return { items };
}
function sanitizeTabsChildren(children: ReactNode) {
return (Children.toArray(children)
.filter((child) => child !== "\n")
.map((child: any) => {
if (!child || (isValidElement(child) && isCarouselItem(child))) {
return child;
}
throw new Error(
`Docusaurus error: Bad <WindowCarousel> child <${
typeof child.type === "string" ? child.type : child.type.name
}>: all children of the <WindowCarousel> component should be <WindowCarouselItem>, and every <WindowCarouselItem> should have a unique "value" prop.`
);
})
?.filter(Boolean) ?? []) as ReactElement<WindowCarouselItemProps>[];
}
function isCarouselItem(
comp: ReactElement<unknown>
): comp is ReactElement<WindowCarouselItemProps> {
const { props } = comp;
return !!props && typeof props === "object" && "value" in props;
}

Просмотреть файл

@ -0,0 +1,34 @@
.items {
position: relative;
z-index: 0;
}
.item {
background-color: var(--colorNeutralBackground1);
box-shadow: var(--shadow4);
box-sizing: border-box;
left: 0;
transition: left 0.2s linear;
width: calc(100% - 100px);
}
.item:not(.item-selected) {
position: absolute;
top: 10px;
left: 96px;
max-height: calc(100% - 20px);
cursor: pointer;
}
.item:not(.item-selected):hover {
outline: 4px solid var(--colorBrandForeground1);
}
.item-selected {
position: relative;
z-index: 100;
}
.item :global(img) {
display: block;
}

Просмотреть файл

@ -0,0 +1,81 @@
import { Tab, TabList } from "@fluentui/react-components";
import { Window } from "@site/src/components/window/window";
import clsx from "clsx";
import { ReactNode, useCallback, useState } from "react";
import { useCarousel } from "./use-carousel";
import style from "./window-carousel.module.css";
export interface WindowCarouselProps {
defaultValue?: string;
children: ReactNode;
}
export const WindowCarousel = ({ defaultValue, children }: WindowCarouselProps) => {
const { items } = useCarousel({ children });
const [value, setValue] = useState(defaultValue ?? items[0].value);
const handleTabChange = useCallback(
(_, data) => {
setValue(data.value);
},
[setValue]
);
return (
<div>
<div className={style["items"]}>
{items.map((x) => {
return (
<WindowCarouselItemRender
key={x.value}
value={x.value}
selected={value === x.value}
onSelected={setValue}
>
{x.content}
</WindowCarouselItemRender>
);
})}
</div>
<TabList selectedValue={value} onTabSelect={handleTabChange}>
{items.map((x) => (
<Tab key={x.value} value={x.value}>
{x.label}
</Tab>
))}
</TabList>
</div>
);
};
export interface WindowCarouselItemRenderProps {
value: string;
selected: boolean;
onSelected: (value: string) => void;
children: ReactNode;
}
const WindowCarouselItemRender = ({
value,
onSelected,
selected,
children,
}: WindowCarouselItemRenderProps) => {
const onClick = useCallback(() => {
onSelected(value);
}, [onSelected, value]);
return (
<Window className={clsx(style["item"], selected && style["item-selected"])} onClick={onClick}>
{children}
</Window>
);
};
export interface WindowCarouselItemProps {
value: string;
children: ReactNode;
}
export const WindowCarouselItem = ({ children }: WindowCarouselItemProps) => {
return <>{children}</>;
};

Просмотреть файл

@ -0,0 +1,49 @@
.window {
border-radius: 12px;
border: 1px solid var(--colorNeutralStrokeOnBrand);
overflow: hidden;
}
.header {
background-color: var(--window-color, var(--colorNeutralBackground4));
display: flex;
align-items: center;
justify-content: space-between;
height: 35px;
padding: 0 16px;
}
.header-divider {
height: 1px;
align-self: stretch;
background: radial-gradient(circle, var(--colorNeutralStroke2) 0%, rgba(82, 82, 82, 0) 80%);
}
.header-right-spacer {
width: 52px;
}
.actions {
display: flex;
align-items: center;
gap: 8px;
}
.btn-close,
.btn-minify,
.btn-expand {
border-radius: 50%;
width: 12px;
height: 12px;
}
.btn-close {
background: #ec6a5e;
}
.btn-minify {
background: #f4bf4f;
}
.btn-expand {
background: #62c554;
}

Просмотреть файл

@ -0,0 +1,45 @@
import clsx from "clsx";
import { MouseEventHandler } from "react";
import style from "./window.module.css";
export interface WindowProps {
title?: string;
className?: string;
hideHeader?: boolean;
onClick?: MouseEventHandler<HTMLDivElement>;
children?: React.ReactNode;
}
export const Window = ({ className, children, title, hideHeader, ...others }: WindowProps) => {
const header = hideHeader ? (
""
) : (
<>
<WindowHeader title={title} />
<div className={style["header-divider"]} />
</>
);
return (
<div className={clsx(style["window"], className)} {...others}>
{header}
{children}
</div>
);
};
interface WindowRimProps {
title?: string;
}
const WindowHeader = ({ title }: WindowRimProps) => {
return (
<div className={style["header"]}>
<div className={style["actions"]}>
<div className={style["btn-close"]}></div>
<div className={style["btn-minify"]}></div>
<div className={style["btn-expand"]}></div>
</div>
<div className={style["header-title"]}>{title}</div>
<div className={style["header-right-spacer"]}></div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,48 @@
export const Links = {
useCases: {
openapi: "/openapi",
dataValidation: "/data-validation",
tooling: "/tooling",
},
gettingStartedOpenAPI: "/docs/getting-started/typespec-for-openapi-dev",
gettingStartedWithHttp: "/docs/getting-started/getting-started-http",
standardLibrary: {
decorators: "/docs/standard-library/built-in-decorators",
},
libraryReferences: {
openapi3: {
index: "/docs/libraries/openapi3/reference",
decorators: "/docs/libraries/openapi3/reference/decorators",
},
jsonSchema: {
index: "/docs/libraries/json-schema/reference",
decorators: "/docs/libraries/json-schema/reference/decorators",
},
},
tooling: {
formatter: "/docs/handbook/formatter",
styleGuide: "/docs/handbook/style-guide",
},
editor: {
home: "/docs/introduction/installation#install-the-vs-and-vscode-extensions",
code: "/docs/introduction/editor/code",
visualStudio: "/docs/introduction/editor/visual-studio",
},
extensibility: {
gettingStarted: "/docs/extending-typespec/basics",
decorators: "/docs/extending-typespec/create-decorators",
emitter: "/docs/extending-typespec/emitters",
},
docs: "/docs",
playground: "/playground",
// External
spectral: "https://stoplight.io/open-source/spectral",
swaggerUI: "https://swagger.io/tools/swagger-ui/",
typespecAzure: "https://azure.github.io/typespec-azure",
// Microsoft links
privacy: "https://go.microsoft.com/fwlink/?LinkId=521839",
};

Просмотреть файл

@ -4,28 +4,193 @@
* work well for content-centric websites.
*/
/* Fluent Colors for reference */
:root {
/* Brand Foreground */
--colorBrandForeground1: #0f6cbd;
--colorBrandForeground2: #115ea3;
--colorNeutralForegroundOnBrand: #ffffff;
/* Brand background */
--colorBrandBackground: #0f6cbd;
--colorBrandBackgroundHover: #115ea3;
/* Neutral Foreground */
--colorNeutralForeground1: #242424;
--colorNeutralForeground2: #424242;
--colorNeutralForeground3: #616161;
/* Neutral background */
--colorNeutralBackground1: #ffffff;
--colorNeutralBackground2: #fafafa;
--colorNeutralBackground3: #f5f5f5;
--colorNeutralBackground4: #f0f0f0;
/* Stroke */
--colorNeutralStroke1: #d1d1d1;
--colorNeutralStroke2: #e0e0e0;
--colorNeutralStroke3: #f0f0f0;
--colorNeutralStrokeOnBrand: #ffffff;
--colorNeutralStroke1Hover: #c7c7c7;
/* Status warning */
--colorStatusWarningBackground1: #fff9f5;
--colorStatusWarningBackground2: #fdcfb4;
--colorStatusWarningBackground3: #f7630c;
--colorStatusWarningForeground1: #bc4b09;
--colorStatusWarningForeground2: #8a3707;
--colorStatusWarningForeground3: #bc4b09;
/* Status danger */
--colorStatusDangerBackground1: #fdf3f4;
--colorStatusDangerBackground2: #eeacb2;
--colorStatusDangerBackground3: #c50f1f;
--colorStatusDangerForeground1: #b10e1c;
--colorStatusDangerForeground2: #6e0811;
--colorStatusDangerForeground3: #c50f1f;
/* Shadows */
--shadow2: 0 0 2px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.14);
--shadow4: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.14);
--shadow8: 0 0 2px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.14);
--shadow16: 0 0 2px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.14);
--shadow28: 0 0 8px rgba(0, 0, 0, 0.12), 0 14px 28px rgba(0, 0, 0, 0.14);
--shadow64: 0 0 8px rgba(0, 0, 0, 0.12), 0 32px 64px rgba(0, 0, 0, 0.14);
--shadow2Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.25);
--shadow4Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.25);
--shadow8Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.25);
--shadow16Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 8px 16px rgba(0, 0, 0, 0.25);
--shadow28Brand: 0 0 8px rgba(0, 0, 0, 0.3), 0 14px 28px rgba(0, 0, 0, 0.25);
--shadow64Brand: 0 0 8px rgba(0, 0, 0, 0.3), 0 32px 64px rgba(0, 0, 0, 0.25);
/* Font weight */
--fontWeightRegular: 400;
--fontWeightMedium: 500;
--fontWeightSemibold: 600;
--fontWeightBold: 700;
/* Stroke width */
--strokeWidthThin: 1px;
--strokeWidthThick: 2px;
--strokeWidthThicker: 3px;
--strokeWidthThickest: 4px;
/* Spacing */
--spacingHorizontalNone: 0;
--spacingHorizontalXXS: 2px;
--spacingHorizontalXS: 4px;
--spacingHorizontalSNudge: 6px;
--spacingHorizontalS: 8px;
--spacingHorizontalMNudge: 10px;
--spacingHorizontalM: 12px;
--spacingHorizontalL: 16px;
--spacingHorizontalXL: 20px;
--spacingHorizontalXXL: 24px;
--spacingHorizontalXXXL: 32px;
--spacingVerticalNone: 0;
--spacingVerticalXXS: 2px;
--spacingVerticalXS: 4px;
--spacingVerticalSNudge: 6px;
--spacingVerticalS: 8px;
--spacingVerticalMNudge: 10px;
--spacingVerticalM: 12px;
--spacingVerticalL: 16px;
--spacingVerticalXL: 20px;
--spacingVerticalXXL: 24px;
--spacingVerticalXXXL: 32px;
}
[data-theme="dark"] {
/* Brand Foreground */
--colorBrandForeground1: #479ef5;
--colorBrandForeground2: #62abf5;
--colorNeutralForegroundOnBrand: #ffffff;
/* Brand background */
--colorBrandBackground: #115ea3;
--colorBrandBackgroundHover: #0f6cbd;
/* Neutral Foreground */
--colorNeutralForeground1: #ffffff;
--colorNeutralForeground2: #d6d6d6;
--colorNeutralForeground3: #adadad;
/* Neutral background */
--colorNeutralBackground1: #292929;
--colorNeutralBackground2: #1f1f1f;
--colorNeutralBackground3: #141414;
--colorNeutralBackground4: #0a0a0a;
/* Stroke */
--colorNeutralStroke1: #666666;
--colorNeutralStroke2: #525252;
--colorNeutralStroke3: #3d3d3d;
--colorNeutralStrokeOnBrand: #292929;
--colorNeutralStroke1Hover: #757575;
/* Status warning */
--colorStatusWarningBackground1: #4a1e04;
--colorStatusWarningBackground2: #8a3707;
--colorStatusWarningBackground3: #f7630c;
--colorStatusWarningForeground1: #faa06b;
--colorStatusWarningForeground2: #fdcfb4;
--colorStatusWarningForeground3: #f98845;
/* Status danger */
--colorStatusDangerBackground1: #3b0509;
--colorStatusDangerBackground2: #6e0811;
--colorStatusDangerBackground3: #c50f1f;
--colorStatusDangerForeground1: #dc626d;
--colorStatusDangerForeground2: #eeacb2;
--colorStatusDangerForeground3: #dc626d;
/* Shadows */
--shadow2: 0 0 2px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.28);
--shadow4: 0 0 2px rgba(0, 0, 0, 0.24), 0 2px 4px rgba(0, 0, 0, 0.28);
--shadow8: 0 0 2px rgba(0, 0, 0, 0.24), 0 4px 8px rgba(0, 0, 0, 0.28);
--shadow16: 0 0 2px rgba(0, 0, 0, 0.24), 0 8px 16px rgba(0, 0, 0, 0.28);
--shadow28: 0 0 8px rgba(0, 0, 0, 0.24), 0 14px 28px rgba(0, 0, 0, 0.28);
--shadow64: 0 0 8px rgba(0, 0, 0, 0.24), 0 32px 64px rgba(0, 0, 0, 0.28);
--shadow2Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.25);
--shadow4Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.25);
--shadow8Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.25);
--shadow16Brand: 0 0 2px rgba(0, 0, 0, 0.3), 0 8px 16px rgba(0, 0, 0, 0.25);
--shadow28Brand: 0 0 8px rgba(0, 0, 0, 0.3), 0 14px 28px rgba(0, 0, 0, 0.25);
--shadow64Brand: 0 0 8px rgba(0, 0, 0, 0.3), 0 32px 64px rgba(0, 0, 0, 0.25);
}
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
--ifm-color-primary-light: #33925d;
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
/* Change docusaurus Colors */
--ifm-color-primary: #0f6cbd;
--ifm-color-primary-dark: #0d61aa;
--ifm-color-primary-darker: #0d5ca1;
--ifm-color-primary-darkest: #0a4c84;
--ifm-color-primary-light: #1077d0;
--ifm-color-primary-lighter: #117cd9;
--ifm-color-primary-lightest: #1c8ced;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
/* Header color */
--ifm-navbar-background-color: var(--colorNeutralBackground3);
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme="dark"] {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
--ifm-color-primary-darkest: #1a8870;
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
/* Change docusaurus Colors */
--ifm-color-primary: #0f6cbd;
--ifm-color-primary: #479ef5;
--ifm-color-primary-dark: #298ef3;
--ifm-color-primary-darker: #1a86f3;
--ifm-color-primary-darkest: #0b6fd2;
--ifm-color-primary-light: #65aef7;
--ifm-color-primary-lighter: #74b6f7;
--ifm-color-primary-lightest: #a1cdfa;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
@ -41,3 +206,15 @@
height: 24px;
width: 24px;
}
[data-theme="dark"] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}
.DocSearch-Button {
border-radius: 4px !important;
border: 1px solid var(--neutral-stroke-transparent-rest, rgba(255, 255, 255, 0)) !important;
background: var(--colorNeutralBackground1) !important;
}

Просмотреть файл

@ -0,0 +1,15 @@
# Welcome
Welcome to the TypeSpec community! We're glad you're here.
## Github
We love stars so make sure you [star us on GitHub](https://github.com/microsoft/typespec).
## Contributing
Check out our [contributing guide](https://github.com/microsoft/typespec/blob/main/CONTRIBUTING.md) if you would like to contribute to TypeSpec.
## Discussion
Ask questions on [Github Discussion](https://github.com/microsoft/typespec/discussions)

Просмотреть файл

@ -0,0 +1,29 @@
.hero {
display: flex;
align-items: stretch;
width: 100%;
height: 100%;
max-height: 100%;
max-width: 100%;
min-width: 0;
min-height: 0;
gap: 5px;
overflow: auto;
}
.hero-main {
flex: 1 1 50%;
max-width: 50%;
height: 100%;
max-height: 100%;
}
.hero-shared {
flex: 1 1 50%;
max-width: 50%;
height: 100%;
}
.hero :global(.theme-code-block) {
height: 100%;
}

Просмотреть файл

@ -0,0 +1,145 @@
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import { ShowcaseLayout } from "../components/layouts/fluent-layout";
import { Section } from "../components/section/section";
import { SectionedLayout } from "../components/sectioned-layout/sectioned-layout";
import { UseCaseOverview } from "../components/use-case-overview/use-case-overview";
import { Links } from "../constants";
export default function Home() {
return (
<ShowcaseLayout>
<DataValidationContent />
</ShowcaseLayout>
);
}
const DataValidationContent = () => {
return (
<div>
<SectionedLayout>
<UseCaseOverview
title="Ensure data consistency"
subtitle="Benefit from the reusability and modularity of TypeSpec types to ensure data consistency across your APIs."
link={Links.libraryReferences.jsonSchema.index}
illustration={<DataValidationHeroIllustration />}
/>
<Section
header="Standard library"
title="Use built-in decorators"
description="TypeSpec standard library provides decorators for common validation patterns."
illustration={<ValidationDecoratorsIllustration />}
>
<LearnMoreCard
title="Standard library reference"
description="Browse the standard library reference documentation for details."
image="people-shield"
link={Links.standardLibrary.decorators}
/>
</Section>
<Section
layout="text-right"
header="Output"
title="Produce Json Schema"
description="Benefit from the Json Schema ecosystem to validate your data while writing a much more concise and readable code."
illustration={<MultiFileIllustration />}
>
<LearnMoreCard
title="Configure the json schema emitter"
description="Change how the json schema is emitted: specify a bundleId to combine all schemas into a single file or use json instead of yaml."
image="shield-settings"
link={Links.libraryReferences.jsonSchema.index}
/>
</Section>
<Section
header="Customize"
title="Json Schema Decorators"
description="The json schema library provide decorators to customize the output with json schema specific concept."
illustration={<JsonSchemaExtensionsIllustration />}
>
<LearnMoreCard
title="Json Schema Decorators Reference"
description="Read the reference documentation for available options."
image="design"
link={Links.libraryReferences.jsonSchema.decorators}
/>
</Section>
</SectionedLayout>
</div>
);
};
import commonLibSharedTsp from "!!raw-loader!@site/static/tsp-samples/data-validation/common-lib/common.tsp";
import commonLibMainTsp from "!!raw-loader!@site/static/tsp-samples/data-validation/common-lib/main.tsp";
export const DataValidationHeroIllustration = () => {
return (
<IllustrationCard>
<Tabs>
<TabItem value="main.tsp">
<CodeBlock language="tsp">{commonLibMainTsp}</CodeBlock>
</TabItem>
<TabItem value="common.tsp">
<CodeBlock language="tsp">{commonLibSharedTsp}</CodeBlock>
</TabItem>
</Tabs>
</IllustrationCard>
);
};
import multiFileTsp from "!!raw-loader!@site/static/tsp-samples/json-schema/multi-file/main.tsp";
import multiFileAddress from "!!raw-loader!@site/static/tsp-samples/json-schema/multi-file/out/Address.yaml";
import multiFileCar from "!!raw-loader!@site/static/tsp-samples/json-schema/multi-file/out/Car.yaml";
import multiFilePerson from "!!raw-loader!@site/static/tsp-samples/json-schema/multi-file/out/Person.yaml";
import { CodeBlock } from "../components/code-block/code-block";
const MultiFileIllustration = () => {
return (
<IllustrationCard>
<Tabs>
<TabItem value="main.tsp">
<CodeBlock language="tsp">{multiFileTsp}</CodeBlock>
</TabItem>
<TabItem value="Address.yaml">
<CodeBlock language="yaml">{multiFileAddress}</CodeBlock>
</TabItem>
<TabItem value="Car.yaml">
<CodeBlock language="yaml">{multiFileCar}</CodeBlock>
</TabItem>
<TabItem value="Person.yaml">
<CodeBlock language="yaml">{multiFilePerson}</CodeBlock>
</TabItem>
</Tabs>
</IllustrationCard>
);
};
import extensionsTsp from "!!raw-loader!@site/static/tsp-samples/json-schema/extensions/main.tsp";
import extensionsYaml from "!!raw-loader!@site/static/tsp-samples/json-schema/extensions/out/output.yaml";
const JsonSchemaExtensionsIllustration = () => {
return (
<IllustrationCard>
<Tabs>
<TabItem value="main.tsp">
<CodeBlock language="tsp">{extensionsTsp}</CodeBlock>
</TabItem>
<TabItem value="output.yaml">
<CodeBlock language="yaml">{extensionsYaml}</CodeBlock>
</TabItem>
</Tabs>
</IllustrationCard>
);
};
import validationDecoratorsTsp from "!!raw-loader!@site/static/tsp-samples/data-validation/validation-decorators.tsp";
import { IllustrationCard } from "../components/illustration-card/illustration-card";
import { LearnMoreCard } from "../components/learn-more-card/learn-more-card";
const ValidationDecoratorsIllustration = () => {
return (
<IllustrationCard>
<CodeBlock language="tsp">{validationDecoratorsTsp}</CodeBlock>
</IllustrationCard>
);
};

Просмотреть файл

@ -0,0 +1,10 @@
import { HomeContent } from "../components/homepage/homepage";
import { ShowcaseLayout } from "../components/layouts/fluent-layout";
export default function Home() {
return (
<ShowcaseLayout>
<HomeContent />
</ShowcaseLayout>
);
}

Просмотреть файл

@ -0,0 +1,55 @@
# Multi protocol
TypeSpec is a protocol agnostic language. It could be used with many different protocols independently or together.
## Examples
### Protobuf service and emit a json schema for the models.
In this example we have a protobuf service and we want to emit a json schema for the models which we can use later to validate the data in our service implementation.
```tsp tryit="{"emit": ["@typespec/protobuf", "@typespec/json-schema"]}"
import "@typespec/protobuf";
import "@typespec/http";
import "@typespec/json-schema";
using TypeSpec.Protobuf;
using TypeSpec.Http;
@JsonSchema.jsonSchema
@Protobuf.package({
name: "kiosk",
})
@TypeSpec.service
namespace KioskExample;
// models.tsp
model Kiosk {
@field(1) id?: int32;
@field(2) name: string;
@field(3) size: ScreenSize;
@field(4) location: LatLng;
@field(5) create_time?: int32;
}
model ScreenSize {
@field(1) width: int32;
@field(2) height: int32;
}
model LatLng {
@field(1) lat: float64;
@field(2) lng: float64;
}
model ListResponse {
@field(1) kiosks: Kiosk[];
}
@Protobuf.service
interface Kiosks {
@post createKiosk(...Kiosk): Kiosk;
@list listKiosks(): ListResponse;
@get getKiosk(@path @field(1) id: int32): Kiosk;
@delete deleteKiosk(@path @field(1) id: int32): void;
}
```

Просмотреть файл

@ -0,0 +1,91 @@
import abstractionCode from "!!raw-loader!@site/static/tsp-samples/openapi3/abstraction.tsp";
import { Links } from "@site/src/constants";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import { CodeBlock } from "../../components/code-block/code-block";
import { ShowcaseLayout } from "../../components/layouts/fluent-layout";
import { Section } from "../../components/section/section";
import { SectionedLayout } from "../../components/sectioned-layout/sectioned-layout";
import { UseCaseOverview } from "../../components/use-case-overview/use-case-overview";
import style from "./openapi.module.css";
export default function Home() {
return (
<ShowcaseLayout>
<OpenApiContent />
</ShowcaseLayout>
);
}
const OpenApiContent = () => {
return (
<div>
<UseCaseOverview
title="Write TypeSpec, emit OpenAPI"
subtitle="Benefit from a huge ecosystem of OpenAPI tools for configuring API gateways, generating code, and validating your data."
link={Links.gettingStartedOpenAPI}
illustration={<OpenAPI3HeroIllustration />}
/>
<SectionedLayout>
<Section
header="Ecosystem"
title="Interoperate with the OpenAPI ecosystem"
description="Write TypeSpec, emit OpenAPI. Benefit from a huge ecosystem of OpenAPI tools for configuring API gateways, generating code, and validating your data."
illustration={<OpenAPI3InteroperateIllustration />}
>
<LearnMoreCard
title="Linters"
description="Integrate with spectral to lint your OpenAPI."
image="shield-blue"
link={Links.spectral}
/>
</Section>
<Section
layout="text-right"
header="Ecosystem"
title="Abstract common patterns"
description="Codify API patterns into reusable components, improving up quality and consistency across your API surface."
illustration={<OpenAPI3AbstractCode />}
>
<LearnMoreCard
title="Example: TypeSpec Azure Library"
description="Azure library for TypeSpec allows a multitude of teams to reuse approved patterns."
image="document-cloud"
link={Links.typespecAzure}
/>
</Section>
</SectionedLayout>
</div>
);
};
import heroMainTsp from "!!raw-loader!@site/static/tsp-samples/openapi3/hero/main.tsp";
import heroOpenAPIYaml from "!!raw-loader!@site/static/tsp-samples/openapi3/hero/out/openapi.yaml";
import { IllustrationCard } from "../../components/illustration-card/illustration-card";
import { OpenAPI3InteroperateIllustration } from "../../components/interoperate-illustration/interoperate-illustration";
import { LearnMoreCard } from "../../components/learn-more-card/learn-more-card";
export const OpenAPI3HeroIllustration = () => {
return (
<IllustrationCard>
<Tabs>
<TabItem value="main.tsp">
<CodeBlock language="tsp">{heroMainTsp}</CodeBlock>
</TabItem>
<TabItem value="openapi.yaml">
<div className={style["hero-openapi"]}>
<CodeBlock language="yaml">{heroOpenAPIYaml}</CodeBlock>
</div>
</TabItem>
</Tabs>
</IllustrationCard>
);
};
export const OpenAPI3AbstractCode = () => {
return (
<IllustrationCard>
<CodeBlock language="tsp">{abstractionCode}</CodeBlock>
</IllustrationCard>
);
};

Просмотреть файл

@ -0,0 +1,4 @@
.hero-openapi {
max-height: 50vh;
overflow-y: auto;
}

Просмотреть файл

@ -2,7 +2,7 @@ import BrowserOnly from "@docusaurus/BrowserOnly";
import { useEffect, useState } from "react";
import "@typespec/playground/styles.css";
import { FluentLayout } from "../components/fluent-layout/fluent-layout";
import { FluentLayout } from "../components/layouts/fluent-layout";
import { VersionData, loadImportMap } from "../components/playground-component/import-map";
import { LoadingSpinner } from "../components/playground-component/loading-spinner";

Просмотреть файл

@ -1,29 +0,0 @@
import useBaseUrl from "@docusaurus/useBaseUrl";
import Layout from "@theme/Layout";
export default function Playground() {
return (
<Layout>
<div
className="play-iframe"
style={{
display: "flex",
position: "absolute",
width: "100%",
height: "80%",
}}
>
{
<iframe
title="TypeSpec Specification"
src={useBaseUrl("/spec.html")}
style={{
height: "100%",
width: "100%",
}}
></iframe>
}
</div>
</Layout>
);
}

Просмотреть файл

@ -0,0 +1,60 @@
.formatter-illustration {
--animation-duration: 6s;
width: 100%;
height: 420px;
overflow: hidden;
position: relative;
}
.formatter-illustration > * {
width: 100%;
height: 416px;
}
.formatter-illustration-formatted {
position: absolute;
opacity: 0;
top: 0;
left: 0;
z-index: 100;
animation: fade var(--animation-duration) infinite alternate;
}
.formatter-illustration-codeblock {
height: 100%;
overflow: hidden;
}
@keyframes fade {
0% {
opacity: 0;
}
40% {
opacity: 0;
}
60% {
opacity: 1;
}
100% {
opacity: 1;
}
}
.badge {
position: absolute;
top: 10px;
right: 10px;
z-index: 200;
padding: 2px 5px;
border-radius: 4px;
color: #fefefe;
}
.formatter-illustration-unformatted .badge {
background-color: var(--colorStatusWarningBackground3);
animation: fade var(--animation-duration) infinite alternate-reverse;
}
.formatter-illustration-formatted .badge {
background-color: var(--ifm-color-primary);
animation: fade var(--animation-duration) infinite alternate;
}

Просмотреть файл

@ -0,0 +1,101 @@
import { CodeBlock } from "../components/code-block/code-block";
import { ShowcaseLayout } from "../components/layouts/fluent-layout";
import { Section } from "../components/section/section";
import { SectionedLayout } from "../components/sectioned-layout/sectioned-layout";
import { UseCaseOverview } from "../components/use-case-overview/use-case-overview";
import style from "./tooling.module.css";
export default function Home() {
return (
<ShowcaseLayout>
<DataValidationContent />
</ShowcaseLayout>
);
}
const DataValidationContent = () => {
return (
<div>
<UseCaseOverview
title="Syntax highlighting, autocomplete, formatter and more"
subtitle="Typespec comes out of the box with many crucial tooling that will improve your productivity."
link={Links.editor.home}
illustration={<LightDarkImg src="illustrations/ide-hero" />}
/>
<SectionedLayout>
<Section
header="Style consistency"
title="Built-in formatter"
description="TypeSpec provide an opinionated formatter that enables you to enforce a consistent style in your codebase."
illustration={<FormatterIllustration />}
>
<LearnMoreCard
title="Formatter usage"
description="See documentation on how to use the formatter."
image="text-edit"
link={Links.tooling.formatter}
/>
</Section>
<Section
layout="text-right"
header="Warning"
title="Warning and errors"
description="Errors and warnings in your spec are reported as you type."
illustration={<LightDarkImg src="illustrations/warnings-and-errors" />}
>
<LearnMoreCard
title="Extension installation"
description="See documentation on how to install editor extensions."
image="text-edit"
link={Links.editor.home}
/>
</Section>
<Section
header="Intellisense"
title="Autocomplete and more"
description="IntelliSense shows you intelligent code completion, hover information, and signature help so that you can write code more quickly and correctly."
illustration={<LightDarkImg src="illustrations/autocomplete" />}
/>
<Section
layout="text-right"
header="Refactor"
title="Bulk renaming"
description="One of the simplest refactoring is to rename a reference. You can rename a identifier and see all its reference across your TypeSpec project update."
illustration={
<video
src={useBaseUrl("/img/illustrations/refactor.mp4")}
autoPlay={true}
loop={true}
/>
}
/>
</SectionedLayout>
</div>
);
};
import notFormattedTsp from "!!raw-loader!@site/static/tsp-samples/tooling/formatter/file.noformat.tsp";
import formattedTsp from "!!raw-loader!@site/static/tsp-samples/tooling/formatter/formatted.tsp";
import useBaseUrl from "@docusaurus/useBaseUrl";
import { LearnMoreCard } from "../components/learn-more-card/learn-more-card";
import { LightDarkImg } from "../components/light-dark-img/light-dark-img";
import { Links } from "../constants";
const FormatterIllustration = () => {
return (
<div className={style["formatter-illustration"]}>
<div className={style["formatter-illustration-unformatted"]}>
<div className={style["badge"]}>Unformatted</div>
<CodeBlock language="tsp" className={style["formatter-illustration-codeblock"]}>
{notFormattedTsp}
</CodeBlock>
</div>
<div className={style["formatter-illustration-formatted"]}>
<div className={style["badge"]}>Formatted</div>
<CodeBlock language="tsp" className={style["formatter-illustration-codeblock"]}>
{formattedTsp}
</CodeBlock>
</div>
</div>
);
};

Просмотреть файл

@ -0,0 +1,4 @@
import { withTspPlayground } from "@site/src/components/tsp-tryit-codeblock/with-tsp-playground";
import CodeBlockDocusaurus from "@theme-original/CodeBlock";
export default withTspPlayground(CodeBlockDocusaurus);

Просмотреть файл

@ -0,0 +1,92 @@
.footer {
padding: 80px;
color: var(--colorNeutralForeground2);
background-color: var(--colorNeutralBackground3);
max-width: 100vw;
overflow: hidden;
}
@media only screen and (max-width: 728px) {
.footer {
padding: 40px;
}
}
.top {
display: flex;
gap: 60px;
}
.top-separator {
flex: 1 1 auto;
}
.main {
display: flex;
flex-direction: column;
gap: 10px;
width: 276px;
}
.main-title {
color: var(--colorNeutralForeground1);
line-height: 22px;
font-weight: 600;
}
.separator {
height: 60px;
}
.bottom {
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 5px;
}
@media only screen and (max-width: 728px) {
.links {
display: none;
}
}
.general-links :global(a) {
color: var(--colorNeutralForeground2);
text-decoration: underline;
}
.general-links :global(a:hover) {
color: var(--colorBrandForeground1);
}
.theme-picker {
display: flex;
align-items: flex-start;
padding: 4px 6px;
gap: 6px;
border: 1px solid var(--colorNeutralStroke1);
border-radius: 70px;
}
.theme-option {
cursor: pointer;
display: flex;
padding: 2px;
justify-content: center;
align-items: center;
}
.theme-option:hover {
color: var(--colorNeutralForeground1);
border-radius: 50%;
}
.selected {
background-color: var(--colorBrandForeground1);
border-radius: 50%;
}

Просмотреть файл

@ -0,0 +1,93 @@
import { useColorMode } from "@docusaurus/theme-common";
import { WeatherMoon20Regular, WeatherSunny20Regular } from "@fluentui/react-icons";
import { Link } from "@site/src/components/link/link";
import { Links } from "@site/src/constants";
import clsx from "clsx";
import style from "./index.module.css";
export default function FooterLayout({ style: theme, links, copyright }) {
return (
<footer
className={clsx(style["footer"], {
"footer--dark": theme === "dark",
})}
data-theme="dark"
>
<div className={style["top"]}>
<div className={style["main"]}>
<div className={style["main-title"]}>TypeSpec</div>
<div className={style["main-description"]}>
Follow us for latest updates, contributions, and more.
</div>
<div>
<a
href="https://github.com/microsoft/typespec"
target="_blank"
rel="noopener noreferrer"
className="header-github-link"
aria-label="Github repository"
></a>
</div>
</div>
<div className={style["top-separator"]}></div>
<div className={style["links"]}>{links}</div>
<div>
<ThemePicker />
</div>
</div>
<div className={style["separator"]}></div>
<div className={style["bottom"]}>
<div className={style["logo-copyright"]}>
<div className={style["logo"]}>
<MicrosoftLogo />
Microsoft
</div>
<div>{copyright}</div>
</div>
<div className={style["general-links"]}>
<Link href={Links.privacy} title="Microsoft Privacy Policy">
Privacy
</Link>
</div>
</div>
</footer>
);
}
const MicrosoftLogo = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M7.66683 1.33325H1.3335V7.66659H7.66683V1.33325Z" />
<path d="M14.6668 1.33325H8.3335V7.66659H14.6668V1.33325Z" />
<path d="M8.3335 8.33325H14.6668V14.6666H8.3335V8.33325Z" />
<path d="M7.66683 8.33325H1.3335V14.6666H7.66683V8.33325Z" />
</svg>
);
const ThemePicker = () => {
const { colorMode, setColorMode } = useColorMode();
return (
<div className={style["theme-picker"]}>
<div
title="Light mode"
className={clsx(style["theme-option"], { [style["selected"]]: colorMode === "light" })}
onClick={() => setColorMode("light")}
>
<WeatherSunny20Regular />
</div>
<div
title="Dark mode"
className={clsx(style["theme-option"], { [style["selected"]]: colorMode === "dark" })}
onClick={() => setColorMode("dark")}
>
<WeatherMoon20Regular />
</div>
</div>
);
};

Просмотреть файл

@ -28,7 +28,7 @@ const lang = {
},
property: {
pattern: /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,
pattern: /\b[A-Za-z]\w*(?=\s*:)/m,
lookbehind: true,
alias: "property",
},
@ -68,7 +68,7 @@ const lang = {
/\b(?:import|model|scalar|namespace|op|interface|union|using|is|extends|enum|alias|return|void|never|if|else|projection|dec|extern|fn)\b/,
function: /\b[a-z_]\w*(?=[ \t]*\()/i,
variable: /\b(?:[A-Z_\d]*[a-z]\w*)?\b/,
variable: /\b[A-Za-z]\w*\b/,
number: /(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:E[+-]?\d+)?/i,
operator:

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.7 KiB

Двоичные данные
packages/website/static/img/fluent/chat-d-standard-128x128.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.0 KiB

Двоичные данные
packages/website/static/img/fluent/chat-l-standard-128x128.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.7 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.7 KiB

Двоичные данные
packages/website/static/img/fluent/design-d-standard-128x128.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.4 KiB

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше