From ffd56f49b6247f36d8e6705ac49a9bac31157ec8 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 30 Jan 2026 00:28:53 +0100 Subject: [PATCH 01/45] chore: initial astro setup --- .astro/collections/docs.schema.json | 646 ++++++++ .astro/content-assets.mjs | 1 + .astro/content-modules.mjs | 4 + .astro/content.d.ts | 218 +++ .astro/data-store.json | 1 + .astro/settings.json | 5 + .astro/types.d.ts | 2 + astro.config.mjs | 26 + package.json | 3 + pnpm-lock.yaml | 2374 ++++++++++++++++++++++++++- src/content.config.ts | 7 + src/content/docs/guides/example.md | 6 + src/content/docs/index.mdx | 28 + tsconfig.astro.json | 12 + tsconfig.docusaurus.json | 17 + tsconfig.json | 14 +- 16 files changed, 3280 insertions(+), 84 deletions(-) create mode 100644 .astro/collections/docs.schema.json create mode 100644 .astro/content-assets.mjs create mode 100644 .astro/content-modules.mjs create mode 100644 .astro/content.d.ts create mode 100644 .astro/data-store.json create mode 100644 .astro/settings.json create mode 100644 .astro/types.d.ts create mode 100644 astro.config.mjs create mode 100644 src/content.config.ts create mode 100644 src/content/docs/guides/example.md create mode 100644 src/content/docs/index.mdx create mode 100644 tsconfig.astro.json create mode 100644 tsconfig.docusaurus.json diff --git a/.astro/collections/docs.schema.json b/.astro/collections/docs.schema.json new file mode 100644 index 0000000000..9500aa03f6 --- /dev/null +++ b/.astro/collections/docs.schema.json @@ -0,0 +1,646 @@ +{ + "$ref": "#/definitions/docs", + "definitions": { + "docs": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "editUrl": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "boolean" + } + ], + "default": true + }, + "head": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "enum": [ + "title", + "base", + "link", + "style", + "meta", + "script", + "noscript", + "template" + ] + }, + "attrs": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "not": {} + } + ] + } + }, + "content": { + "type": "string" + } + }, + "required": [ + "tag" + ], + "additionalProperties": false + }, + "default": [] + }, + "tableOfContents": { + "anyOf": [ + { + "type": "object", + "properties": { + "minHeadingLevel": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "default": 2 + }, + "maxHeadingLevel": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "default": 3 + } + }, + "additionalProperties": false + }, + { + "type": "boolean" + } + ], + "default": { + "minHeadingLevel": 2, + "maxHeadingLevel": 3 + } + }, + "template": { + "type": "string", + "enum": [ + "doc", + "splash" + ], + "default": "doc" + }, + "hero": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "tagline": { + "type": "string" + }, + "image": { + "anyOf": [ + { + "type": "object", + "properties": { + "alt": { + "type": "string", + "default": "" + }, + "file": { + "type": "string" + } + }, + "required": [ + "file" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "alt": { + "type": "string", + "default": "" + }, + "dark": { + "type": "string" + }, + "light": { + "type": "string" + } + }, + "required": [ + "dark", + "light" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "html": { + "type": "string" + } + }, + "required": [ + "html" + ], + "additionalProperties": false + } + ] + }, + "actions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "link": { + "type": "string" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "minimal" + ], + "default": "primary" + }, + "icon": { + "anyOf": [ + { + "type": "string", + "enum": [ + "up-caret", + "down-caret", + "right-caret", + "left-caret", + "up-arrow", + "down-arrow", + "right-arrow", + "left-arrow", + "bars", + "translate", + "pencil", + "pen", + "document", + "add-document", + "setting", + "external", + "download", + "cloud-download", + "moon", + "sun", + "laptop", + "open-book", + "information", + "magnifier", + "forward-slash", + "close", + "error", + "warning", + "approve-check-circle", + "approve-check", + "rocket", + "star", + "puzzle", + "list-format", + "random", + "comment", + "comment-alt", + "heart", + "github", + "gitlab", + "bitbucket", + "codePen", + "farcaster", + "discord", + "gitter", + "twitter", + "x.com", + "mastodon", + "codeberg", + "youtube", + "threads", + "linkedin", + "twitch", + "azureDevOps", + "microsoftTeams", + "instagram", + "stackOverflow", + "telegram", + "rss", + "facebook", + "email", + "phone", + "reddit", + "patreon", + "signal", + "slack", + "matrix", + "hackerOne", + "openCollective", + "blueSky", + "discourse", + "zulip", + "pinterest", + "tiktok", + "astro", + "alpine", + "pnpm", + "biome", + "bun", + "mdx", + "apple", + "linux", + "homebrew", + "nix", + "starlight", + "pkl", + "node", + "cloudflare", + "vercel", + "netlify", + "deno", + "jsr", + "nostr", + "backstage", + "confluence", + "jira", + "storybook", + "vscode", + "jetbrains", + "zed", + "vim", + "figma", + "sketch", + "npm", + "sourcehut", + "substack", + "seti:folder", + "seti:bsl", + "seti:mdo", + "seti:salesforce", + "seti:asm", + "seti:bicep", + "seti:bazel", + "seti:c", + "seti:c-sharp", + "seti:html", + "seti:cpp", + "seti:clojure", + "seti:coldfusion", + "seti:config", + "seti:crystal", + "seti:crystal_embedded", + "seti:json", + "seti:css", + "seti:csv", + "seti:xls", + "seti:cu", + "seti:cake", + "seti:cake_php", + "seti:d", + "seti:word", + "seti:elixir", + "seti:elixir_script", + "seti:hex", + "seti:elm", + "seti:favicon", + "seti:f-sharp", + "seti:git", + "seti:go", + "seti:godot", + "seti:gradle", + "seti:grails", + "seti:graphql", + "seti:hacklang", + "seti:haml", + "seti:mustache", + "seti:haskell", + "seti:haxe", + "seti:jade", + "seti:java", + "seti:javascript", + "seti:jinja", + "seti:julia", + "seti:karma", + "seti:kotlin", + "seti:dart", + "seti:liquid", + "seti:livescript", + "seti:lua", + "seti:markdown", + "seti:argdown", + "seti:info", + "seti:clock", + "seti:maven", + "seti:nim", + "seti:github", + "seti:notebook", + "seti:nunjucks", + "seti:npm", + "seti:ocaml", + "seti:odata", + "seti:perl", + "seti:php", + "seti:pipeline", + "seti:pddl", + "seti:plan", + "seti:happenings", + "seti:powershell", + "seti:prisma", + "seti:pug", + "seti:puppet", + "seti:purescript", + "seti:python", + "seti:react", + "seti:rescript", + "seti:R", + "seti:ruby", + "seti:rust", + "seti:sass", + "seti:spring", + "seti:slim", + "seti:smarty", + "seti:sbt", + "seti:scala", + "seti:ethereum", + "seti:stylus", + "seti:svelte", + "seti:swift", + "seti:db", + "seti:terraform", + "seti:tex", + "seti:default", + "seti:twig", + "seti:typescript", + "seti:tsconfig", + "seti:vala", + "seti:vite", + "seti:vue", + "seti:wasm", + "seti:wat", + "seti:xml", + "seti:yml", + "seti:prolog", + "seti:zig", + "seti:zip", + "seti:wgt", + "seti:illustrator", + "seti:photoshop", + "seti:pdf", + "seti:font", + "seti:image", + "seti:svg", + "seti:sublime", + "seti:code-search", + "seti:shell", + "seti:video", + "seti:audio", + "seti:windows", + "seti:jenkins", + "seti:babel", + "seti:bower", + "seti:docker", + "seti:code-climate", + "seti:eslint", + "seti:firebase", + "seti:firefox", + "seti:gitlab", + "seti:grunt", + "seti:gulp", + "seti:ionic", + "seti:platformio", + "seti:rollup", + "seti:stylelint", + "seti:yarn", + "seti:webpack", + "seti:lock", + "seti:license", + "seti:makefile", + "seti:heroku", + "seti:todo", + "seti:ignored" + ] + }, + { + "type": "string", + "pattern": "^\\ import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Findex.mdx&astroContentModuleFlag=true")]]); + \ No newline at end of file diff --git a/.astro/content.d.ts b/.astro/content.d.ts new file mode 100644 index 0000000000..f65e91bb4e --- /dev/null +++ b/.astro/content.d.ts @@ -0,0 +1,218 @@ +declare module 'astro:content' { + interface Render { + '.mdx': Promise<{ + Content: import('astro').MDXContent; + headings: import('astro').MarkdownHeading[]; + remarkPluginFrontmatter: Record; + components: import('astro').MDXInstance<{}>['components']; + }>; + } +} + +declare module 'astro:content' { + export interface RenderResult { + Content: import('astro/runtime/server/index.js').AstroComponentFactory; + headings: import('astro').MarkdownHeading[]; + remarkPluginFrontmatter: Record; + } + interface Render { + '.md': Promise; + } + + export interface RenderedContent { + html: string; + metadata?: { + imagePaths: Array; + [key: string]: unknown; + }; + } +} + +declare module 'astro:content' { + type Flatten = T extends { [K: string]: infer U } ? U : never; + + export type CollectionKey = keyof AnyEntryMap; + export type CollectionEntry = Flatten; + + export type ContentCollectionKey = keyof ContentEntryMap; + export type DataCollectionKey = keyof DataEntryMap; + + type AllValuesOf = T extends any ? T[keyof T] : never; + type ValidContentEntrySlug = AllValuesOf< + ContentEntryMap[C] + >['slug']; + + export type ReferenceDataEntry< + C extends CollectionKey, + E extends keyof DataEntryMap[C] = string, + > = { + collection: C; + id: E; + }; + export type ReferenceContentEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}) = string, + > = { + collection: C; + slug: E; + }; + export type ReferenceLiveEntry = { + collection: C; + id: string; + }; + + /** @deprecated Use `getEntry` instead. */ + export function getEntryBySlug< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + // Note that this has to accept a regular string too, for SSR + entrySlug: E, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + + /** @deprecated Use `getEntry` instead. */ + export function getDataEntryById( + collection: C, + entryId: E, + ): Promise>; + + export function getCollection>( + collection: C, + filter?: (entry: CollectionEntry) => entry is E, + ): Promise; + export function getCollection( + collection: C, + filter?: (entry: CollectionEntry) => unknown, + ): Promise[]>; + + export function getLiveCollection( + collection: C, + filter?: LiveLoaderCollectionFilterType, + ): Promise< + import('astro').LiveDataCollectionResult, LiveLoaderErrorType> + >; + + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + entry: ReferenceContentEntry, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + entry: ReferenceDataEntry, + ): E extends keyof DataEntryMap[C] + ? Promise + : Promise | undefined>; + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + slug: E, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + collection: C, + id: E, + ): E extends keyof DataEntryMap[C] + ? string extends keyof DataEntryMap[C] + ? Promise | undefined + : Promise + : Promise | undefined>; + export function getLiveEntry( + collection: C, + filter: string | LiveLoaderEntryFilterType, + ): Promise, LiveLoaderErrorType>>; + + /** Resolve an array of entry references from the same collection */ + export function getEntries( + entries: ReferenceContentEntry>[], + ): Promise[]>; + export function getEntries( + entries: ReferenceDataEntry[], + ): Promise[]>; + + export function render( + entry: AnyEntryMap[C][string], + ): Promise; + + export function reference( + collection: C, + ): import('astro/zod').ZodEffects< + import('astro/zod').ZodString, + C extends keyof ContentEntryMap + ? ReferenceContentEntry> + : ReferenceDataEntry + >; + // Allow generic `string` to avoid excessive type errors in the config + // if `dev` is not running to update as you edit. + // Invalid collection names will be caught at build time. + export function reference( + collection: C, + ): import('astro/zod').ZodEffects; + + type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; + type InferEntrySchema = import('astro/zod').infer< + ReturnTypeOrOriginal['schema']> + >; + + type ContentEntryMap = { + + }; + + type DataEntryMap = { + "docs": Record; + rendered?: RenderedContent; + filePath?: string; +}>; + + }; + + type AnyEntryMap = ContentEntryMap & DataEntryMap; + + type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< + infer TData, + infer TEntryFilter, + infer TCollectionFilter, + infer TError + > + ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } + : { data: never; entryFilter: never; collectionFilter: never; error: never }; + type ExtractDataType = ExtractLoaderTypes['data']; + type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; + type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; + type ExtractErrorType = ExtractLoaderTypes['error']; + + type LiveLoaderDataType = + LiveContentConfig['collections'][C]['schema'] extends undefined + ? ExtractDataType + : import('astro/zod').infer< + Exclude + >; + type LiveLoaderEntryFilterType = + ExtractEntryFilterType; + type LiveLoaderCollectionFilterType = + ExtractCollectionFilterType; + type LiveLoaderErrorType = ExtractErrorType< + LiveContentConfig['collections'][C]['loader'] + >; + + export type ContentConfig = typeof import("../src/content.config.js"); + export type LiveContentConfig = never; +} diff --git a/.astro/data-store.json b/.astro/data-store.json new file mode 100644 index 0000000000..bc1484f191 --- /dev/null +++ b/.astro/data-store.json @@ -0,0 +1 @@ +[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.16.16","content-config-digest","28e7bc87492477d9","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[null,null,null],\"rehypePlugins\":[[null,{\"experimentalHeadingIdCompat\":false}],null,[null,{\"themes\":[{\"name\":\"Night Owl No Italics\",\"type\":\"dark\",\"colors\":{\"focusBorder\":\"#122d42\",\"foreground\":\"#d6deeb\",\"disabledForeground\":\"#cccccc80\",\"descriptionForeground\":\"#d6deebb3\",\"errorForeground\":\"#ef5350\",\"icon.foreground\":\"#c5c5c5\",\"contrastActiveBorder\":null,\"contrastBorder\":\"#122d42\",\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#4f4f4f\",\"textLink.activeForeground\":\"#3794ff\",\"textLink.foreground\":\"#3794ff\",\"textPreformat.foreground\":\"#d7ba7d\",\"textSeparator.foreground\":\"#ffffff2e\",\"editor.background\":\"#23262f\",\"editor.foreground\":\"#d6deeb\",\"editorLineNumber.foreground\":\"#4b6479\",\"editorLineNumber.activeForeground\":\"#c5e4fd\",\"editorActiveLineNumber.foreground\":\"#c6c6c6\",\"editor.selectionBackground\":\"#1d3b53\",\"editor.inactiveSelectionBackground\":\"#7e57c25a\",\"editor.selectionHighlightBackground\":\"#5f7e9779\",\"editorError.foreground\":\"#ef5350\",\"editorWarning.foreground\":\"#b39554\",\"editorInfo.foreground\":\"#3794ff\",\"editorHint.foreground\":\"#eeeeeeb2\",\"problemsErrorIcon.foreground\":\"#ef5350\",\"problemsWarningIcon.foreground\":\"#b39554\",\"problemsInfoIcon.foreground\":\"#3794ff\",\"editor.findMatchBackground\":\"#5f7e9779\",\"editor.findMatchHighlightBackground\":\"#1085bb5d\",\"editor.findRangeHighlightBackground\":\"#3a3d4166\",\"editorLink.activeForeground\":\"#4e94ce\",\"editorLightBulb.foreground\":\"#ffcc00\",\"editorLightBulbAutoFix.foreground\":\"#75beff\",\"diffEditor.insertedTextBackground\":\"#99b76d23\",\"diffEditor.insertedTextBorder\":\"#c5e47833\",\"diffEditor.removedTextBackground\":\"#ef535033\",\"diffEditor.removedTextBorder\":\"#ef53504d\",\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#011627\",\"editorStickyScrollHover.background\":\"#2a2d2e\",\"editorInlayHint.background\":\"#5f7e97cc\",\"editorInlayHint.foreground\":\"#ffffff\",\"editorInlayHint.typeBackground\":\"#5f7e97cc\",\"editorInlayHint.typeForeground\":\"#ffffff\",\"editorInlayHint.parameterBackground\":\"#5f7e97cc\",\"editorInlayHint.parameterForeground\":\"#ffffff\",\"editorPane.background\":\"#011627\",\"editorGroup.emptyBackground\":\"#011627\",\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-black)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#011627\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#011627\",\"editorGroup.dropBackground\":\"#7e57c273\",\"editorGroup.dropIntoPromptForeground\":\"#d6deeb\",\"editorGroup.dropIntoPromptBackground\":\"#021320\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#011627\",\"sideBySideEditor.verticalBorder\":\"#011627\",\"scrollbar.shadow\":\"#010b14\",\"scrollbarSlider.background\":\"#ffffff17\",\"scrollbarSlider.hoverBackground\":\"#ffffff40\",\"scrollbarSlider.activeBackground\":\"#084d8180\",\"panel.background\":\"#011627\",\"panel.border\":\"#5f7e97\",\"panelTitle.activeBorder\":\"#5f7e97\",\"panelTitle.activeForeground\":\"#ffffffcc\",\"panelTitle.inactiveForeground\":\"#d6deeb80\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#011627\",\"widget.shadow\":\"#011627\",\"editorWidget.background\":\"#021320\",\"editorWidget.foreground\":\"#d6deeb\",\"editorWidget.border\":\"#5f7e97\",\"quickInput.background\":\"#021320\",\"quickInput.foreground\":\"#d6deeb\",\"quickInputTitle.background\":\"#ffffff1a\",\"pickerGroup.foreground\":\"#d1aaff\",\"pickerGroup.border\":\"#011627\",\"editor.hoverHighlightBackground\":\"#7e57c25a\",\"editorHoverWidget.background\":\"#011627\",\"editorHoverWidget.foreground\":\"#d6deeb\",\"editorHoverWidget.border\":\"#5f7e97\",\"editorHoverWidget.statusBarBackground\":\"#011a2f\",\"titleBar.activeBackground\":\"var(--sl-color-black)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#010e1a\",\"titleBar.inactiveForeground\":\"#eeefff99\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#5a5d5e50\",\"toolbar.activeBackground\":\"#63666750\",\"tab.activeBackground\":\"#0b2942\",\"tab.unfocusedActiveBackground\":\"#0b2942\",\"tab.inactiveBackground\":\"#01111d\",\"tab.unfocusedInactiveBackground\":\"#01111d\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#5f7e97\",\"tab.unfocusedActiveForeground\":\"#5f7e97\",\"tab.unfocusedInactiveForeground\":\"#5f7e97\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#272b3b\",\"tab.lastPinnedBorder\":\"#585858\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":\"#262a39\",\"tab.activeBorderTop\":\"var(--sl-color-accent-high)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#3399cc\",\"tab.inactiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedActiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedInactiveModifiedBorder\":\"#3399cc40\",\"badge.background\":\"#5f7e97\",\"badge.foreground\":\"#ffffff\",\"button.background\":\"#7e57c2cc\",\"button.foreground\":\"#ffffffcc\",\"button.border\":\"#122d42\",\"button.separator\":\"#ffffff52\",\"button.hoverBackground\":\"#7e57c2\",\"button.secondaryBackground\":\"#3a3d41\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#46494e\",\"dropdown.background\":\"#011627\",\"dropdown.foreground\":\"#ffffffcc\",\"dropdown.border\":\"#5f7e97\",\"list.activeSelectionBackground\":\"#234d708c\",\"list.activeSelectionForeground\":\"#ffffff\",\"tree.indentGuidesStroke\":\"#585858\",\"input.background\":\"#0b253a\",\"input.foreground\":\"#ffffffcc\",\"input.placeholderForeground\":\"#5f7e97\",\"inputOption.activeBorder\":\"#ffffffcc\",\"inputOption.hoverBackground\":\"#5a5d5e80\",\"inputOption.activeBackground\":\"#122d4266\",\"inputOption.activeForeground\":\"#ffffff\",\"inputValidation.infoBackground\":\"#00589ef2\",\"inputValidation.infoBorder\":\"#64b5f6\",\"inputValidation.warningBackground\":\"#675700f2\",\"inputValidation.warningBorder\":\"#ffca28\",\"inputValidation.errorBackground\":\"#ab0300f2\",\"inputValidation.errorBorder\":\"#ef5350\",\"keybindingLabel.background\":\"#8080802b\",\"keybindingLabel.foreground\":\"#cccccc\",\"keybindingLabel.border\":\"#33333399\",\"keybindingLabel.bottomBorder\":\"#44444499\",\"menu.foreground\":\"#ffffffcc\",\"menu.background\":\"#011627\",\"menu.selectionForeground\":\"#ffffff\",\"menu.selectionBackground\":\"#234d708c\",\"menu.separatorBackground\":\"#606060\",\"editor.snippetTabstopHighlightBackground\":\"#7c7c74c\",\"editor.snippetFinalTabstopHighlightBorder\":\"#525252\",\"terminal.ansiBlack\":\"#011627\",\"terminal.ansiRed\":\"#ef5350\",\"terminal.ansiGreen\":\"#22da6e\",\"terminal.ansiYellow\":\"#c5e478\",\"terminal.ansiBlue\":\"#82aaff\",\"terminal.ansiMagenta\":\"#c792ea\",\"terminal.ansiCyan\":\"#21c7a8\",\"terminal.ansiWhite\":\"#ffffff\",\"terminal.ansiBrightBlack\":\"#575656\",\"terminal.ansiBrightRed\":\"#ef5350\",\"terminal.ansiBrightGreen\":\"#22da6e\",\"terminal.ansiBrightYellow\":\"#ffeb95\",\"terminal.ansiBrightBlue\":\"#82aaff\",\"terminal.ansiBrightMagenta\":\"#c792ea\",\"terminal.ansiBrightCyan\":\"#7fdbca\",\"terminal.ansiBrightWhite\":\"#ffffff\",\"selection.background\":\"#4373c2\",\"input.border\":\"#5f7e97\",\"punctuation.definition.generic.begin.html\":\"#ef5350f2\",\"progress.background\":\"#7e57c2\",\"breadcrumb.foreground\":\"#a599e9\",\"breadcrumb.focusForeground\":\"#ffffff\",\"breadcrumb.activeSelectionForeground\":\"#ffffff\",\"breadcrumbPicker.background\":\"#001122\",\"list.invalidItemForeground\":\"#975f94\",\"list.dropBackground\":\"#011627\",\"list.focusBackground\":\"#010d18\",\"list.focusForeground\":\"#ffffff\",\"list.highlightForeground\":\"#ffffff\",\"list.hoverBackground\":\"#011627\",\"list.hoverForeground\":\"#ffffff\",\"list.inactiveSelectionBackground\":\"#0e293f\",\"list.inactiveSelectionForeground\":\"#5f7e97\",\"activityBar.background\":\"#011627\",\"activityBar.dropBackground\":\"#5f7e97\",\"activityBar.foreground\":\"#5f7e97\",\"activityBar.border\":\"#011627\",\"activityBarBadge.background\":\"#44596b\",\"activityBarBadge.foreground\":\"#ffffff\",\"sideBar.background\":\"#011627\",\"sideBar.foreground\":\"#89a4bb\",\"sideBar.border\":\"#011627\",\"sideBarTitle.foreground\":\"#5f7e97\",\"sideBarSectionHeader.background\":\"#011627\",\"sideBarSectionHeader.foreground\":\"#5f7e97\",\"editorCursor.foreground\":\"#80a4c2\",\"editor.wordHighlightBackground\":\"#f6bbe533\",\"editor.wordHighlightStrongBackground\":\"#e2a2f433\",\"editor.lineHighlightBackground\":\"#0003\",\"editor.rangeHighlightBackground\":\"#7e57c25a\",\"editorIndentGuide.background\":\"#5e81ce52\",\"editorIndentGuide.activeBackground\":\"#7e97ac\",\"editorRuler.foreground\":\"#5e81ce52\",\"editorCodeLens.foreground\":\"#5e82ceb4\",\"editorBracketMatch.background\":\"#5f7e974d\",\"editorOverviewRuler.currentContentForeground\":\"#7e57c2\",\"editorOverviewRuler.incomingContentForeground\":\"#7e57c2\",\"editorOverviewRuler.commonContentForeground\":\"#7e57c2\",\"editorGutter.background\":\"#011627\",\"editorGutter.modifiedBackground\":\"#e2b93d\",\"editorGutter.addedBackground\":\"#9ccc65\",\"editorGutter.deletedBackground\":\"#ef5350\",\"editorSuggestWidget.background\":\"#2c3043\",\"editorSuggestWidget.border\":\"#2b2f40\",\"editorSuggestWidget.foreground\":\"#d6deeb\",\"editorSuggestWidget.highlightForeground\":\"#ffffff\",\"editorSuggestWidget.selectedBackground\":\"#5f7e97\",\"debugExceptionWidget.background\":\"#011627\",\"debugExceptionWidget.border\":\"#5f7e97\",\"editorMarkerNavigation.background\":\"#0b2942\",\"editorMarkerNavigationError.background\":\"#ef5350\",\"editorMarkerNavigationWarning.background\":\"#ffca28\",\"peekView.border\":\"#5f7e97\",\"peekViewEditor.background\":\"#011627\",\"peekViewEditor.matchHighlightBackground\":\"#7e57c25a\",\"peekViewResult.background\":\"#011627\",\"peekViewResult.fileForeground\":\"#5f7e97\",\"peekViewResult.lineForeground\":\"#5f7e97\",\"peekViewResult.matchHighlightBackground\":\"#ffffffcc\",\"peekViewResult.selectionBackground\":\"#2e3250\",\"peekViewResult.selectionForeground\":\"#5f7e97\",\"peekViewTitle.background\":\"#011627\",\"peekViewTitleDescription.foreground\":\"#697098\",\"peekViewTitleLabel.foreground\":\"#5f7e97\",\"merge.currentHeaderBackground\":\"#5f7e97\",\"merge.incomingHeaderBackground\":\"#7e57c25a\",\"statusBar.background\":\"#011627\",\"statusBar.foreground\":\"#5f7e97\",\"statusBar.border\":\"#262a39\",\"statusBar.debuggingBackground\":\"#202431\",\"statusBar.debuggingBorder\":\"#1f2330\",\"statusBar.noFolderBackground\":\"#011627\",\"statusBar.noFolderBorder\":\"#25293a\",\"statusBarItem.activeBackground\":\"#202431\",\"statusBarItem.hoverBackground\":\"#202431\",\"statusBarItem.prominentBackground\":\"#202431\",\"statusBarItem.prominentHoverBackground\":\"#202431\",\"notifications.background\":\"#01111d\",\"notifications.border\":\"#262a39\",\"notificationCenter.border\":\"#262a39\",\"notificationToast.border\":\"#262a39\",\"notifications.foreground\":\"#ffffffcc\",\"notificationLink.foreground\":\"#80cbc4\",\"extensionButton.prominentForeground\":\"#ffffffcc\",\"extensionButton.prominentBackground\":\"#7e57c2cc\",\"extensionButton.prominentHoverBackground\":\"#7e57c2\",\"terminal.selectionBackground\":\"#1b90dd4d\",\"terminalCursor.background\":\"#234d70\",\"debugToolBar.background\":\"#011627\",\"welcomePage.buttonBackground\":\"#011627\",\"welcomePage.buttonHoverBackground\":\"#011627\",\"walkThrough.embeddedEditorBackground\":\"#011627\",\"gitDecoration.modifiedResourceForeground\":\"#a2bffc\",\"gitDecoration.deletedResourceForeground\":\"#ef535090\",\"gitDecoration.untrackedResourceForeground\":\"#c5e478ff\",\"gitDecoration.ignoredResourceForeground\":\"#395a75\",\"gitDecoration.conflictingResourceForeground\":\"#ffeb95cc\",\"source.elm\":\"#5f7e97\",\"string.quoted.single.js\":\"#ffffff\",\"meta.objectliteral.js\":\"#82aaff\"},\"fg\":\"#d6deeb\",\"bg\":\"#23262f\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#a2bffc\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#f27775fe\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#d6deeb\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#919f9f\",\"fontStyle\":\"\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#5ca7e4\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#caece6\",\"fontStyle\":\"\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"fontStyle\":\"\",\"foreground\":\"#c5e478\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"background\":\"#ff2c83\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#d3423e\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#919f9f\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#cdebf7\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#ffffff\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#80cbc4\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#57eaf1\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#fad430\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#faf39f\",\"fontStyle\":\"\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#82aaff\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Comparison, returns, imports, and Keyword Operator Ruby\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#989da0\",\"background\":\"#F78C6C\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"background\":\"#8BD649\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#ec5f67\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#ef787f\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#31e1eb\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Unquoted String Zone\",\"scope\":[\"string.unquoted.preprocessor.message.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Region\",\"scope\":[\"punctuation.separator.hash.cs\",\"keyword.preprocessor.region.cs\",\"keyword.preprocessor.endregion.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"bold\"}},{\"name\":\"C# Other Variables\",\"scope\":[\"variable.other.object.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Enum\",\"scope\":[\"entity.name.type.enum.cs\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Dart String\",\"scope\":[\"string.interpolated.single.dart\",\"string.interpolated.double.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Dart Class\",\"scope\":[\"support.class.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#ff6d6d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#dddddd\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#e0dec6\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#6ae9f0\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#78ccf0\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#8b96ea\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#c789d6\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#ff869a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#d6deeb\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#8eace3\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#d7dbe0\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-6)\",\"terminalBackground\":\"var(--sl-color-gray-6)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-6)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#ffffff17\",\"markBorderColor\":\"#ffffff40\"}}},{\"name\":\"Night Owl Light\",\"type\":\"light\",\"colors\":{\"focusBorder\":\"#93a1a1\",\"foreground\":\"#403f53\",\"disabledForeground\":\"#61616180\",\"descriptionForeground\":\"#403f53\",\"errorForeground\":\"#403f53\",\"icon.foreground\":\"#424242\",\"contrastActiveBorder\":null,\"contrastBorder\":null,\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#dcdcdc66\",\"textLink.activeForeground\":\"#006ab1\",\"textLink.foreground\":\"#006ab1\",\"textPreformat.foreground\":\"#a31515\",\"textSeparator.foreground\":\"#0000002e\",\"editor.background\":\"#f6f7f9\",\"editor.foreground\":\"#403f53\",\"editorLineNumber.foreground\":\"#90a7b2\",\"editorLineNumber.activeForeground\":\"#403f53\",\"editorActiveLineNumber.foreground\":\"#0b216f\",\"editor.selectionBackground\":\"#e0e0e0\",\"editor.inactiveSelectionBackground\":\"#e0e0e080\",\"editor.selectionHighlightBackground\":\"#339cec33\",\"editorError.foreground\":\"#e64d49\",\"editorWarning.foreground\":\"#daaa01\",\"editorInfo.foreground\":\"#1a85ff\",\"editorHint.foreground\":\"#6c6c6c\",\"problemsErrorIcon.foreground\":\"#e64d49\",\"problemsWarningIcon.foreground\":\"#daaa01\",\"problemsInfoIcon.foreground\":\"#1a85ff\",\"editor.findMatchBackground\":\"#93a1a16c\",\"editor.findMatchHighlightBackground\":\"#93a1a16c\",\"editor.findRangeHighlightBackground\":\"#7497a633\",\"editorLink.activeForeground\":\"#0000ff\",\"editorLightBulb.foreground\":\"#ddb100\",\"editorLightBulbAutoFix.foreground\":\"#007acc\",\"diffEditor.insertedTextBackground\":\"#9ccc2c40\",\"diffEditor.insertedTextBorder\":null,\"diffEditor.removedTextBackground\":\"#ff000033\",\"diffEditor.removedTextBorder\":null,\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#fbfbfb\",\"editorStickyScrollHover.background\":\"#f0f0f0\",\"editorInlayHint.background\":\"#2aa29899\",\"editorInlayHint.foreground\":\"#f0f0f0\",\"editorInlayHint.typeBackground\":\"#2aa29899\",\"editorInlayHint.typeForeground\":\"#f0f0f0\",\"editorInlayHint.parameterBackground\":\"#2aa29899\",\"editorInlayHint.parameterForeground\":\"#f0f0f0\",\"editorPane.background\":\"#fbfbfb\",\"editorGroup.emptyBackground\":null,\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-gray-6)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#f0f0f0\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#f0f0f0\",\"editorGroup.dropBackground\":\"#2677cb2d\",\"editorGroup.dropIntoPromptForeground\":\"#403f53\",\"editorGroup.dropIntoPromptBackground\":\"#f0f0f0\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#f0f0f0\",\"sideBySideEditor.verticalBorder\":\"#f0f0f0\",\"scrollbar.shadow\":\"#cccccc\",\"scrollbarSlider.background\":\"#0000001a\",\"scrollbarSlider.hoverBackground\":\"#00000055\",\"scrollbarSlider.activeBackground\":\"#00000099\",\"panel.background\":\"#f0f0f0\",\"panel.border\":\"#d9d9d9\",\"panelTitle.activeBorder\":\"#424242\",\"panelTitle.activeForeground\":\"#424242\",\"panelTitle.inactiveForeground\":\"#424242bf\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#f6f6f6\",\"widget.shadow\":\"#d9d9d9\",\"editorWidget.background\":\"#f0f0f0\",\"editorWidget.foreground\":\"#403f53\",\"editorWidget.border\":\"#d9d9d9\",\"quickInput.background\":\"#f0f0f0\",\"quickInput.foreground\":\"#403f53\",\"quickInputTitle.background\":\"#0000000f\",\"pickerGroup.foreground\":\"#403f53\",\"pickerGroup.border\":\"#d9d9d9\",\"editor.hoverHighlightBackground\":\"#339cec33\",\"editorHoverWidget.background\":\"#f0f0f0\",\"editorHoverWidget.foreground\":\"#403f53\",\"editorHoverWidget.border\":\"#d9d9d9\",\"editorHoverWidget.statusBarBackground\":\"#e4e4e4\",\"titleBar.activeBackground\":\"var(--sl-color-gray-6)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#f0f0f099\",\"titleBar.inactiveForeground\":\"#33333399\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#b8b8b850\",\"toolbar.activeBackground\":\"#a6a6a650\",\"tab.activeBackground\":\"#f6f6f6\",\"tab.unfocusedActiveBackground\":\"#f6f6f6\",\"tab.inactiveBackground\":\"#f0f0f0\",\"tab.unfocusedInactiveBackground\":\"#f0f0f0\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#403f53\",\"tab.unfocusedActiveForeground\":\"#403f53b3\",\"tab.unfocusedInactiveForeground\":\"#403f5380\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#f0f0f0\",\"tab.lastPinnedBorder\":\"#a9a9a9\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":null,\"tab.activeBorderTop\":\"var(--sl-color-accent)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#2aa298\",\"tab.inactiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedActiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedInactiveModifiedBorder\":\"#93a1a1\",\"badge.background\":\"#2aa298\",\"badge.foreground\":\"#f0f0f0\",\"button.background\":\"#2aa298\",\"button.foreground\":\"#f0f0f0\",\"button.border\":null,\"button.separator\":\"#f0f0f066\",\"button.hoverBackground\":\"#22827a\",\"button.secondaryBackground\":\"#5f6a79\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#4c5561\",\"dropdown.background\":\"#f0f0f0\",\"dropdown.foreground\":\"#403f53\",\"dropdown.border\":\"#d9d9d9\",\"list.activeSelectionBackground\":\"#d3e8f8\",\"list.activeSelectionForeground\":\"#403f53\",\"tree.indentGuidesStroke\":\"#a9a9a9\",\"input.background\":\"#f0f0f0\",\"input.foreground\":\"#403f53\",\"input.placeholderForeground\":\"#93a1a1\",\"inputOption.activeBorder\":\"#2aa298\",\"inputOption.hoverBackground\":\"#b8b8b850\",\"inputOption.activeBackground\":\"#93a1a133\",\"inputOption.activeForeground\":\"#000000\",\"inputValidation.infoBackground\":\"#f0f0f0\",\"inputValidation.infoBorder\":\"#d0d0d0\",\"inputValidation.warningBackground\":\"#daaa01\",\"inputValidation.warningBorder\":\"#e0af02\",\"inputValidation.errorBackground\":\"#f76e6e\",\"inputValidation.errorBorder\":\"#de3d3b\",\"keybindingLabel.background\":\"#dddddd66\",\"keybindingLabel.foreground\":\"#555555\",\"keybindingLabel.border\":\"#cccccc66\",\"keybindingLabel.bottomBorder\":\"#bbbbbb66\",\"menu.foreground\":\"#403f53\",\"menu.background\":\"#f0f0f0\",\"menu.selectionForeground\":\"#403f53\",\"menu.selectionBackground\":\"#d3e8f8\",\"menu.separatorBackground\":\"#d4d4d4\",\"editor.snippetTabstopHighlightBackground\":\"#0a326433\",\"editor.snippetFinalTabstopHighlightBorder\":\"#0a326480\",\"terminal.ansiBlack\":\"#403f53\",\"terminal.ansiRed\":\"#de3d3b\",\"terminal.ansiGreen\":\"#08916a\",\"terminal.ansiYellow\":\"#e0af02\",\"terminal.ansiBlue\":\"#288ed7\",\"terminal.ansiMagenta\":\"#d6438a\",\"terminal.ansiCyan\":\"#2aa298\",\"terminal.ansiWhite\":\"#f0f0f0\",\"terminal.ansiBrightBlack\":\"#403f53\",\"terminal.ansiBrightRed\":\"#de3d3b\",\"terminal.ansiBrightGreen\":\"#08916a\",\"terminal.ansiBrightYellow\":\"#daaa01\",\"terminal.ansiBrightBlue\":\"#288ed7\",\"terminal.ansiBrightMagenta\":\"#d6438a\",\"terminal.ansiBrightCyan\":\"#2aa298\",\"terminal.ansiBrightWhite\":\"#f0f0f0\",\"selection.background\":\"#7a8181ad\",\"notifications.background\":\"#f0f0f0\",\"notifications.foreground\":\"#403f53\",\"notificationLink.foreground\":\"#994cc3\",\"notifications.border\":\"#cccccc\",\"notificationCenter.border\":\"#cccccc\",\"notificationToast.border\":\"#cccccc\",\"notificationCenterHeader.foreground\":\"#403f53\",\"notificationCenterHeader.background\":\"#f0f0f0\",\"input.border\":\"#d9d9d9\",\"progressBar.background\":\"#2aa298\",\"list.inactiveSelectionBackground\":\"#e0e7ea\",\"list.inactiveSelectionForeground\":\"#403f53\",\"list.focusBackground\":\"#d3e8f8\",\"list.hoverBackground\":\"#d3e8f8\",\"list.focusForeground\":\"#403f53\",\"list.hoverForeground\":\"#403f53\",\"list.highlightForeground\":\"#403f53\",\"list.errorForeground\":\"#e64d49\",\"list.warningForeground\":\"#daaa01\",\"activityBar.background\":\"#f0f0f0\",\"activityBar.foreground\":\"#403f53\",\"activityBar.dropBackground\":\"#d0d0d0\",\"activityBarBadge.background\":\"#403f53\",\"activityBarBadge.foreground\":\"#f0f0f0\",\"activityBar.border\":\"#f0f0f0\",\"sideBar.background\":\"#f0f0f0\",\"sideBar.foreground\":\"#403f53\",\"sideBarTitle.foreground\":\"#403f53\",\"sideBar.border\":\"#f0f0f0\",\"editorGroup.background\":\"#f6f6f6\",\"editorCursor.foreground\":\"#90a7b2\",\"editor.wordHighlightBackground\":\"#339cec33\",\"editor.wordHighlightStrongBackground\":\"#007dd659\",\"editor.lineHighlightBackground\":\"#f0f0f0\",\"editor.rangeHighlightBackground\":\"#7497a633\",\"editorWhitespace.foreground\":\"#d9d9d9\",\"editorIndentGuide.background\":\"#d9d9d9\",\"editorCodeLens.foreground\":\"#403f53\",\"editorBracketMatch.background\":\"#d3e8f8\",\"editorBracketMatch.border\":\"#2aa298\",\"editorError.border\":\"#fbfbfb\",\"editorWarning.border\":\"#daaa01\",\"editorGutter.addedBackground\":\"#49d0c5\",\"editorGutter.modifiedBackground\":\"#6fbef6\",\"editorGutter.deletedBackground\":\"#f76e6e\",\"editorRuler.foreground\":\"#d9d9d9\",\"editorOverviewRuler.errorForeground\":\"#e64d49\",\"editorOverviewRuler.warningForeground\":\"#daaa01\",\"editorSuggestWidget.background\":\"#f0f0f0\",\"editorSuggestWidget.foreground\":\"#403f53\",\"editorSuggestWidget.highlightForeground\":\"#403f53\",\"editorSuggestWidget.selectedBackground\":\"#d3e8f8\",\"editorSuggestWidget.border\":\"#d9d9d9\",\"debugExceptionWidget.background\":\"#f0f0f0\",\"debugExceptionWidget.border\":\"#d9d9d9\",\"editorMarkerNavigation.background\":\"#d0d0d0\",\"editorMarkerNavigationError.background\":\"#f76e6e\",\"editorMarkerNavigationWarning.background\":\"#daaa01\",\"debugToolBar.background\":\"#f0f0f0\",\"extensionButton.prominentBackground\":\"#2aa298\",\"extensionButton.prominentForeground\":\"#f0f0f0\",\"statusBar.background\":\"#f0f0f0\",\"statusBar.border\":\"#f0f0f0\",\"statusBar.debuggingBackground\":\"#f0f0f0\",\"statusBar.debuggingForeground\":\"#403f53\",\"statusBar.foreground\":\"#403f53\",\"statusBar.noFolderBackground\":\"#f0f0f0\",\"statusBar.noFolderForeground\":\"#403f53\",\"peekView.border\":\"#d9d9d9\",\"peekViewEditor.background\":\"#f6f6f6\",\"peekViewEditorGutter.background\":\"#f6f6f6\",\"peekViewEditor.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.background\":\"#f0f0f0\",\"peekViewResult.fileForeground\":\"#403f53\",\"peekViewResult.lineForeground\":\"#403f53\",\"peekViewResult.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.selectionBackground\":\"#e0e7ea\",\"peekViewResult.selectionForeground\":\"#403f53\",\"peekViewTitle.background\":\"#f0f0f0\",\"peekViewTitleLabel.foreground\":\"#403f53\",\"peekViewTitleDescription.foreground\":\"#403f53\",\"terminal.foreground\":\"#403f53\"},\"fg\":\"#403f53\",\"bg\":\"#f6f7f9\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#556484\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#ae3c3afd\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#403f53\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#5f636f\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#3a688f\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"foreground\":\"#bb2060\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#5d6376\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#58656a\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#646464\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#286d70\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword Operator Comparison, imports, returns and Keyword Operator Ruby\",\"scope\":[\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.module.ruby\",\"keyword.control.class.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Control Conditional\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"foreground\":\"#486e26\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#a64348\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#186e73\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#984e4d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#3e697c\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#555ea2\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#111111\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#7c5686\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Symbols\",\"scope\":[\"constant.language.symbol.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#954f5a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#403f53\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\",\"variable.other.property.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#76578b\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-7)\",\"terminalBackground\":\"var(--sl-color-gray-7)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-7)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#0000001a\",\"markBorderColor\":\"#00000055\"}}}],\"defaultLocale\":\"en\",\"cascadeLayer\":\"starlight.components\",\"styleOverrides\":{\"borderRadius\":\"0px\",\"borderWidth\":\"1px\",\"codePaddingBlock\":\"0.75rem\",\"codePaddingInline\":\"1rem\",\"codeFontFamily\":\"var(--__sl-font-mono)\",\"codeFontSize\":\"var(--sl-text-code)\",\"codeLineHeight\":\"var(--sl-line-height)\",\"uiFontFamily\":\"var(--__sl-font)\",\"textMarkers\":{\"lineDiffIndicatorMarginLeft\":\"0.25rem\",\"defaultChroma\":\"45\",\"backgroundOpacity\":\"60%\"}},\"plugins\":[{\"name\":\"Starlight Plugin\",\"hooks\":{}},{\"name\":\"astro-expressive-code\",\"hooks\":{}}]}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[\"en\"],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","docs",["Map",11,12,25,26],"index",{"id":11,"data":13,"body":22,"filePath":23,"digest":24,"deferredRender":16},{"title":14,"description":15,"editUrl":16,"head":17,"template":18,"sidebar":19,"pagefind":16,"draft":20},"Welcome","Get started building your docs site with Starlight.",true,[],"splash",{"hidden":20,"attrs":21},false,{},"import { Card, CardGrid } from '@astrojs/starlight/components';\n\n## Next steps\n\n\u003CCardGrid stagger>\n\t\u003CCard title=\"Update content\" icon=\"pencil\">\n\t\tEdit `src/content/docs/index.mdx` to see this page change.\n\t\u003C/Card>\n\t\u003CCard title=\"Change page layout\" icon=\"document\">\n\t\tDelete `template: splash` in `src/content/docs/index.mdx` to display a\n\t\tsidebar on this page.\n\t\u003C/Card>\n\t\u003CCard title=\"Add new content\" icon=\"add-document\">\n\t\tAdd Markdown or MDX files to `src/content/docs` to create new pages.\n\t\u003C/Card>\n\t\u003CCard title=\"Configure your site\" icon=\"setting\">\n\t\tEdit your `sidebar` and other config in `astro.config.mjs`.\n\t\u003C/Card>\n\t\u003CCard title=\"Read the docs\" icon=\"open-book\">\n\t\tLearn more in [the Starlight Docs](https://starlight.astro.build/).\n\t\u003C/Card>\n\u003C/CardGrid>","src/content/docs/index.mdx","576d77b3cc95d789","guides/example",{"id":25,"data":27,"body":34,"filePath":35,"digest":36,"rendered":37},{"title":28,"description":29,"editUrl":16,"head":30,"template":31,"sidebar":32,"pagefind":16,"draft":20},"Example Guide","A guide in my new Starlight docs site.",[],"doc",{"hidden":20,"attrs":33},{},"# Hello","src/content/docs/guides/example.md","46be7f85f8f2de8e",{"html":38,"metadata":39},"\u003Cdiv class=\"sl-heading-wrapper level-h1\">\u003Ch1 id=\"hello\">Hello\u003C/h1>\u003Ca class=\"sl-anchor-link\" href=\"#hello\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\">\u003Cpath fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\">Section titled “Hello”\u003C/span>\u003C/a>\u003C/div>",{"headings":40,"localImagePaths":45,"remoteImagePaths":46,"frontmatter":47,"imagePaths":48},[41],{"depth":42,"slug":43,"text":44},1,"hello","Hello",[],[],{"title":28,"description":29},[]] \ No newline at end of file diff --git a/.astro/settings.json b/.astro/settings.json new file mode 100644 index 0000000000..c085660bc8 --- /dev/null +++ b/.astro/settings.json @@ -0,0 +1,5 @@ +{ + "_variables": { + "lastUpdateCheck": 1769728146579 + } +} \ No newline at end of file diff --git a/.astro/types.d.ts b/.astro/types.d.ts new file mode 100644 index 0000000000..03d7cc43f1 --- /dev/null +++ b/.astro/types.d.ts @@ -0,0 +1,2 @@ +/// +/// \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000000..820c59b245 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,26 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + starlight({ + title: 'Feature-Sliced Design', + social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }], + sidebar: [ + { + label: 'Guides', + items: [ + // Each item here is one entry in the navigation menu. + { label: 'Example Guide', slug: 'guides/example' }, + ], + }, + { + label: 'Reference', + autogenerate: { directory: 'reference' }, + }, + ], + }), + ], +}); diff --git a/package.json b/package.json index d94dc1c1ef..a429d2288e 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "2.1.0", "private": true, "scripts": { + "dev": "astro dev", "docusaurus": "docusaurus", "start": "docusaurus start", "start:ru": "docusaurus start --locale ru", @@ -49,6 +50,7 @@ "react-fast-marquee": "^1.6.5" }, "devDependencies": { + "@astrojs/starlight": "^0.37.4", "@babel/eslint-parser": "^7.28.4", "@docusaurus/module-type-aliases": "^3.9.1", "@docusaurus/theme-classic": "^3.9.1", @@ -63,6 +65,7 @@ "@types/react-dom": "^19.1.9", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", + "astro": "^5.16.16", "docusaurus-plugin-sass": "^0.2.6", "eslint": "^7.32.0", "eslint-config-prettier": "^9.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b59b35c602..9c6060fc4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,22 +13,22 @@ importers: version: 6.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/core': specifier: ^3.9.1 - version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/faster': specifier: ^3.9.1 version: 3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)) '@docusaurus/plugin-client-redirects': specifier: ^3.9.1 - version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/plugin-content-docs': specifier: ^3.9.1 - version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/plugin-ideal-image': specifier: ^3.9.1 - version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/preset-classic': specifier: ^3.9.1 - version: 3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2) + version: 3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2) '@fontsource-variable/overpass': specifier: ^5.2.8 version: 5.2.8 @@ -37,7 +37,7 @@ importers: version: 3.1.1(@types/react@19.1.13)(react@19.1.1) '@signalwire/docusaurus-plugin-llms-txt': specifier: ^1.2.2 - version: 1.2.2(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)) + version: 1.2.2(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -66,6 +66,9 @@ importers: specifier: ^1.6.5 version: 1.6.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) devDependencies: + '@astrojs/starlight': + specifier: ^0.37.4 + version: 0.37.4(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)) '@babel/eslint-parser': specifier: ^7.28.4 version: 7.28.4(@babel/core@7.28.4)(eslint@7.32.0) @@ -74,7 +77,7 @@ importers: version: 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/theme-classic': specifier: ^3.9.1 - version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/tsconfig': specifier: ^3.9.1 version: 3.9.1 @@ -108,9 +111,12 @@ importers: '@typescript-eslint/parser': specifier: ^6.21.0 version: 6.21.0(eslint@7.32.0)(typescript@5.9.2) + astro: + specifier: ^5.16.16 + version: 5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2) docusaurus-plugin-sass: specifier: ^0.2.6 - version: 0.2.6(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@rspack/core@1.5.7)(sass@1.93.2)(webpack@5.101.3(@swc/core@1.13.19)) + version: 0.2.6(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@rspack/core@1.5.7)(sass@1.93.2)(webpack@5.101.3(@swc/core@1.13.19)) eslint: specifier: ^7.32.0 version: 7.32.0 @@ -260,6 +266,37 @@ packages: react: '>=16.0.0' react-dom: '>=16.0.0' + '@astrojs/compiler@2.13.0': + resolution: {integrity: sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==} + + '@astrojs/internal-helpers@0.7.5': + resolution: {integrity: sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==} + + '@astrojs/markdown-remark@6.3.10': + resolution: {integrity: sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==} + + '@astrojs/mdx@4.3.13': + resolution: {integrity: sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + astro: ^5.0.0 + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/sitemap@3.7.0': + resolution: {integrity: sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA==} + + '@astrojs/starlight@0.37.4': + resolution: {integrity: sha512-ygPGDgRd9nCcNgaYMNN7UeAMAkDOR1ibv3ps3xEz+cuvKG3CRLd19UwdB+Gyz1tbkyfjPWPkFKNhLwNybro8Tw==} + peerDependencies: + astro: ^5.5.0 + + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@babel/code-frame@7.12.11': resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} @@ -361,6 +398,10 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -382,6 +423,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.6': + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} engines: {node: '>=6.9.0'} @@ -844,6 +890,10 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.6': + resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + engines: {node: '>=6.9.0'} + '@cacheable/memoize@2.0.2': resolution: {integrity: sha512-wPrr7FUiq3Qt4yQyda2/NcOLTJCFcQSU3Am2adP+WLy+sz93/fKTokVTHmtz+rjp4PD7ee0AEOeRVNN6IvIfsg==} @@ -853,6 +903,10 @@ packages: '@cacheable/utils@2.0.2': resolution: {integrity: sha512-JTFM3raFhVv8LH95T7YnZbf2YoE9wEtkPPStuRF9a6ExZ103hFvs+QyCuYJ6r0hA9wRtbzgZtwUCoDWxssZd4Q==} + '@capsizecss/unpack@4.0.0': + resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} + engines: {node: '>=18'} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -1133,6 +1187,10 @@ packages: peerDependencies: postcss: ^8.4 + '@ctrl/tinycolor@4.2.0': + resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} + engines: {node: '>=14'} + '@discoveryjs/json-ext@0.5.7': resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} @@ -1376,9 +1434,168 @@ packages: '@emnapi/runtime@1.5.0': resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.0': resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1408,6 +1625,18 @@ packages: resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} engines: {node: ^10.12.0 || >=12.0.0} + '@expressive-code/core@0.41.6': + resolution: {integrity: sha512-FvJQP+hG0jWi/FLBSmvHInDqWR7jNANp9PUDjdMqSshHb0y7sxx3vHuoOr6SgXjWw+MGLqorZyPQ0aAlHEok6g==} + + '@expressive-code/plugin-frames@0.41.6': + resolution: {integrity: sha512-d+hkSYXIQot6fmYnOmWAM+7TNWRv/dhfjMsNq+mIZz8Tb4mPHOcgcfZeEM5dV9TDL0ioQNvtcqQNuzA1sRPjxg==} + + '@expressive-code/plugin-shiki@0.41.6': + resolution: {integrity: sha512-Y6zmKBmsIUtWTzdefqlzm/h9Zz0Rc4gNdt2GTIH7fhHH2I9+lDYCa27BDwuBhjqcos6uK81Aca9dLUC4wzN+ng==} + + '@expressive-code/plugin-text-markers@0.41.6': + resolution: {integrity: sha512-PBFa1wGyYzRExMDzBmAWC6/kdfG1oLn4pLpBeTfIRrALPjcGA/59HP3e7q9J0Smk4pC7U+lWkA2LHR8FYV8U7Q==} + '@fontsource-variable/overpass@5.2.8': resolution: {integrity: sha512-7/gP2f9ylVFYEhdyeHl6JSEcmNQ5QBdDs5BuT4V+FqSvkk9XjwdikmtTJuNTRWevMAhBkTUWf/dm68G2SbMdjQ==} @@ -1426,6 +1655,159 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} deprecated: Use @eslint/object-schema instead + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1548,6 +1930,42 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@pagefind/darwin-arm64@1.4.0': + resolution: {integrity: sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==} + cpu: [arm64] + os: [darwin] + + '@pagefind/darwin-x64@1.4.0': + resolution: {integrity: sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==} + cpu: [x64] + os: [darwin] + + '@pagefind/default-ui@1.4.0': + resolution: {integrity: sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ==} + + '@pagefind/freebsd-x64@1.4.0': + resolution: {integrity: sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==} + cpu: [x64] + os: [freebsd] + + '@pagefind/linux-arm64@1.4.0': + resolution: {integrity: sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==} + cpu: [arm64] + os: [linux] + + '@pagefind/linux-x64@1.4.0': + resolution: {integrity: sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==} + cpu: [x64] + os: [linux] + + '@pagefind/windows-x64@1.4.0': + resolution: {integrity: sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==} + cpu: [x64] + os: [win32] + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -1657,6 +2075,153 @@ packages: react: '>=18.0.0' react-dom: '>=18.0.0' + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.57.0': + resolution: {integrity: sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.57.0': + resolution: {integrity: sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.57.0': + resolution: {integrity: sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.57.0': + resolution: {integrity: sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.57.0': + resolution: {integrity: sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.57.0': + resolution: {integrity: sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.57.0': + resolution: {integrity: sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.57.0': + resolution: {integrity: sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.57.0': + resolution: {integrity: sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.57.0': + resolution: {integrity: sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.57.0': + resolution: {integrity: sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.57.0': + resolution: {integrity: sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.57.0': + resolution: {integrity: sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.57.0': + resolution: {integrity: sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.57.0': + resolution: {integrity: sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.57.0': + resolution: {integrity: sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.57.0': + resolution: {integrity: sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.57.0': + resolution: {integrity: sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.57.0': + resolution: {integrity: sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.57.0': + resolution: {integrity: sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.57.0': + resolution: {integrity: sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.57.0': + resolution: {integrity: sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.57.0': + resolution: {integrity: sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.57.0': + resolution: {integrity: sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.57.0': + resolution: {integrity: sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==} + cpu: [x64] + os: [win32] + '@rspack/binding-darwin-arm64@1.5.7': resolution: {integrity: sha512-prQ/vgJxOPdlYiR4gVeOEKofTCEOu70JQIQApqFnw8lKM7rd9ag8ogDNqmc2L/GGXGHLAqds28oeKXRlzYf7+Q==} cpu: [arm64] @@ -1729,6 +2294,27 @@ packages: '@rushstack/eslint-patch@1.0.6': resolution: {integrity: sha512-Myxw//kzromB9yWgS8qYGuGVf91oBUUJpNvy5eM50sqvmKLbKjwLxohJnkWGTeeI9v9IBMtPLxz5Gc60FIfvCA==} + '@shikijs/core@3.21.0': + resolution: {integrity: sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA==} + + '@shikijs/engine-javascript@3.21.0': + resolution: {integrity: sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ==} + + '@shikijs/engine-oniguruma@3.21.0': + resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==} + + '@shikijs/langs@3.21.0': + resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==} + + '@shikijs/themes@3.21.0': + resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==} + + '@shikijs/types@3.21.0': + resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -2071,6 +2657,9 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2095,6 +2684,9 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} @@ -2406,6 +2998,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -2417,6 +3013,9 @@ packages: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -2441,6 +3040,16 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + astro-expressive-code@0.41.6: + resolution: {integrity: sha512-l47tb1uhmVIebHUkw+HEPtU/av0G4O8Q34g2cbkPvC7/e9ZhANcjUUciKt9Hp6gSVDdIuXBBLwJQn2LkeGMOAw==} + peerDependencies: + astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta + + astro@5.16.16: + resolution: {integrity: sha512-MFlFvQ84ixaHyqB3uGwMhNHdBLZ3vHawyq3PqzQS2TNWiNfQrxp5ag6S3lX+Cvnh0MUcXX+UnJBPMBHjP1/1ZQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -2456,6 +3065,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + b4a@1.7.3: resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} peerDependencies: @@ -2531,6 +3144,9 @@ packages: bare-url@2.2.2: resolution: {integrity: sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA==} + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -2544,6 +3160,9 @@ packages: bcp-47-match@2.0.3: resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} + bcp-47@2.1.0: + resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -2572,6 +3191,10 @@ packages: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -2647,6 +3270,10 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} @@ -2699,6 +3326,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -2710,6 +3341,10 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -2781,6 +3416,10 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2796,6 +3435,9 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} @@ -2844,6 +3486,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -2851,6 +3496,10 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + copy-webpack-plugin@11.0.0: resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} engines: {node: '>= 14.15.0'} @@ -2891,6 +3540,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} @@ -3110,6 +3762,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -3122,6 +3777,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -3135,6 +3793,10 @@ packages: resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==} engines: {node: '>=8'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} @@ -3143,9 +3805,20 @@ packages: engines: {node: '>= 4.0.0'} hasBin: true + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + + devalue@5.6.2: + resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff@8.0.3: + resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + engines: {node: '>=0.3.1'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -3154,6 +3827,9 @@ packages: resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} hasBin: true + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dns-packet@5.6.1: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} @@ -3205,6 +3881,10 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -3221,6 +3901,9 @@ packages: electron-to-chromium@1.5.224: resolution: {integrity: sha512-kWAoUu/bwzvnhpdZSIc6KUyvkI1rbRXMT0Eq8pKReyOyaPZcctMli+EgvcN1PAvwVc7Tdo4Fxi2PsLNDU05mdg==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3311,6 +3994,11 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -3481,6 +4169,9 @@ packages: estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -3503,6 +4194,9 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events-universal@1.0.1: resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} @@ -3526,6 +4220,9 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} + expressive-code@0.41.6: + resolution: {integrity: sha512-W/5+IQbrpCIM5KGLjO35wlp1NCwDOOVQb+PAvzEoGkW1xjGM807ZGfBKptNWH6UECvt6qgmLyWolCMYKh7eQmA==} + extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -3566,6 +4263,15 @@ packages: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} engines: {node: '>=0.8.0'} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + feed@4.2.2: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} @@ -3625,6 +4331,10 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -3634,6 +4344,13 @@ packages: debug: optional: true + fontace@0.4.0: + resolution: {integrity: sha512-moThBCItUe2bjZip5PF/iZClpKHGLwMvR79Kp8XpGRBrvoRSnySN4VcILdv3/MJzbhvUA5WeiUXF5o538m5fvg==} + + fontkitten@1.0.2: + resolution: {integrity: sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q==} + engines: {node: '>=20'} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -3689,6 +4406,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -3714,6 +4435,9 @@ packages: github-slugger@1.5.0: resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -3791,6 +4515,9 @@ packages: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} + h3@1.15.5: + resolution: {integrity: sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==} + handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} @@ -3836,6 +4563,9 @@ packages: hast-util-embedded@3.0.0: resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + hast-util-format@1.1.0: + resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} + hast-util-from-html@2.0.3: resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} @@ -3915,6 +4645,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + html-minifier-terser@6.1.0: resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} engines: {node: '>=12'} @@ -3944,6 +4677,9 @@ packages: webpack: optional: true + html-whitespace-sensitive-tag-names@3.0.1: + resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} @@ -3992,6 +4728,9 @@ packages: resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} engines: {node: '>=10.18'} + i18next@23.16.8: + resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -4033,6 +4772,9 @@ packages: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + import-modules@2.1.0: resolution: {integrity: sha512-8HEWcnkbGpovH9yInoisxaSoIg9Brbul+Ju3Kqe2UsYDUBJD/iQjSgEj0zPcTDPKfPp2fs5xlv1i+JSye/m1/A==} engines: {node: '>=8'} @@ -4084,6 +4826,9 @@ packages: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -4349,6 +5094,10 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -4405,6 +5154,10 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + known-css-properties@0.36.0: resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} @@ -4560,9 +5313,19 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lru-cache@11.2.5: + resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -4585,6 +5348,9 @@ packages: mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + mdast-util-directive@3.1.0: resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} @@ -4908,6 +5674,13 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -4925,10 +5698,16 @@ packages: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + node-releases@2.0.21: resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} @@ -4998,6 +5777,12 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -5013,6 +5798,12 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.4: + resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + open@10.2.0: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} @@ -5053,6 +5844,10 @@ packages: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@6.2.0: + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} + p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} @@ -5077,6 +5872,10 @@ packages: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} + p-queue@8.1.1: + resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} + engines: {node: '>=18'} + p-retry@6.2.1: resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} engines: {node: '>=16.17'} @@ -5085,6 +5884,10 @@ packages: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + p-try@1.0.0: resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} engines: {node: '>=4'} @@ -5097,6 +5900,13 @@ packages: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + + pagefind@1.4.0: + resolution: {integrity: sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==} + hasBin: true + param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} @@ -5115,6 +5925,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + parse-numeric-range@1.3.0: resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} @@ -5178,6 +5991,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + piccolore@0.1.3: + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -5185,6 +6001,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} @@ -5433,6 +6253,12 @@ packages: peerDependencies: postcss: ^8.1.0 + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + postcss-nesting@13.0.2: resolution: {integrity: sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==} engines: {node: '>=18'} @@ -5707,6 +6533,9 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -5810,6 +6639,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -5835,6 +6668,15 @@ packages: regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -5866,6 +6708,12 @@ packages: resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true + rehype-expressive-code@0.41.6: + resolution: {integrity: sha512-aBMX8kxPtjmDSFUdZlAWJkMvsQ4ZMASfee90JWIAV8tweltXLzkWC3q++43ToTelI8ac5iC0B3/S/Cl4Ql1y2g==} + + rehype-format@5.0.1: + resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} + rehype-minify-whitespace@6.0.2: resolution: {integrity: sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==} @@ -5881,6 +6729,12 @@ packages: rehype-remark@10.0.1: resolution: {integrity: sha512-EmDndlb5NVwXGfUa4c9GPK+lXeItTilLhE6ADSaQuHr4JUlKw9MidzGzx4HpqZrNCt6vnHmEifXQiiA+CEnjYQ==} + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + relateurl@0.2.7: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} @@ -5907,6 +6761,10 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} @@ -5960,6 +6818,18 @@ packages: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} engines: {node: '>=14.16'} + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -5973,6 +6843,11 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rollup@4.57.0: + resolution: {integrity: sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rtlcss@4.3.0: resolution: {integrity: sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==} engines: {node: '>=12.0.0'} @@ -6083,6 +6958,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -6130,6 +7010,10 @@ packages: resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} engines: {node: '>=14.15.0'} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -6142,6 +7026,9 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} + shiki@3.21.0: + resolution: {integrity: sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w==} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -6186,6 +7073,11 @@ packages: engines: {node: '>=12.0.0', npm: '>=5.6.0'} hasBin: true + sitemap@8.0.2: + resolution: {integrity: sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + skin-tone@2.0.0: resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} engines: {node: '>=8'} @@ -6202,6 +7094,10 @@ packages: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} + smol-toml@1.6.0: + resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + engines: {node: '>= 18'} + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -6271,6 +7167,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + streamx@2.23.0: resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} @@ -6282,6 +7181,10 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -6435,6 +7338,11 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + svgo@4.0.0: + resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==} + engines: {node: '>=16'} + hasBin: true + swc-loader@0.2.6: resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==} peerDependencies: @@ -6507,12 +7415,23 @@ packages: thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinypool@1.1.1: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} @@ -6550,6 +7469,16 @@ packages: peerDependencies: typescript: '>=4.2.0' + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -6587,6 +7516,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -6615,10 +7548,19 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -6645,6 +7587,9 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unifont@0.7.3: + resolution: {integrity: sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA==} + unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} @@ -6655,18 +7600,30 @@ packages: unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + unist-util-position-from-estree@2.0.0: resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -6678,6 +7635,68 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unstorage@1.17.4: + resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -6746,6 +7765,54 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + watchpack@2.4.4: resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} @@ -6831,6 +7898,10 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + which-typed-array@1.1.19: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} @@ -6848,6 +7919,10 @@ packages: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} @@ -6863,6 +7938,10 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -6909,13 +7988,42 @@ packages: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} hasBin: true + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + yocto-queue@1.2.1: resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} engines: {node: '>=12.20'} + yocto-spinner@0.2.3: + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + peerDependencies: + zod: ^3.25 || ^4 + + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.11: resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} @@ -7067,14 +8175,119 @@ snapshots: '@ant-design/icons-svg@4.4.2': {} - '@ant-design/icons@6.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@ant-design/icons@6.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/colors': 8.0.0 + '@ant-design/icons-svg': 4.4.2 + '@rc-component/util': 1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@astrojs/compiler@2.13.0': {} + + '@astrojs/internal-helpers@0.7.5': {} + + '@astrojs/markdown-remark@6.3.10': + dependencies: + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.21.0 + smol-toml: 1.6.0 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/mdx@4.3.13(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2))': + dependencies: + '@astrojs/markdown-remark': 6.3.10 + '@mdx-js/mdx': 3.1.1 + acorn: 8.15.0 + astro: 5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2) + es-module-lexer: 1.7.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.5 + piccolore: 0.1.3 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-smartypants: 3.0.2 + source-map: 0.7.6 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.3.0': + dependencies: + prismjs: 1.30.0 + + '@astrojs/sitemap@3.7.0': + dependencies: + sitemap: 8.0.2 + stream-replace-string: 2.0.0 + zod: 3.25.76 + + '@astrojs/starlight@0.37.4(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2))': + dependencies: + '@astrojs/markdown-remark': 6.3.10 + '@astrojs/mdx': 4.3.13(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)) + '@astrojs/sitemap': 3.7.0 + '@pagefind/default-ui': 1.4.0 + '@types/hast': 3.0.4 + '@types/js-yaml': 4.0.9 + '@types/mdast': 4.0.4 + astro: 5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2) + astro-expressive-code: 0.41.6(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)) + bcp-47: 2.1.0 + hast-util-from-html: 2.0.3 + hast-util-select: 6.0.4 + hast-util-to-string: 3.0.1 + hastscript: 9.0.1 + i18next: 23.16.8 + js-yaml: 4.1.0 + klona: 2.0.6 + magic-string: 0.30.21 + mdast-util-directive: 3.1.0 + mdast-util-to-markdown: 2.1.2 + mdast-util-to-string: 4.0.0 + pagefind: 1.4.0 + rehype: 13.0.2 + rehype-format: 5.0.1 + remark-directive: 3.0.1 + ultrahtml: 1.6.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/telemetry@3.3.0': dependencies: - '@ant-design/colors': 8.0.0 - '@ant-design/icons-svg': 4.4.2 - '@rc-component/util': 1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - classnames: 2.5.1 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + ci-info: 4.3.1 + debug: 4.4.3 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color '@babel/code-frame@7.12.11': dependencies: @@ -7227,6 +8440,8 @@ snapshots: '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.3': @@ -7253,6 +8468,10 @@ snapshots: dependencies: '@babel/types': 7.28.4 + '@babel/parser@7.28.6': + dependencies: + '@babel/types': 7.28.6 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -7848,6 +9067,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@cacheable/memoize@2.0.2': dependencies: '@cacheable/utils': 2.0.2 @@ -7862,6 +9086,10 @@ snapshots: '@cacheable/utils@2.0.2': {} + '@capsizecss/unpack@4.0.0': + dependencies: + fontkitten: 1.0.2 + '@colors/colors@1.5.0': optional: true @@ -8153,6 +9381,8 @@ snapshots: dependencies: postcss: 8.5.6 + '@ctrl/tinycolor@4.2.0': {} + '@discoveryjs/json-ext@0.5.7': {} '@docsearch/css@4.1.0': {} @@ -8200,7 +9430,7 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/bundler@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/bundler@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: '@babel/core': 7.28.4 '@docusaurus/babel': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8212,7 +9442,7 @@ snapshots: clean-css: 5.3.3 copy-webpack-plugin: 11.0.0(webpack@5.101.3(@swc/core@1.13.19)) css-loader: 6.11.0(@rspack/core@1.5.7)(webpack@5.101.3(@swc/core@1.13.19)) - css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.101.3(@swc/core@1.13.19)) + css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(lightningcss@1.30.1)(webpack@5.101.3(@swc/core@1.13.19)) cssnano: 6.1.2(postcss@8.5.6) file-loader: 6.2.0(webpack@5.101.3(@swc/core@1.13.19)) html-minifier-terser: 7.2.0 @@ -8243,10 +9473,10 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: '@docusaurus/babel': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@docusaurus/bundler': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/bundler': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 '@docusaurus/mdx-loader': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8401,9 +9631,9 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/plugin-client-redirects@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-client-redirects@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-common': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8432,13 +9662,13 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-blog@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-content-blog@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 '@docusaurus/mdx-loader': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-common': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8473,13 +9703,13 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 '@docusaurus/mdx-loader': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/module-type-aliases': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-common': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8513,9 +9743,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-pages@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-content-pages@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/mdx-loader': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8543,9 +9773,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-css-cascade-layers@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-css-cascade-layers@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-validation': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8570,9 +9800,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-debug@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-debug@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) fs-extra: 11.3.2 @@ -8598,9 +9828,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-analytics@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-google-analytics@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-validation': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 @@ -8624,9 +9854,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-gtag@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-google-gtag@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-validation': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@types/gtag.js': 0.0.12 @@ -8651,9 +9881,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-tag-manager@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-google-tag-manager@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-validation': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 @@ -8677,9 +9907,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-ideal-image@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-ideal-image@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/lqip-loader': 3.9.1(webpack@5.101.3(@swc/core@1.13.19)) '@docusaurus/responsive-loader': 1.7.1(sharp@0.32.6) '@docusaurus/theme-translations': 3.9.1 @@ -8710,9 +9940,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-sitemap@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-sitemap@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8741,9 +9971,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-svgr@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/plugin-svgr@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-validation': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8771,22 +10001,22 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/preset-classic@3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2)': - dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-content-blog': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-content-pages': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-css-cascade-layers': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-debug': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-google-analytics': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-google-gtag': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-google-tag-manager': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-sitemap': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-svgr': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/theme-classic': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@docusaurus/theme-search-algolia': 3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2) + '@docusaurus/preset-classic@3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-content-blog': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-content-pages': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-css-cascade-layers': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-debug': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-google-analytics': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-google-gtag': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-google-tag-manager': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-sitemap': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-svgr': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/theme-classic': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@docusaurus/theme-search-algolia': 3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2) '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) @@ -8822,16 +10052,16 @@ snapshots: optionalDependencies: sharp: 0.32.6 - '@docusaurus/theme-classic@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': + '@docusaurus/theme-classic@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 '@docusaurus/mdx-loader': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/module-type-aliases': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@docusaurus/plugin-content-blog': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/plugin-content-pages': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@docusaurus/plugin-content-blog': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-content-pages': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/theme-translations': 3.9.1 '@docusaurus/types': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -8869,11 +10099,11 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/theme-common@3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@docusaurus/theme-common@3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@docusaurus/mdx-loader': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/module-type-aliases': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-common': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@types/history': 4.7.11 @@ -8893,13 +10123,13 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/theme-search-algolia@3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2)': + '@docusaurus/theme-search-algolia@3.9.1(@algolia/client-search@5.38.0)(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(@types/react@19.1.13)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3)(typescript@5.9.2)': dependencies: '@docsearch/react': 4.1.0(@algolia/client-search@5.38.0)(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(search-insights@2.17.3) - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) '@docusaurus/logger': 3.9.1 - '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) - '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@docusaurus/plugin-content-docs': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.9.1(@docusaurus/plugin-content-docs@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/theme-translations': 3.9.1 '@docusaurus/utils': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@docusaurus/utils-validation': 3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -9039,11 +10269,94 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@7.32.0)': dependencies: eslint: 7.32.0 @@ -9088,6 +10401,31 @@ snapshots: transitivePeerDependencies: - supports-color + '@expressive-code/core@0.41.6': + dependencies: + '@ctrl/tinycolor': 4.2.0 + hast-util-select: 6.0.4 + hast-util-to-html: 9.0.5 + hast-util-to-text: 4.0.2 + hastscript: 9.0.1 + postcss: 8.5.6 + postcss-nested: 6.2.0(postcss@8.5.6) + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + + '@expressive-code/plugin-frames@0.41.6': + dependencies: + '@expressive-code/core': 0.41.6 + + '@expressive-code/plugin-shiki@0.41.6': + dependencies: + '@expressive-code/core': 0.41.6 + shiki: 3.21.0 + + '@expressive-code/plugin-text-markers@0.41.6': + dependencies: + '@expressive-code/core': 0.41.6 + '@fontsource-variable/overpass@5.2.8': {} '@hapi/hoek@9.3.0': {} @@ -9106,6 +10444,103 @@ snapshots: '@humanwhocodes/object-schema@1.2.1': {} + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 @@ -9272,6 +10707,28 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@oslojs/encoding@1.1.0': {} + + '@pagefind/darwin-arm64@1.4.0': + optional: true + + '@pagefind/darwin-x64@1.4.0': + optional: true + + '@pagefind/default-ui@1.4.0': {} + + '@pagefind/freebsd-x64@1.4.0': + optional: true + + '@pagefind/linux-arm64@1.4.0': + optional: true + + '@pagefind/linux-x64@1.4.0': + optional: true + + '@pagefind/windows-x64@1.4.0': + optional: true + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -9354,6 +10811,89 @@ snapshots: react-dom: 19.1.1(react@19.1.1) react-is: 18.3.1 + '@rollup/pluginutils@5.3.0(rollup@4.57.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.57.0 + + '@rollup/rollup-android-arm-eabi@4.57.0': + optional: true + + '@rollup/rollup-android-arm64@4.57.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.57.0': + optional: true + + '@rollup/rollup-darwin-x64@4.57.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.57.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.57.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.57.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.57.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.57.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.57.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.57.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.57.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.57.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.57.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.57.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.57.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.57.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.57.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.57.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.57.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.57.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.57.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.57.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.57.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.57.0': + optional: true + '@rspack/binding-darwin-arm64@1.5.7': optional: true @@ -9409,6 +10949,39 @@ snapshots: '@rushstack/eslint-patch@1.0.6': {} + '@shikijs/core@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 + + '@shikijs/engine-oniguruma@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + + '@shikijs/themes@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + + '@shikijs/types@3.21.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@sideway/address@4.1.5': dependencies: '@hapi/hoek': 9.3.0 @@ -9417,9 +10990,9 @@ snapshots: '@sideway/pinpoint@2.0.0': {} - '@signalwire/docusaurus-plugin-llms-txt@1.2.2(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))': + '@signalwire/docusaurus-plugin-llms-txt@1.2.2(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))': dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) fs-extra: 11.3.2 hast-util-select: 6.0.4 hast-util-to-html: 9.0.5 @@ -9742,6 +11315,8 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/js-yaml@4.0.9': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -9762,6 +11337,10 @@ snapshots: '@types/ms@2.1.0': {} + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + '@types/node-forge@1.3.14': dependencies: '@types/node': 22.18.6 @@ -10142,6 +11721,8 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.2: {} + array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -10160,6 +11741,8 @@ snapshots: is-string: 1.1.1 math-intrinsics: 1.1.0 + array-iterate@2.0.1: {} + array-union@2.1.0: {} array.prototype.flat@1.3.3: @@ -10190,6 +11773,113 @@ snapshots: astring@1.9.0: {} + astro-expressive-code@0.41.6(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)): + dependencies: + astro: 5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2) + rehype-expressive-code: 0.41.6 + + astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2): + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/markdown-remark': 6.3.10 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 4.0.0 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.57.0) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.3.1 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.1.1 + cssesc: 3.0.0 + debug: 4.4.3 + deterministic-object-hash: 2.0.2 + devalue: 5.6.2 + diff: 8.0.3 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.12 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.4.0 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + magic-string: 0.30.21 + magicast: 0.5.1 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.6.0 + piccolore: 0.1.3 + picomatch: 4.0.3 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.3 + shiki: 3.21.0 + smol-toml: 1.6.0 + svgo: 4.0.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tsconfck: 3.1.6(typescript@5.9.2) + ultrahtml: 1.6.0 + unifont: 0.7.3 + unist-util-visit: 5.0.0 + unstorage: 1.17.4 + vfile: 6.0.3 + vite: 6.4.1(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0) + vitefu: 1.1.1(vite@6.4.1(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.2)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + async-function@1.0.0: {} autoprefixer@10.4.21(postcss@8.5.6): @@ -10206,6 +11896,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axobject-query@4.1.0: {} + b4a@1.7.3: {} babel-loader@9.2.1(@babel/core@7.28.4)(webpack@5.101.3(@swc/core@1.13.19)): @@ -10284,6 +11976,8 @@ snapshots: bare-path: 3.0.0 optional: true + base-64@1.0.0: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.8.7: {} @@ -10292,6 +11986,12 @@ snapshots: bcp-47-match@2.0.3: {} + bcp-47@2.1.0: + dependencies: + is-alphabetical: 2.0.1 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + big.js@5.2.2: {} binary-extensions@2.3.0: {} @@ -10348,6 +12048,17 @@ snapshots: widest-line: 4.0.1 wrap-ansi: 8.1.0 + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -10434,6 +12145,8 @@ snapshots: camelcase@7.0.1: {} + camelcase@8.0.0: {} + caniuse-api@3.0.0: dependencies: browserslist: 4.26.2 @@ -10503,12 +12216,18 @@ snapshots: dependencies: readdirp: 4.1.2 + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + chownr@1.1.4: {} chrome-trace-event@1.0.4: {} ci-info@3.9.0: {} + ci-info@4.3.1: {} + classnames@2.5.1: {} clean-css@5.3.3: @@ -10571,6 +12290,8 @@ snapshots: commander@10.0.1: {} + commander@11.1.0: {} + commander@2.20.3: {} commander@5.1.0: {} @@ -10579,6 +12300,8 @@ snapshots: commander@8.3.0: {} + common-ancestor-path@1.0.1: {} + common-path-prefix@3.0.0: {} compressible@2.0.18: @@ -10631,10 +12354,14 @@ snapshots: convert-source-map@2.0.0: {} + cookie-es@1.2.2: {} + cookie-signature@1.0.6: {} cookie@0.7.1: {} + cookie@1.1.1: {} + copy-webpack-plugin@11.0.0(webpack@5.101.3(@swc/core@1.13.19)): dependencies: fast-glob: 3.3.3 @@ -10679,6 +12406,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 @@ -10715,7 +12446,7 @@ snapshots: '@rspack/core': 1.5.7 webpack: 5.101.3(@swc/core@1.13.19) - css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.101.3(@swc/core@1.13.19)): + css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(lightningcss@1.30.1)(webpack@5.101.3(@swc/core@1.13.19)): dependencies: '@jridgewell/trace-mapping': 0.3.31 cssnano: 6.1.2(postcss@8.5.6) @@ -10726,6 +12457,7 @@ snapshots: webpack: 5.101.3(@swc/core@1.13.19) optionalDependencies: clean-css: 5.3.3 + lightningcss: 1.30.1 css-prefers-color-scheme@10.0.0(postcss@8.5.6): dependencies: @@ -10902,12 +12634,16 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.4: {} + depd@1.1.2: {} depd@2.0.0: {} dequal@2.0.3: {} + destr@2.0.5: {} + destroy@1.2.0: {} detect-libc@1.0.3: @@ -10915,6 +12651,9 @@ snapshots: detect-libc@2.1.1: {} + detect-libc@2.1.2: + optional: true + detect-node@2.1.0: {} detect-port@1.6.1: @@ -10924,16 +12663,26 @@ snapshots: transitivePeerDependencies: - supports-color + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.6.2: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 + diff@8.0.3: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 direction@2.0.1: {} + dlv@1.1.3: {} + dns-packet@5.6.1: dependencies: '@leichtgewicht/ip-codec': 2.0.5 @@ -10946,9 +12695,9 @@ snapshots: dependencies: esutils: 2.0.3 - docusaurus-plugin-sass@0.2.6(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@rspack/core@1.5.7)(sass@1.93.2)(webpack@5.101.3(@swc/core@1.13.19)): + docusaurus-plugin-sass@0.2.6(@docusaurus/core@3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2))(@rspack/core@1.5.7)(sass@1.93.2)(webpack@5.101.3(@swc/core@1.13.19)): dependencies: - '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + '@docusaurus/core': 3.9.1(@docusaurus/faster@3.9.1(@docusaurus/types@3.9.1(@swc/core@1.13.19)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@19.1.1))(@rspack/core@1.5.7)(@swc/core@1.13.19)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2) sass: 1.93.2 sass-loader: 16.0.5(@rspack/core@1.5.7)(sass@1.93.2)(webpack@5.101.3(@swc/core@1.13.19)) transitivePeerDependencies: @@ -11004,6 +12753,8 @@ snapshots: dependencies: is-obj: 2.0.0 + dset@3.1.4: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -11018,6 +12769,8 @@ snapshots: electron-to-chromium@1.5.224: {} + emoji-regex@10.6.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -11156,6 +12909,35 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + escalade@3.2.0: {} escape-goat@4.0.0: {} @@ -11387,6 +13169,8 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/unist': 3.0.3 + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -11404,6 +13188,8 @@ snapshots: eventemitter3@4.0.7: {} + eventemitter3@5.0.4: {} + events-universal@1.0.1: dependencies: bare-events: 2.7.0 @@ -11462,6 +13248,13 @@ snapshots: transitivePeerDependencies: - supports-color + expressive-code@0.41.6: + dependencies: + '@expressive-code/core': 0.41.6 + '@expressive-code/plugin-frames': 0.41.6 + '@expressive-code/plugin-shiki': 0.41.6 + '@expressive-code/plugin-text-markers': 0.41.6 + extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 @@ -11500,6 +13293,10 @@ snapshots: dependencies: websocket-driver: 0.7.4 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + feed@4.2.2: dependencies: xml-js: 1.6.11 @@ -11573,8 +13370,18 @@ snapshots: flatted@3.3.3: {} + flattie@1.1.1: {} + follow-redirects@1.15.11: {} + fontace@0.4.0: + dependencies: + fontkitten: 1.0.2 + + fontkitten@1.0.2: + dependencies: + tiny-inflate: 1.0.3 + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -11619,6 +13426,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -11651,6 +13460,8 @@ snapshots: github-slugger@1.5.0: {} + github-slugger@2.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -11749,6 +13560,18 @@ snapshots: dependencies: duplexer: 0.1.2 + h3@1.15.5: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.3 + uncrypto: 0.1.3 + handle-thing@2.0.1: {} has-bigints@1.1.0: {} @@ -11784,6 +13607,16 @@ snapshots: '@types/hast': 3.0.4 hast-util-is-element: 3.0.0 + hast-util-format@1.1.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.1 + hast-util-from-html@2.0.3: dependencies: '@types/hast': 3.0.4 @@ -12003,6 +13836,8 @@ snapshots: html-escaper@2.0.2: {} + html-escaper@3.0.3: {} + html-minifier-terser@6.1.0: dependencies: camel-case: 4.1.2 @@ -12038,6 +13873,8 @@ snapshots: '@rspack/core': 1.5.7 webpack: 5.101.3(@swc/core@1.13.19) + html-whitespace-sensitive-tag-names@3.0.1: {} + htmlparser2@6.1.0: dependencies: domelementtype: 2.3.0 @@ -12102,6 +13939,10 @@ snapshots: hyperdyperid@1.2.0: {} + i18next@23.16.8: + dependencies: + '@babel/runtime': 7.28.4 + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -12129,6 +13970,8 @@ snapshots: import-lazy@4.0.0: {} + import-meta-resolve@4.2.0: {} + import-modules@2.1.0: {} imurmurhash@0.1.4: {} @@ -12166,6 +14009,8 @@ snapshots: ipaddr.js@2.2.0: {} + iron-webcrypto@1.2.1: {} + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -12408,6 +14253,10 @@ snapshots: dependencies: argparse: 2.0.1 + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -12455,6 +14304,8 @@ snapshots: kleur@3.0.3: {} + klona@2.0.6: {} + known-css-properties@0.36.0: {} known-css-properties@0.37.0: {} @@ -12578,10 +14429,22 @@ snapshots: lowercase-keys@3.0.0: {} + lru-cache@11.2.5: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 + source-map-js: 1.2.1 + markdown-extensions@2.0.0: {} markdown-table@2.0.0: @@ -12596,6 +14459,12 @@ snapshots: mathml-tag-names@2.1.3: {} + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + mdast-util-directive@3.1.0: dependencies: '@types/mdast': 4.0.4 @@ -13186,6 +15055,12 @@ snapshots: neo-async@2.6.2: {} + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -13207,8 +15082,12 @@ snapshots: emojilib: 2.4.0 skin-tone: 2.0.0 + node-fetch-native@1.6.7: {} + node-forge@1.3.1: {} + node-mock-http@1.0.4: {} + node-releases@2.0.21: {} normalize-package-data@2.5.0: @@ -13282,6 +15161,14 @@ snapshots: obuf@1.1.2: {} + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.3 + + ohash@2.0.11: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -13296,6 +15183,14 @@ snapshots: dependencies: mimic-fn: 2.1.0 + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.4: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + open@10.2.0: dependencies: default-browser: 5.2.1 @@ -13342,6 +15237,10 @@ snapshots: dependencies: yocto-queue: 1.2.1 + p-limit@6.2.0: + dependencies: + yocto-queue: 1.2.1 + p-locate@2.0.0: dependencies: p-limit: 1.3.0 @@ -13365,6 +15264,11 @@ snapshots: eventemitter3: 4.0.7 p-timeout: 3.2.0 + p-queue@8.1.1: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 6.1.4 + p-retry@6.2.1: dependencies: '@types/retry': 0.12.2 @@ -13375,6 +15279,8 @@ snapshots: dependencies: p-finally: 1.0.0 + p-timeout@6.1.4: {} + p-try@1.0.0: {} p-try@2.2.0: {} @@ -13386,6 +15292,17 @@ snapshots: registry-url: 6.0.1 semver: 7.7.2 + package-manager-detector@1.6.0: {} + + pagefind@1.4.0: + optionalDependencies: + '@pagefind/darwin-arm64': 1.4.0 + '@pagefind/darwin-x64': 1.4.0 + '@pagefind/freebsd-x64': 1.4.0 + '@pagefind/linux-arm64': 1.4.0 + '@pagefind/linux-x64': 1.4.0 + '@pagefind/windows-x64': 1.4.0 + param-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -13417,6 +15334,15 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + parse-numeric-range@1.3.0: {} parse5-htmlparser2-tree-adapter@7.1.0: @@ -13467,10 +15393,14 @@ snapshots: path-type@4.0.0: {} + piccolore@0.1.3: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.3: {} + pify@3.0.0: {} pkg-dir@7.0.0: @@ -13713,6 +15643,11 @@ snapshots: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + postcss-nesting@13.0.2(postcss@8.5.6): dependencies: '@csstools/selector-resolve-nested': 3.1.0(postcss-selector-parser@7.1.0) @@ -14029,6 +15964,8 @@ snapshots: quick-lru@5.1.1: {} + radix3@1.1.2: {} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -14155,6 +16092,8 @@ snapshots: readdirp@4.1.2: {} + readdirp@5.0.0: {} + recma-build-jsx@1.0.0: dependencies: '@types/estree': 1.0.8 @@ -14201,6 +16140,16 @@ snapshots: regenerate@1.4.2: {} + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.4: @@ -14237,6 +16186,15 @@ snapshots: dependencies: jsesc: 3.1.0 + rehype-expressive-code@0.41.6: + dependencies: + expressive-code: 0.41.6 + + rehype-format@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-format: 1.1.0 + rehype-minify-whitespace@6.0.2: dependencies: '@types/hast': 3.0.4 @@ -14270,6 +16228,19 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + relateurl@0.2.7: {} remark-directive@3.0.1: @@ -14333,6 +16304,13 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -14383,6 +16361,31 @@ snapshots: dependencies: lowercase-keys: 3.0.0 + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.0.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + retry@0.13.1: {} reusify@1.1.0: {} @@ -14391,6 +16394,37 @@ snapshots: dependencies: glob: 7.2.3 + rollup@4.57.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.57.0 + '@rollup/rollup-android-arm64': 4.57.0 + '@rollup/rollup-darwin-arm64': 4.57.0 + '@rollup/rollup-darwin-x64': 4.57.0 + '@rollup/rollup-freebsd-arm64': 4.57.0 + '@rollup/rollup-freebsd-x64': 4.57.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.57.0 + '@rollup/rollup-linux-arm-musleabihf': 4.57.0 + '@rollup/rollup-linux-arm64-gnu': 4.57.0 + '@rollup/rollup-linux-arm64-musl': 4.57.0 + '@rollup/rollup-linux-loong64-gnu': 4.57.0 + '@rollup/rollup-linux-loong64-musl': 4.57.0 + '@rollup/rollup-linux-ppc64-gnu': 4.57.0 + '@rollup/rollup-linux-ppc64-musl': 4.57.0 + '@rollup/rollup-linux-riscv64-gnu': 4.57.0 + '@rollup/rollup-linux-riscv64-musl': 4.57.0 + '@rollup/rollup-linux-s390x-gnu': 4.57.0 + '@rollup/rollup-linux-x64-gnu': 4.57.0 + '@rollup/rollup-linux-x64-musl': 4.57.0 + '@rollup/rollup-openbsd-x64': 4.57.0 + '@rollup/rollup-openharmony-arm64': 4.57.0 + '@rollup/rollup-win32-arm64-msvc': 4.57.0 + '@rollup/rollup-win32-ia32-msvc': 4.57.0 + '@rollup/rollup-win32-x64-gnu': 4.57.0 + '@rollup/rollup-win32-x64-msvc': 4.57.0 + fsevents: 2.3.3 + rtlcss@4.3.0: dependencies: escalade: 3.2.0 @@ -14492,6 +16526,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: {} + send@0.19.0: dependencies: debug: 2.6.9 @@ -14591,6 +16627,38 @@ snapshots: - bare-buffer - react-native-b4a + sharp@0.34.5: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -14599,6 +16667,17 @@ snapshots: shell-quote@1.8.3: {} + shiki@3.21.0: + dependencies: + '@shikijs/core': 3.21.0 + '@shikijs/engine-javascript': 3.21.0 + '@shikijs/engine-oniguruma': 3.21.0 + '@shikijs/langs': 3.21.0 + '@shikijs/themes': 3.21.0 + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -14658,6 +16737,13 @@ snapshots: arg: 5.0.2 sax: 1.4.1 + sitemap@8.0.2: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + skin-tone@2.0.0: dependencies: unicode-emoji-modifier-base: 1.0.0 @@ -14672,6 +16758,8 @@ snapshots: astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 + smol-toml@1.6.0: {} + snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -14748,6 +16836,8 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stream-replace-string@2.0.0: {} + streamx@2.23.0: dependencies: events-universal: 1.0.1 @@ -14768,6 +16858,12 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.2 + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -14984,6 +17080,16 @@ snapshots: csso: 5.0.5 picocolors: 1.1.1 + svgo@4.0.0: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.1.0 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.4.1 + swc-loader@0.2.6(@swc/core@1.13.19)(webpack@5.101.3(@swc/core@1.13.19)): dependencies: '@swc/core': 1.13.19 @@ -15074,10 +17180,19 @@ snapshots: thunky@1.1.0: {} + tiny-inflate@1.0.3: {} + tiny-invariant@1.3.3: {} tiny-warning@1.0.3: {} + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + tinypool@1.1.1: {} to-regex-range@5.0.1: @@ -15102,6 +17217,10 @@ snapshots: dependencies: typescript: 5.9.2 + tsconfck@3.1.6(typescript@5.9.2): + optionalDependencies: + typescript: 5.9.2 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -15131,6 +17250,8 @@ snapshots: type-fest@2.19.0: {} + type-fest@4.41.0: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -15175,6 +17296,10 @@ snapshots: typescript@5.9.2: {} + ufo@1.6.3: {} + + ultrahtml@1.6.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -15182,6 +17307,8 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + uncrypto@0.1.3: {} + undici-types@6.21.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -15207,6 +17334,12 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unifont@0.7.3: + dependencies: + css-tree: 3.1.0 + ofetch: 1.5.1 + ohash: 2.0.11 + unique-string@3.0.0: dependencies: crypto-random-string: 4.0.0 @@ -15220,6 +17353,11 @@ snapshots: dependencies: '@types/unist': 3.0.3 + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + unist-util-position-from-estree@2.0.0: dependencies: '@types/unist': 3.0.3 @@ -15228,15 +17366,29 @@ snapshots: dependencies: '@types/unist': 3.0.3 + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit-parents@6.0.1: dependencies: '@types/unist': 3.0.3 unist-util-is: 6.0.0 + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -15247,6 +17399,17 @@ snapshots: unpipe@1.0.0: {} + unstorage@1.17.4: + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.5 + lru-cache: 11.2.5 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.3 + update-browserslist-db@1.1.3(browserslist@4.26.2): dependencies: browserslist: 4.26.2 @@ -15323,6 +17486,26 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + vite@6.4.1(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.18.6 + fsevents: 2.3.3 + jiti: 1.21.7 + lightningcss: 1.30.1 + sass: 1.93.2 + terser: 5.44.0 + + vitefu@1.1.1(vite@6.4.1(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)): + optionalDependencies: + vite: 6.4.1(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0) + watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 @@ -15498,6 +17681,8 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 + which-pm-runs@1.1.0: {} + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -15520,6 +17705,10 @@ snapshots: dependencies: string-width: 5.1.2 + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + wildcard@2.0.1: {} word-wrap@1.2.5: {} @@ -15536,6 +17725,12 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.2 + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + wrappy@1.0.2: {} write-file-atomic@3.0.3: @@ -15564,10 +17759,31 @@ snapshots: dependencies: sax: 1.4.1 + xxhash-wasm@1.1.0: {} + yallist@3.1.1: {} + yargs-parser@21.1.1: {} + yocto-queue@1.2.1: {} + yocto-spinner@0.2.3: + dependencies: + yoctocolors: 2.1.2 + + yoctocolors@2.1.2: {} + + zod-to-json-schema@3.25.1(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod-to-ts@1.2.0(typescript@5.9.2)(zod@3.25.76): + dependencies: + typescript: 5.9.2 + zod: 3.25.76 + + zod@3.25.76: {} + zod@4.1.11: {} zwitch@2.0.4: {} diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000000..6a7b7a02b0 --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from 'astro:content'; +import { docsLoader } from '@astrojs/starlight/loaders'; +import { docsSchema } from '@astrojs/starlight/schema'; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/src/content/docs/guides/example.md b/src/content/docs/guides/example.md new file mode 100644 index 0000000000..29415f984e --- /dev/null +++ b/src/content/docs/guides/example.md @@ -0,0 +1,6 @@ +--- +title: Example Guide +description: A guide in my new Starlight docs site. +--- + +# Hello diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx new file mode 100644 index 0000000000..915cfaca8a --- /dev/null +++ b/src/content/docs/index.mdx @@ -0,0 +1,28 @@ +--- +title: Welcome +description: Get started building your docs site with Starlight. +template: splash +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; + +## Next steps + + + + Edit `src/content/docs/index.mdx` to see this page change. + + + Delete `template: splash` in `src/content/docs/index.mdx` to display a + sidebar on this page. + + + Add Markdown or MDX files to `src/content/docs` to create new pages. + + + Edit your `sidebar` and other config in `astro.config.mjs`. + + + Learn more in [the Starlight Docs](https://starlight.astro.build/). + + diff --git a/tsconfig.astro.json b/tsconfig.astro.json new file mode 100644 index 0000000000..5042b26175 --- /dev/null +++ b/tsconfig.astro.json @@ -0,0 +1,12 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [ + ".astro/types.d.ts", + "src/content.config.ts", + "src/content/**" + ], + "exclude": [ + "dist", + "build" + ] +} diff --git a/tsconfig.docusaurus.json b/tsconfig.docusaurus.json new file mode 100644 index 0000000000..b495e29fff --- /dev/null +++ b/tsconfig.docusaurus.json @@ -0,0 +1,17 @@ +{ + "extends": "@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": ".", + "lib": [ + "DOM", + "ESNext" + ] + }, + "exclude": [ + ".astro/types.d.ts", + "src/content.config.ts", + "src/content/**", + "dist", + "build" + ] +} diff --git a/tsconfig.json b/tsconfig.json index fd4988854b..93276a2ead 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { - "extends": "@docusaurus/tsconfig", - "compilerOptions": { - "baseUrl": ".", - "lib": ["DOM", "ESNext"] - } + "files": [], + "references": [ + { + "path": "./tsconfig.astro.json" + }, + { + "path": "./tsconfig.docusaurus.json" + }, + ] } From 266a4ad2f3b06fff6913267768fdc1cf8d7312f1 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 30 Jan 2026 00:45:05 +0100 Subject: [PATCH 02/45] feat: add i18n --- .astro/content-modules.mjs | 2 +- .astro/data-store.json | 2 +- astro.config.mjs | 9 +++++++++ src/content/docs/{ => en}/guides/example.md | 0 src/content/docs/{ => en}/index.mdx | 0 5 files changed, 11 insertions(+), 2 deletions(-) rename src/content/docs/{ => en}/guides/example.md (100%) rename src/content/docs/{ => en}/index.mdx (100%) diff --git a/.astro/content-modules.mjs b/.astro/content-modules.mjs index e6cec1ffb8..1cdaaaae6a 100644 --- a/.astro/content-modules.mjs +++ b/.astro/content-modules.mjs @@ -1,4 +1,4 @@ export default new Map([ -["src/content/docs/index.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Findex.mdx&astroContentModuleFlag=true")]]); +["src/content/docs/en/index.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Fen%2Findex.mdx&astroContentModuleFlag=true")]]); \ No newline at end of file diff --git a/.astro/data-store.json b/.astro/data-store.json index bc1484f191..aed938fbd2 100644 --- a/.astro/data-store.json +++ b/.astro/data-store.json @@ -1 +1 @@ -[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.16.16","content-config-digest","28e7bc87492477d9","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[null,null,null],\"rehypePlugins\":[[null,{\"experimentalHeadingIdCompat\":false}],null,[null,{\"themes\":[{\"name\":\"Night Owl No Italics\",\"type\":\"dark\",\"colors\":{\"focusBorder\":\"#122d42\",\"foreground\":\"#d6deeb\",\"disabledForeground\":\"#cccccc80\",\"descriptionForeground\":\"#d6deebb3\",\"errorForeground\":\"#ef5350\",\"icon.foreground\":\"#c5c5c5\",\"contrastActiveBorder\":null,\"contrastBorder\":\"#122d42\",\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#4f4f4f\",\"textLink.activeForeground\":\"#3794ff\",\"textLink.foreground\":\"#3794ff\",\"textPreformat.foreground\":\"#d7ba7d\",\"textSeparator.foreground\":\"#ffffff2e\",\"editor.background\":\"#23262f\",\"editor.foreground\":\"#d6deeb\",\"editorLineNumber.foreground\":\"#4b6479\",\"editorLineNumber.activeForeground\":\"#c5e4fd\",\"editorActiveLineNumber.foreground\":\"#c6c6c6\",\"editor.selectionBackground\":\"#1d3b53\",\"editor.inactiveSelectionBackground\":\"#7e57c25a\",\"editor.selectionHighlightBackground\":\"#5f7e9779\",\"editorError.foreground\":\"#ef5350\",\"editorWarning.foreground\":\"#b39554\",\"editorInfo.foreground\":\"#3794ff\",\"editorHint.foreground\":\"#eeeeeeb2\",\"problemsErrorIcon.foreground\":\"#ef5350\",\"problemsWarningIcon.foreground\":\"#b39554\",\"problemsInfoIcon.foreground\":\"#3794ff\",\"editor.findMatchBackground\":\"#5f7e9779\",\"editor.findMatchHighlightBackground\":\"#1085bb5d\",\"editor.findRangeHighlightBackground\":\"#3a3d4166\",\"editorLink.activeForeground\":\"#4e94ce\",\"editorLightBulb.foreground\":\"#ffcc00\",\"editorLightBulbAutoFix.foreground\":\"#75beff\",\"diffEditor.insertedTextBackground\":\"#99b76d23\",\"diffEditor.insertedTextBorder\":\"#c5e47833\",\"diffEditor.removedTextBackground\":\"#ef535033\",\"diffEditor.removedTextBorder\":\"#ef53504d\",\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#011627\",\"editorStickyScrollHover.background\":\"#2a2d2e\",\"editorInlayHint.background\":\"#5f7e97cc\",\"editorInlayHint.foreground\":\"#ffffff\",\"editorInlayHint.typeBackground\":\"#5f7e97cc\",\"editorInlayHint.typeForeground\":\"#ffffff\",\"editorInlayHint.parameterBackground\":\"#5f7e97cc\",\"editorInlayHint.parameterForeground\":\"#ffffff\",\"editorPane.background\":\"#011627\",\"editorGroup.emptyBackground\":\"#011627\",\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-black)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#011627\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#011627\",\"editorGroup.dropBackground\":\"#7e57c273\",\"editorGroup.dropIntoPromptForeground\":\"#d6deeb\",\"editorGroup.dropIntoPromptBackground\":\"#021320\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#011627\",\"sideBySideEditor.verticalBorder\":\"#011627\",\"scrollbar.shadow\":\"#010b14\",\"scrollbarSlider.background\":\"#ffffff17\",\"scrollbarSlider.hoverBackground\":\"#ffffff40\",\"scrollbarSlider.activeBackground\":\"#084d8180\",\"panel.background\":\"#011627\",\"panel.border\":\"#5f7e97\",\"panelTitle.activeBorder\":\"#5f7e97\",\"panelTitle.activeForeground\":\"#ffffffcc\",\"panelTitle.inactiveForeground\":\"#d6deeb80\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#011627\",\"widget.shadow\":\"#011627\",\"editorWidget.background\":\"#021320\",\"editorWidget.foreground\":\"#d6deeb\",\"editorWidget.border\":\"#5f7e97\",\"quickInput.background\":\"#021320\",\"quickInput.foreground\":\"#d6deeb\",\"quickInputTitle.background\":\"#ffffff1a\",\"pickerGroup.foreground\":\"#d1aaff\",\"pickerGroup.border\":\"#011627\",\"editor.hoverHighlightBackground\":\"#7e57c25a\",\"editorHoverWidget.background\":\"#011627\",\"editorHoverWidget.foreground\":\"#d6deeb\",\"editorHoverWidget.border\":\"#5f7e97\",\"editorHoverWidget.statusBarBackground\":\"#011a2f\",\"titleBar.activeBackground\":\"var(--sl-color-black)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#010e1a\",\"titleBar.inactiveForeground\":\"#eeefff99\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#5a5d5e50\",\"toolbar.activeBackground\":\"#63666750\",\"tab.activeBackground\":\"#0b2942\",\"tab.unfocusedActiveBackground\":\"#0b2942\",\"tab.inactiveBackground\":\"#01111d\",\"tab.unfocusedInactiveBackground\":\"#01111d\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#5f7e97\",\"tab.unfocusedActiveForeground\":\"#5f7e97\",\"tab.unfocusedInactiveForeground\":\"#5f7e97\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#272b3b\",\"tab.lastPinnedBorder\":\"#585858\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":\"#262a39\",\"tab.activeBorderTop\":\"var(--sl-color-accent-high)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#3399cc\",\"tab.inactiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedActiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedInactiveModifiedBorder\":\"#3399cc40\",\"badge.background\":\"#5f7e97\",\"badge.foreground\":\"#ffffff\",\"button.background\":\"#7e57c2cc\",\"button.foreground\":\"#ffffffcc\",\"button.border\":\"#122d42\",\"button.separator\":\"#ffffff52\",\"button.hoverBackground\":\"#7e57c2\",\"button.secondaryBackground\":\"#3a3d41\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#46494e\",\"dropdown.background\":\"#011627\",\"dropdown.foreground\":\"#ffffffcc\",\"dropdown.border\":\"#5f7e97\",\"list.activeSelectionBackground\":\"#234d708c\",\"list.activeSelectionForeground\":\"#ffffff\",\"tree.indentGuidesStroke\":\"#585858\",\"input.background\":\"#0b253a\",\"input.foreground\":\"#ffffffcc\",\"input.placeholderForeground\":\"#5f7e97\",\"inputOption.activeBorder\":\"#ffffffcc\",\"inputOption.hoverBackground\":\"#5a5d5e80\",\"inputOption.activeBackground\":\"#122d4266\",\"inputOption.activeForeground\":\"#ffffff\",\"inputValidation.infoBackground\":\"#00589ef2\",\"inputValidation.infoBorder\":\"#64b5f6\",\"inputValidation.warningBackground\":\"#675700f2\",\"inputValidation.warningBorder\":\"#ffca28\",\"inputValidation.errorBackground\":\"#ab0300f2\",\"inputValidation.errorBorder\":\"#ef5350\",\"keybindingLabel.background\":\"#8080802b\",\"keybindingLabel.foreground\":\"#cccccc\",\"keybindingLabel.border\":\"#33333399\",\"keybindingLabel.bottomBorder\":\"#44444499\",\"menu.foreground\":\"#ffffffcc\",\"menu.background\":\"#011627\",\"menu.selectionForeground\":\"#ffffff\",\"menu.selectionBackground\":\"#234d708c\",\"menu.separatorBackground\":\"#606060\",\"editor.snippetTabstopHighlightBackground\":\"#7c7c74c\",\"editor.snippetFinalTabstopHighlightBorder\":\"#525252\",\"terminal.ansiBlack\":\"#011627\",\"terminal.ansiRed\":\"#ef5350\",\"terminal.ansiGreen\":\"#22da6e\",\"terminal.ansiYellow\":\"#c5e478\",\"terminal.ansiBlue\":\"#82aaff\",\"terminal.ansiMagenta\":\"#c792ea\",\"terminal.ansiCyan\":\"#21c7a8\",\"terminal.ansiWhite\":\"#ffffff\",\"terminal.ansiBrightBlack\":\"#575656\",\"terminal.ansiBrightRed\":\"#ef5350\",\"terminal.ansiBrightGreen\":\"#22da6e\",\"terminal.ansiBrightYellow\":\"#ffeb95\",\"terminal.ansiBrightBlue\":\"#82aaff\",\"terminal.ansiBrightMagenta\":\"#c792ea\",\"terminal.ansiBrightCyan\":\"#7fdbca\",\"terminal.ansiBrightWhite\":\"#ffffff\",\"selection.background\":\"#4373c2\",\"input.border\":\"#5f7e97\",\"punctuation.definition.generic.begin.html\":\"#ef5350f2\",\"progress.background\":\"#7e57c2\",\"breadcrumb.foreground\":\"#a599e9\",\"breadcrumb.focusForeground\":\"#ffffff\",\"breadcrumb.activeSelectionForeground\":\"#ffffff\",\"breadcrumbPicker.background\":\"#001122\",\"list.invalidItemForeground\":\"#975f94\",\"list.dropBackground\":\"#011627\",\"list.focusBackground\":\"#010d18\",\"list.focusForeground\":\"#ffffff\",\"list.highlightForeground\":\"#ffffff\",\"list.hoverBackground\":\"#011627\",\"list.hoverForeground\":\"#ffffff\",\"list.inactiveSelectionBackground\":\"#0e293f\",\"list.inactiveSelectionForeground\":\"#5f7e97\",\"activityBar.background\":\"#011627\",\"activityBar.dropBackground\":\"#5f7e97\",\"activityBar.foreground\":\"#5f7e97\",\"activityBar.border\":\"#011627\",\"activityBarBadge.background\":\"#44596b\",\"activityBarBadge.foreground\":\"#ffffff\",\"sideBar.background\":\"#011627\",\"sideBar.foreground\":\"#89a4bb\",\"sideBar.border\":\"#011627\",\"sideBarTitle.foreground\":\"#5f7e97\",\"sideBarSectionHeader.background\":\"#011627\",\"sideBarSectionHeader.foreground\":\"#5f7e97\",\"editorCursor.foreground\":\"#80a4c2\",\"editor.wordHighlightBackground\":\"#f6bbe533\",\"editor.wordHighlightStrongBackground\":\"#e2a2f433\",\"editor.lineHighlightBackground\":\"#0003\",\"editor.rangeHighlightBackground\":\"#7e57c25a\",\"editorIndentGuide.background\":\"#5e81ce52\",\"editorIndentGuide.activeBackground\":\"#7e97ac\",\"editorRuler.foreground\":\"#5e81ce52\",\"editorCodeLens.foreground\":\"#5e82ceb4\",\"editorBracketMatch.background\":\"#5f7e974d\",\"editorOverviewRuler.currentContentForeground\":\"#7e57c2\",\"editorOverviewRuler.incomingContentForeground\":\"#7e57c2\",\"editorOverviewRuler.commonContentForeground\":\"#7e57c2\",\"editorGutter.background\":\"#011627\",\"editorGutter.modifiedBackground\":\"#e2b93d\",\"editorGutter.addedBackground\":\"#9ccc65\",\"editorGutter.deletedBackground\":\"#ef5350\",\"editorSuggestWidget.background\":\"#2c3043\",\"editorSuggestWidget.border\":\"#2b2f40\",\"editorSuggestWidget.foreground\":\"#d6deeb\",\"editorSuggestWidget.highlightForeground\":\"#ffffff\",\"editorSuggestWidget.selectedBackground\":\"#5f7e97\",\"debugExceptionWidget.background\":\"#011627\",\"debugExceptionWidget.border\":\"#5f7e97\",\"editorMarkerNavigation.background\":\"#0b2942\",\"editorMarkerNavigationError.background\":\"#ef5350\",\"editorMarkerNavigationWarning.background\":\"#ffca28\",\"peekView.border\":\"#5f7e97\",\"peekViewEditor.background\":\"#011627\",\"peekViewEditor.matchHighlightBackground\":\"#7e57c25a\",\"peekViewResult.background\":\"#011627\",\"peekViewResult.fileForeground\":\"#5f7e97\",\"peekViewResult.lineForeground\":\"#5f7e97\",\"peekViewResult.matchHighlightBackground\":\"#ffffffcc\",\"peekViewResult.selectionBackground\":\"#2e3250\",\"peekViewResult.selectionForeground\":\"#5f7e97\",\"peekViewTitle.background\":\"#011627\",\"peekViewTitleDescription.foreground\":\"#697098\",\"peekViewTitleLabel.foreground\":\"#5f7e97\",\"merge.currentHeaderBackground\":\"#5f7e97\",\"merge.incomingHeaderBackground\":\"#7e57c25a\",\"statusBar.background\":\"#011627\",\"statusBar.foreground\":\"#5f7e97\",\"statusBar.border\":\"#262a39\",\"statusBar.debuggingBackground\":\"#202431\",\"statusBar.debuggingBorder\":\"#1f2330\",\"statusBar.noFolderBackground\":\"#011627\",\"statusBar.noFolderBorder\":\"#25293a\",\"statusBarItem.activeBackground\":\"#202431\",\"statusBarItem.hoverBackground\":\"#202431\",\"statusBarItem.prominentBackground\":\"#202431\",\"statusBarItem.prominentHoverBackground\":\"#202431\",\"notifications.background\":\"#01111d\",\"notifications.border\":\"#262a39\",\"notificationCenter.border\":\"#262a39\",\"notificationToast.border\":\"#262a39\",\"notifications.foreground\":\"#ffffffcc\",\"notificationLink.foreground\":\"#80cbc4\",\"extensionButton.prominentForeground\":\"#ffffffcc\",\"extensionButton.prominentBackground\":\"#7e57c2cc\",\"extensionButton.prominentHoverBackground\":\"#7e57c2\",\"terminal.selectionBackground\":\"#1b90dd4d\",\"terminalCursor.background\":\"#234d70\",\"debugToolBar.background\":\"#011627\",\"welcomePage.buttonBackground\":\"#011627\",\"welcomePage.buttonHoverBackground\":\"#011627\",\"walkThrough.embeddedEditorBackground\":\"#011627\",\"gitDecoration.modifiedResourceForeground\":\"#a2bffc\",\"gitDecoration.deletedResourceForeground\":\"#ef535090\",\"gitDecoration.untrackedResourceForeground\":\"#c5e478ff\",\"gitDecoration.ignoredResourceForeground\":\"#395a75\",\"gitDecoration.conflictingResourceForeground\":\"#ffeb95cc\",\"source.elm\":\"#5f7e97\",\"string.quoted.single.js\":\"#ffffff\",\"meta.objectliteral.js\":\"#82aaff\"},\"fg\":\"#d6deeb\",\"bg\":\"#23262f\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#a2bffc\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#f27775fe\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#d6deeb\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#919f9f\",\"fontStyle\":\"\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#5ca7e4\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#caece6\",\"fontStyle\":\"\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"fontStyle\":\"\",\"foreground\":\"#c5e478\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"background\":\"#ff2c83\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#d3423e\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#919f9f\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#cdebf7\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#ffffff\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#80cbc4\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#57eaf1\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#fad430\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#faf39f\",\"fontStyle\":\"\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#82aaff\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Comparison, returns, imports, and Keyword Operator Ruby\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#989da0\",\"background\":\"#F78C6C\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"background\":\"#8BD649\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#ec5f67\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#ef787f\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#31e1eb\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Unquoted String Zone\",\"scope\":[\"string.unquoted.preprocessor.message.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Region\",\"scope\":[\"punctuation.separator.hash.cs\",\"keyword.preprocessor.region.cs\",\"keyword.preprocessor.endregion.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"bold\"}},{\"name\":\"C# Other Variables\",\"scope\":[\"variable.other.object.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Enum\",\"scope\":[\"entity.name.type.enum.cs\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Dart String\",\"scope\":[\"string.interpolated.single.dart\",\"string.interpolated.double.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Dart Class\",\"scope\":[\"support.class.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#ff6d6d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#dddddd\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#e0dec6\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#6ae9f0\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#78ccf0\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#8b96ea\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#c789d6\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#ff869a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#d6deeb\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#8eace3\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#d7dbe0\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-6)\",\"terminalBackground\":\"var(--sl-color-gray-6)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-6)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#ffffff17\",\"markBorderColor\":\"#ffffff40\"}}},{\"name\":\"Night Owl Light\",\"type\":\"light\",\"colors\":{\"focusBorder\":\"#93a1a1\",\"foreground\":\"#403f53\",\"disabledForeground\":\"#61616180\",\"descriptionForeground\":\"#403f53\",\"errorForeground\":\"#403f53\",\"icon.foreground\":\"#424242\",\"contrastActiveBorder\":null,\"contrastBorder\":null,\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#dcdcdc66\",\"textLink.activeForeground\":\"#006ab1\",\"textLink.foreground\":\"#006ab1\",\"textPreformat.foreground\":\"#a31515\",\"textSeparator.foreground\":\"#0000002e\",\"editor.background\":\"#f6f7f9\",\"editor.foreground\":\"#403f53\",\"editorLineNumber.foreground\":\"#90a7b2\",\"editorLineNumber.activeForeground\":\"#403f53\",\"editorActiveLineNumber.foreground\":\"#0b216f\",\"editor.selectionBackground\":\"#e0e0e0\",\"editor.inactiveSelectionBackground\":\"#e0e0e080\",\"editor.selectionHighlightBackground\":\"#339cec33\",\"editorError.foreground\":\"#e64d49\",\"editorWarning.foreground\":\"#daaa01\",\"editorInfo.foreground\":\"#1a85ff\",\"editorHint.foreground\":\"#6c6c6c\",\"problemsErrorIcon.foreground\":\"#e64d49\",\"problemsWarningIcon.foreground\":\"#daaa01\",\"problemsInfoIcon.foreground\":\"#1a85ff\",\"editor.findMatchBackground\":\"#93a1a16c\",\"editor.findMatchHighlightBackground\":\"#93a1a16c\",\"editor.findRangeHighlightBackground\":\"#7497a633\",\"editorLink.activeForeground\":\"#0000ff\",\"editorLightBulb.foreground\":\"#ddb100\",\"editorLightBulbAutoFix.foreground\":\"#007acc\",\"diffEditor.insertedTextBackground\":\"#9ccc2c40\",\"diffEditor.insertedTextBorder\":null,\"diffEditor.removedTextBackground\":\"#ff000033\",\"diffEditor.removedTextBorder\":null,\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#fbfbfb\",\"editorStickyScrollHover.background\":\"#f0f0f0\",\"editorInlayHint.background\":\"#2aa29899\",\"editorInlayHint.foreground\":\"#f0f0f0\",\"editorInlayHint.typeBackground\":\"#2aa29899\",\"editorInlayHint.typeForeground\":\"#f0f0f0\",\"editorInlayHint.parameterBackground\":\"#2aa29899\",\"editorInlayHint.parameterForeground\":\"#f0f0f0\",\"editorPane.background\":\"#fbfbfb\",\"editorGroup.emptyBackground\":null,\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-gray-6)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#f0f0f0\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#f0f0f0\",\"editorGroup.dropBackground\":\"#2677cb2d\",\"editorGroup.dropIntoPromptForeground\":\"#403f53\",\"editorGroup.dropIntoPromptBackground\":\"#f0f0f0\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#f0f0f0\",\"sideBySideEditor.verticalBorder\":\"#f0f0f0\",\"scrollbar.shadow\":\"#cccccc\",\"scrollbarSlider.background\":\"#0000001a\",\"scrollbarSlider.hoverBackground\":\"#00000055\",\"scrollbarSlider.activeBackground\":\"#00000099\",\"panel.background\":\"#f0f0f0\",\"panel.border\":\"#d9d9d9\",\"panelTitle.activeBorder\":\"#424242\",\"panelTitle.activeForeground\":\"#424242\",\"panelTitle.inactiveForeground\":\"#424242bf\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#f6f6f6\",\"widget.shadow\":\"#d9d9d9\",\"editorWidget.background\":\"#f0f0f0\",\"editorWidget.foreground\":\"#403f53\",\"editorWidget.border\":\"#d9d9d9\",\"quickInput.background\":\"#f0f0f0\",\"quickInput.foreground\":\"#403f53\",\"quickInputTitle.background\":\"#0000000f\",\"pickerGroup.foreground\":\"#403f53\",\"pickerGroup.border\":\"#d9d9d9\",\"editor.hoverHighlightBackground\":\"#339cec33\",\"editorHoverWidget.background\":\"#f0f0f0\",\"editorHoverWidget.foreground\":\"#403f53\",\"editorHoverWidget.border\":\"#d9d9d9\",\"editorHoverWidget.statusBarBackground\":\"#e4e4e4\",\"titleBar.activeBackground\":\"var(--sl-color-gray-6)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#f0f0f099\",\"titleBar.inactiveForeground\":\"#33333399\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#b8b8b850\",\"toolbar.activeBackground\":\"#a6a6a650\",\"tab.activeBackground\":\"#f6f6f6\",\"tab.unfocusedActiveBackground\":\"#f6f6f6\",\"tab.inactiveBackground\":\"#f0f0f0\",\"tab.unfocusedInactiveBackground\":\"#f0f0f0\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#403f53\",\"tab.unfocusedActiveForeground\":\"#403f53b3\",\"tab.unfocusedInactiveForeground\":\"#403f5380\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#f0f0f0\",\"tab.lastPinnedBorder\":\"#a9a9a9\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":null,\"tab.activeBorderTop\":\"var(--sl-color-accent)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#2aa298\",\"tab.inactiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedActiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedInactiveModifiedBorder\":\"#93a1a1\",\"badge.background\":\"#2aa298\",\"badge.foreground\":\"#f0f0f0\",\"button.background\":\"#2aa298\",\"button.foreground\":\"#f0f0f0\",\"button.border\":null,\"button.separator\":\"#f0f0f066\",\"button.hoverBackground\":\"#22827a\",\"button.secondaryBackground\":\"#5f6a79\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#4c5561\",\"dropdown.background\":\"#f0f0f0\",\"dropdown.foreground\":\"#403f53\",\"dropdown.border\":\"#d9d9d9\",\"list.activeSelectionBackground\":\"#d3e8f8\",\"list.activeSelectionForeground\":\"#403f53\",\"tree.indentGuidesStroke\":\"#a9a9a9\",\"input.background\":\"#f0f0f0\",\"input.foreground\":\"#403f53\",\"input.placeholderForeground\":\"#93a1a1\",\"inputOption.activeBorder\":\"#2aa298\",\"inputOption.hoverBackground\":\"#b8b8b850\",\"inputOption.activeBackground\":\"#93a1a133\",\"inputOption.activeForeground\":\"#000000\",\"inputValidation.infoBackground\":\"#f0f0f0\",\"inputValidation.infoBorder\":\"#d0d0d0\",\"inputValidation.warningBackground\":\"#daaa01\",\"inputValidation.warningBorder\":\"#e0af02\",\"inputValidation.errorBackground\":\"#f76e6e\",\"inputValidation.errorBorder\":\"#de3d3b\",\"keybindingLabel.background\":\"#dddddd66\",\"keybindingLabel.foreground\":\"#555555\",\"keybindingLabel.border\":\"#cccccc66\",\"keybindingLabel.bottomBorder\":\"#bbbbbb66\",\"menu.foreground\":\"#403f53\",\"menu.background\":\"#f0f0f0\",\"menu.selectionForeground\":\"#403f53\",\"menu.selectionBackground\":\"#d3e8f8\",\"menu.separatorBackground\":\"#d4d4d4\",\"editor.snippetTabstopHighlightBackground\":\"#0a326433\",\"editor.snippetFinalTabstopHighlightBorder\":\"#0a326480\",\"terminal.ansiBlack\":\"#403f53\",\"terminal.ansiRed\":\"#de3d3b\",\"terminal.ansiGreen\":\"#08916a\",\"terminal.ansiYellow\":\"#e0af02\",\"terminal.ansiBlue\":\"#288ed7\",\"terminal.ansiMagenta\":\"#d6438a\",\"terminal.ansiCyan\":\"#2aa298\",\"terminal.ansiWhite\":\"#f0f0f0\",\"terminal.ansiBrightBlack\":\"#403f53\",\"terminal.ansiBrightRed\":\"#de3d3b\",\"terminal.ansiBrightGreen\":\"#08916a\",\"terminal.ansiBrightYellow\":\"#daaa01\",\"terminal.ansiBrightBlue\":\"#288ed7\",\"terminal.ansiBrightMagenta\":\"#d6438a\",\"terminal.ansiBrightCyan\":\"#2aa298\",\"terminal.ansiBrightWhite\":\"#f0f0f0\",\"selection.background\":\"#7a8181ad\",\"notifications.background\":\"#f0f0f0\",\"notifications.foreground\":\"#403f53\",\"notificationLink.foreground\":\"#994cc3\",\"notifications.border\":\"#cccccc\",\"notificationCenter.border\":\"#cccccc\",\"notificationToast.border\":\"#cccccc\",\"notificationCenterHeader.foreground\":\"#403f53\",\"notificationCenterHeader.background\":\"#f0f0f0\",\"input.border\":\"#d9d9d9\",\"progressBar.background\":\"#2aa298\",\"list.inactiveSelectionBackground\":\"#e0e7ea\",\"list.inactiveSelectionForeground\":\"#403f53\",\"list.focusBackground\":\"#d3e8f8\",\"list.hoverBackground\":\"#d3e8f8\",\"list.focusForeground\":\"#403f53\",\"list.hoverForeground\":\"#403f53\",\"list.highlightForeground\":\"#403f53\",\"list.errorForeground\":\"#e64d49\",\"list.warningForeground\":\"#daaa01\",\"activityBar.background\":\"#f0f0f0\",\"activityBar.foreground\":\"#403f53\",\"activityBar.dropBackground\":\"#d0d0d0\",\"activityBarBadge.background\":\"#403f53\",\"activityBarBadge.foreground\":\"#f0f0f0\",\"activityBar.border\":\"#f0f0f0\",\"sideBar.background\":\"#f0f0f0\",\"sideBar.foreground\":\"#403f53\",\"sideBarTitle.foreground\":\"#403f53\",\"sideBar.border\":\"#f0f0f0\",\"editorGroup.background\":\"#f6f6f6\",\"editorCursor.foreground\":\"#90a7b2\",\"editor.wordHighlightBackground\":\"#339cec33\",\"editor.wordHighlightStrongBackground\":\"#007dd659\",\"editor.lineHighlightBackground\":\"#f0f0f0\",\"editor.rangeHighlightBackground\":\"#7497a633\",\"editorWhitespace.foreground\":\"#d9d9d9\",\"editorIndentGuide.background\":\"#d9d9d9\",\"editorCodeLens.foreground\":\"#403f53\",\"editorBracketMatch.background\":\"#d3e8f8\",\"editorBracketMatch.border\":\"#2aa298\",\"editorError.border\":\"#fbfbfb\",\"editorWarning.border\":\"#daaa01\",\"editorGutter.addedBackground\":\"#49d0c5\",\"editorGutter.modifiedBackground\":\"#6fbef6\",\"editorGutter.deletedBackground\":\"#f76e6e\",\"editorRuler.foreground\":\"#d9d9d9\",\"editorOverviewRuler.errorForeground\":\"#e64d49\",\"editorOverviewRuler.warningForeground\":\"#daaa01\",\"editorSuggestWidget.background\":\"#f0f0f0\",\"editorSuggestWidget.foreground\":\"#403f53\",\"editorSuggestWidget.highlightForeground\":\"#403f53\",\"editorSuggestWidget.selectedBackground\":\"#d3e8f8\",\"editorSuggestWidget.border\":\"#d9d9d9\",\"debugExceptionWidget.background\":\"#f0f0f0\",\"debugExceptionWidget.border\":\"#d9d9d9\",\"editorMarkerNavigation.background\":\"#d0d0d0\",\"editorMarkerNavigationError.background\":\"#f76e6e\",\"editorMarkerNavigationWarning.background\":\"#daaa01\",\"debugToolBar.background\":\"#f0f0f0\",\"extensionButton.prominentBackground\":\"#2aa298\",\"extensionButton.prominentForeground\":\"#f0f0f0\",\"statusBar.background\":\"#f0f0f0\",\"statusBar.border\":\"#f0f0f0\",\"statusBar.debuggingBackground\":\"#f0f0f0\",\"statusBar.debuggingForeground\":\"#403f53\",\"statusBar.foreground\":\"#403f53\",\"statusBar.noFolderBackground\":\"#f0f0f0\",\"statusBar.noFolderForeground\":\"#403f53\",\"peekView.border\":\"#d9d9d9\",\"peekViewEditor.background\":\"#f6f6f6\",\"peekViewEditorGutter.background\":\"#f6f6f6\",\"peekViewEditor.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.background\":\"#f0f0f0\",\"peekViewResult.fileForeground\":\"#403f53\",\"peekViewResult.lineForeground\":\"#403f53\",\"peekViewResult.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.selectionBackground\":\"#e0e7ea\",\"peekViewResult.selectionForeground\":\"#403f53\",\"peekViewTitle.background\":\"#f0f0f0\",\"peekViewTitleLabel.foreground\":\"#403f53\",\"peekViewTitleDescription.foreground\":\"#403f53\",\"terminal.foreground\":\"#403f53\"},\"fg\":\"#403f53\",\"bg\":\"#f6f7f9\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#556484\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#ae3c3afd\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#403f53\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#5f636f\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#3a688f\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"foreground\":\"#bb2060\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#5d6376\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#58656a\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#646464\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#286d70\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword Operator Comparison, imports, returns and Keyword Operator Ruby\",\"scope\":[\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.module.ruby\",\"keyword.control.class.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Control Conditional\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"foreground\":\"#486e26\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#a64348\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#186e73\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#984e4d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#3e697c\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#555ea2\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#111111\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#7c5686\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Symbols\",\"scope\":[\"constant.language.symbol.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#954f5a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#403f53\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\",\"variable.other.property.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#76578b\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-7)\",\"terminalBackground\":\"var(--sl-color-gray-7)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-7)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#0000001a\",\"markBorderColor\":\"#00000055\"}}}],\"defaultLocale\":\"en\",\"cascadeLayer\":\"starlight.components\",\"styleOverrides\":{\"borderRadius\":\"0px\",\"borderWidth\":\"1px\",\"codePaddingBlock\":\"0.75rem\",\"codePaddingInline\":\"1rem\",\"codeFontFamily\":\"var(--__sl-font-mono)\",\"codeFontSize\":\"var(--sl-text-code)\",\"codeLineHeight\":\"var(--sl-line-height)\",\"uiFontFamily\":\"var(--__sl-font)\",\"textMarkers\":{\"lineDiffIndicatorMarginLeft\":\"0.25rem\",\"defaultChroma\":\"45\",\"backgroundOpacity\":\"60%\"}},\"plugins\":[{\"name\":\"Starlight Plugin\",\"hooks\":{}},{\"name\":\"astro-expressive-code\",\"hooks\":{}}]}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[\"en\"],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","docs",["Map",11,12,25,26],"index",{"id":11,"data":13,"body":22,"filePath":23,"digest":24,"deferredRender":16},{"title":14,"description":15,"editUrl":16,"head":17,"template":18,"sidebar":19,"pagefind":16,"draft":20},"Welcome","Get started building your docs site with Starlight.",true,[],"splash",{"hidden":20,"attrs":21},false,{},"import { Card, CardGrid } from '@astrojs/starlight/components';\n\n## Next steps\n\n\u003CCardGrid stagger>\n\t\u003CCard title=\"Update content\" icon=\"pencil\">\n\t\tEdit `src/content/docs/index.mdx` to see this page change.\n\t\u003C/Card>\n\t\u003CCard title=\"Change page layout\" icon=\"document\">\n\t\tDelete `template: splash` in `src/content/docs/index.mdx` to display a\n\t\tsidebar on this page.\n\t\u003C/Card>\n\t\u003CCard title=\"Add new content\" icon=\"add-document\">\n\t\tAdd Markdown or MDX files to `src/content/docs` to create new pages.\n\t\u003C/Card>\n\t\u003CCard title=\"Configure your site\" icon=\"setting\">\n\t\tEdit your `sidebar` and other config in `astro.config.mjs`.\n\t\u003C/Card>\n\t\u003CCard title=\"Read the docs\" icon=\"open-book\">\n\t\tLearn more in [the Starlight Docs](https://starlight.astro.build/).\n\t\u003C/Card>\n\u003C/CardGrid>","src/content/docs/index.mdx","576d77b3cc95d789","guides/example",{"id":25,"data":27,"body":34,"filePath":35,"digest":36,"rendered":37},{"title":28,"description":29,"editUrl":16,"head":30,"template":31,"sidebar":32,"pagefind":16,"draft":20},"Example Guide","A guide in my new Starlight docs site.",[],"doc",{"hidden":20,"attrs":33},{},"# Hello","src/content/docs/guides/example.md","46be7f85f8f2de8e",{"html":38,"metadata":39},"\u003Cdiv class=\"sl-heading-wrapper level-h1\">\u003Ch1 id=\"hello\">Hello\u003C/h1>\u003Ca class=\"sl-anchor-link\" href=\"#hello\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\">\u003Cpath fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\">Section titled “Hello”\u003C/span>\u003C/a>\u003C/div>",{"headings":40,"localImagePaths":45,"remoteImagePaths":46,"frontmatter":47,"imagePaths":48},[41],{"depth":42,"slug":43,"text":44},1,"hello","Hello",[],[],{"title":28,"description":29},[]] \ No newline at end of file +[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.16.16","content-config-digest","28e7bc87492477d9","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[null,null,null],\"rehypePlugins\":[[null,{\"experimentalHeadingIdCompat\":false}],null,[null,{\"themes\":[{\"name\":\"Night Owl No Italics\",\"type\":\"dark\",\"colors\":{\"focusBorder\":\"#122d42\",\"foreground\":\"#d6deeb\",\"disabledForeground\":\"#cccccc80\",\"descriptionForeground\":\"#d6deebb3\",\"errorForeground\":\"#ef5350\",\"icon.foreground\":\"#c5c5c5\",\"contrastActiveBorder\":null,\"contrastBorder\":\"#122d42\",\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#4f4f4f\",\"textLink.activeForeground\":\"#3794ff\",\"textLink.foreground\":\"#3794ff\",\"textPreformat.foreground\":\"#d7ba7d\",\"textSeparator.foreground\":\"#ffffff2e\",\"editor.background\":\"#23262f\",\"editor.foreground\":\"#d6deeb\",\"editorLineNumber.foreground\":\"#4b6479\",\"editorLineNumber.activeForeground\":\"#c5e4fd\",\"editorActiveLineNumber.foreground\":\"#c6c6c6\",\"editor.selectionBackground\":\"#1d3b53\",\"editor.inactiveSelectionBackground\":\"#7e57c25a\",\"editor.selectionHighlightBackground\":\"#5f7e9779\",\"editorError.foreground\":\"#ef5350\",\"editorWarning.foreground\":\"#b39554\",\"editorInfo.foreground\":\"#3794ff\",\"editorHint.foreground\":\"#eeeeeeb2\",\"problemsErrorIcon.foreground\":\"#ef5350\",\"problemsWarningIcon.foreground\":\"#b39554\",\"problemsInfoIcon.foreground\":\"#3794ff\",\"editor.findMatchBackground\":\"#5f7e9779\",\"editor.findMatchHighlightBackground\":\"#1085bb5d\",\"editor.findRangeHighlightBackground\":\"#3a3d4166\",\"editorLink.activeForeground\":\"#4e94ce\",\"editorLightBulb.foreground\":\"#ffcc00\",\"editorLightBulbAutoFix.foreground\":\"#75beff\",\"diffEditor.insertedTextBackground\":\"#99b76d23\",\"diffEditor.insertedTextBorder\":\"#c5e47833\",\"diffEditor.removedTextBackground\":\"#ef535033\",\"diffEditor.removedTextBorder\":\"#ef53504d\",\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#011627\",\"editorStickyScrollHover.background\":\"#2a2d2e\",\"editorInlayHint.background\":\"#5f7e97cc\",\"editorInlayHint.foreground\":\"#ffffff\",\"editorInlayHint.typeBackground\":\"#5f7e97cc\",\"editorInlayHint.typeForeground\":\"#ffffff\",\"editorInlayHint.parameterBackground\":\"#5f7e97cc\",\"editorInlayHint.parameterForeground\":\"#ffffff\",\"editorPane.background\":\"#011627\",\"editorGroup.emptyBackground\":\"#011627\",\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-black)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#011627\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#011627\",\"editorGroup.dropBackground\":\"#7e57c273\",\"editorGroup.dropIntoPromptForeground\":\"#d6deeb\",\"editorGroup.dropIntoPromptBackground\":\"#021320\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#011627\",\"sideBySideEditor.verticalBorder\":\"#011627\",\"scrollbar.shadow\":\"#010b14\",\"scrollbarSlider.background\":\"#ffffff17\",\"scrollbarSlider.hoverBackground\":\"#ffffff40\",\"scrollbarSlider.activeBackground\":\"#084d8180\",\"panel.background\":\"#011627\",\"panel.border\":\"#5f7e97\",\"panelTitle.activeBorder\":\"#5f7e97\",\"panelTitle.activeForeground\":\"#ffffffcc\",\"panelTitle.inactiveForeground\":\"#d6deeb80\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#011627\",\"widget.shadow\":\"#011627\",\"editorWidget.background\":\"#021320\",\"editorWidget.foreground\":\"#d6deeb\",\"editorWidget.border\":\"#5f7e97\",\"quickInput.background\":\"#021320\",\"quickInput.foreground\":\"#d6deeb\",\"quickInputTitle.background\":\"#ffffff1a\",\"pickerGroup.foreground\":\"#d1aaff\",\"pickerGroup.border\":\"#011627\",\"editor.hoverHighlightBackground\":\"#7e57c25a\",\"editorHoverWidget.background\":\"#011627\",\"editorHoverWidget.foreground\":\"#d6deeb\",\"editorHoverWidget.border\":\"#5f7e97\",\"editorHoverWidget.statusBarBackground\":\"#011a2f\",\"titleBar.activeBackground\":\"var(--sl-color-black)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#010e1a\",\"titleBar.inactiveForeground\":\"#eeefff99\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#5a5d5e50\",\"toolbar.activeBackground\":\"#63666750\",\"tab.activeBackground\":\"#0b2942\",\"tab.unfocusedActiveBackground\":\"#0b2942\",\"tab.inactiveBackground\":\"#01111d\",\"tab.unfocusedInactiveBackground\":\"#01111d\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#5f7e97\",\"tab.unfocusedActiveForeground\":\"#5f7e97\",\"tab.unfocusedInactiveForeground\":\"#5f7e97\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#272b3b\",\"tab.lastPinnedBorder\":\"#585858\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":\"#262a39\",\"tab.activeBorderTop\":\"var(--sl-color-accent-high)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#3399cc\",\"tab.inactiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedActiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedInactiveModifiedBorder\":\"#3399cc40\",\"badge.background\":\"#5f7e97\",\"badge.foreground\":\"#ffffff\",\"button.background\":\"#7e57c2cc\",\"button.foreground\":\"#ffffffcc\",\"button.border\":\"#122d42\",\"button.separator\":\"#ffffff52\",\"button.hoverBackground\":\"#7e57c2\",\"button.secondaryBackground\":\"#3a3d41\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#46494e\",\"dropdown.background\":\"#011627\",\"dropdown.foreground\":\"#ffffffcc\",\"dropdown.border\":\"#5f7e97\",\"list.activeSelectionBackground\":\"#234d708c\",\"list.activeSelectionForeground\":\"#ffffff\",\"tree.indentGuidesStroke\":\"#585858\",\"input.background\":\"#0b253a\",\"input.foreground\":\"#ffffffcc\",\"input.placeholderForeground\":\"#5f7e97\",\"inputOption.activeBorder\":\"#ffffffcc\",\"inputOption.hoverBackground\":\"#5a5d5e80\",\"inputOption.activeBackground\":\"#122d4266\",\"inputOption.activeForeground\":\"#ffffff\",\"inputValidation.infoBackground\":\"#00589ef2\",\"inputValidation.infoBorder\":\"#64b5f6\",\"inputValidation.warningBackground\":\"#675700f2\",\"inputValidation.warningBorder\":\"#ffca28\",\"inputValidation.errorBackground\":\"#ab0300f2\",\"inputValidation.errorBorder\":\"#ef5350\",\"keybindingLabel.background\":\"#8080802b\",\"keybindingLabel.foreground\":\"#cccccc\",\"keybindingLabel.border\":\"#33333399\",\"keybindingLabel.bottomBorder\":\"#44444499\",\"menu.foreground\":\"#ffffffcc\",\"menu.background\":\"#011627\",\"menu.selectionForeground\":\"#ffffff\",\"menu.selectionBackground\":\"#234d708c\",\"menu.separatorBackground\":\"#606060\",\"editor.snippetTabstopHighlightBackground\":\"#7c7c74c\",\"editor.snippetFinalTabstopHighlightBorder\":\"#525252\",\"terminal.ansiBlack\":\"#011627\",\"terminal.ansiRed\":\"#ef5350\",\"terminal.ansiGreen\":\"#22da6e\",\"terminal.ansiYellow\":\"#c5e478\",\"terminal.ansiBlue\":\"#82aaff\",\"terminal.ansiMagenta\":\"#c792ea\",\"terminal.ansiCyan\":\"#21c7a8\",\"terminal.ansiWhite\":\"#ffffff\",\"terminal.ansiBrightBlack\":\"#575656\",\"terminal.ansiBrightRed\":\"#ef5350\",\"terminal.ansiBrightGreen\":\"#22da6e\",\"terminal.ansiBrightYellow\":\"#ffeb95\",\"terminal.ansiBrightBlue\":\"#82aaff\",\"terminal.ansiBrightMagenta\":\"#c792ea\",\"terminal.ansiBrightCyan\":\"#7fdbca\",\"terminal.ansiBrightWhite\":\"#ffffff\",\"selection.background\":\"#4373c2\",\"input.border\":\"#5f7e97\",\"punctuation.definition.generic.begin.html\":\"#ef5350f2\",\"progress.background\":\"#7e57c2\",\"breadcrumb.foreground\":\"#a599e9\",\"breadcrumb.focusForeground\":\"#ffffff\",\"breadcrumb.activeSelectionForeground\":\"#ffffff\",\"breadcrumbPicker.background\":\"#001122\",\"list.invalidItemForeground\":\"#975f94\",\"list.dropBackground\":\"#011627\",\"list.focusBackground\":\"#010d18\",\"list.focusForeground\":\"#ffffff\",\"list.highlightForeground\":\"#ffffff\",\"list.hoverBackground\":\"#011627\",\"list.hoverForeground\":\"#ffffff\",\"list.inactiveSelectionBackground\":\"#0e293f\",\"list.inactiveSelectionForeground\":\"#5f7e97\",\"activityBar.background\":\"#011627\",\"activityBar.dropBackground\":\"#5f7e97\",\"activityBar.foreground\":\"#5f7e97\",\"activityBar.border\":\"#011627\",\"activityBarBadge.background\":\"#44596b\",\"activityBarBadge.foreground\":\"#ffffff\",\"sideBar.background\":\"#011627\",\"sideBar.foreground\":\"#89a4bb\",\"sideBar.border\":\"#011627\",\"sideBarTitle.foreground\":\"#5f7e97\",\"sideBarSectionHeader.background\":\"#011627\",\"sideBarSectionHeader.foreground\":\"#5f7e97\",\"editorCursor.foreground\":\"#80a4c2\",\"editor.wordHighlightBackground\":\"#f6bbe533\",\"editor.wordHighlightStrongBackground\":\"#e2a2f433\",\"editor.lineHighlightBackground\":\"#0003\",\"editor.rangeHighlightBackground\":\"#7e57c25a\",\"editorIndentGuide.background\":\"#5e81ce52\",\"editorIndentGuide.activeBackground\":\"#7e97ac\",\"editorRuler.foreground\":\"#5e81ce52\",\"editorCodeLens.foreground\":\"#5e82ceb4\",\"editorBracketMatch.background\":\"#5f7e974d\",\"editorOverviewRuler.currentContentForeground\":\"#7e57c2\",\"editorOverviewRuler.incomingContentForeground\":\"#7e57c2\",\"editorOverviewRuler.commonContentForeground\":\"#7e57c2\",\"editorGutter.background\":\"#011627\",\"editorGutter.modifiedBackground\":\"#e2b93d\",\"editorGutter.addedBackground\":\"#9ccc65\",\"editorGutter.deletedBackground\":\"#ef5350\",\"editorSuggestWidget.background\":\"#2c3043\",\"editorSuggestWidget.border\":\"#2b2f40\",\"editorSuggestWidget.foreground\":\"#d6deeb\",\"editorSuggestWidget.highlightForeground\":\"#ffffff\",\"editorSuggestWidget.selectedBackground\":\"#5f7e97\",\"debugExceptionWidget.background\":\"#011627\",\"debugExceptionWidget.border\":\"#5f7e97\",\"editorMarkerNavigation.background\":\"#0b2942\",\"editorMarkerNavigationError.background\":\"#ef5350\",\"editorMarkerNavigationWarning.background\":\"#ffca28\",\"peekView.border\":\"#5f7e97\",\"peekViewEditor.background\":\"#011627\",\"peekViewEditor.matchHighlightBackground\":\"#7e57c25a\",\"peekViewResult.background\":\"#011627\",\"peekViewResult.fileForeground\":\"#5f7e97\",\"peekViewResult.lineForeground\":\"#5f7e97\",\"peekViewResult.matchHighlightBackground\":\"#ffffffcc\",\"peekViewResult.selectionBackground\":\"#2e3250\",\"peekViewResult.selectionForeground\":\"#5f7e97\",\"peekViewTitle.background\":\"#011627\",\"peekViewTitleDescription.foreground\":\"#697098\",\"peekViewTitleLabel.foreground\":\"#5f7e97\",\"merge.currentHeaderBackground\":\"#5f7e97\",\"merge.incomingHeaderBackground\":\"#7e57c25a\",\"statusBar.background\":\"#011627\",\"statusBar.foreground\":\"#5f7e97\",\"statusBar.border\":\"#262a39\",\"statusBar.debuggingBackground\":\"#202431\",\"statusBar.debuggingBorder\":\"#1f2330\",\"statusBar.noFolderBackground\":\"#011627\",\"statusBar.noFolderBorder\":\"#25293a\",\"statusBarItem.activeBackground\":\"#202431\",\"statusBarItem.hoverBackground\":\"#202431\",\"statusBarItem.prominentBackground\":\"#202431\",\"statusBarItem.prominentHoverBackground\":\"#202431\",\"notifications.background\":\"#01111d\",\"notifications.border\":\"#262a39\",\"notificationCenter.border\":\"#262a39\",\"notificationToast.border\":\"#262a39\",\"notifications.foreground\":\"#ffffffcc\",\"notificationLink.foreground\":\"#80cbc4\",\"extensionButton.prominentForeground\":\"#ffffffcc\",\"extensionButton.prominentBackground\":\"#7e57c2cc\",\"extensionButton.prominentHoverBackground\":\"#7e57c2\",\"terminal.selectionBackground\":\"#1b90dd4d\",\"terminalCursor.background\":\"#234d70\",\"debugToolBar.background\":\"#011627\",\"welcomePage.buttonBackground\":\"#011627\",\"welcomePage.buttonHoverBackground\":\"#011627\",\"walkThrough.embeddedEditorBackground\":\"#011627\",\"gitDecoration.modifiedResourceForeground\":\"#a2bffc\",\"gitDecoration.deletedResourceForeground\":\"#ef535090\",\"gitDecoration.untrackedResourceForeground\":\"#c5e478ff\",\"gitDecoration.ignoredResourceForeground\":\"#395a75\",\"gitDecoration.conflictingResourceForeground\":\"#ffeb95cc\",\"source.elm\":\"#5f7e97\",\"string.quoted.single.js\":\"#ffffff\",\"meta.objectliteral.js\":\"#82aaff\"},\"fg\":\"#d6deeb\",\"bg\":\"#23262f\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#a2bffc\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#f27775fe\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#d6deeb\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#919f9f\",\"fontStyle\":\"\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#5ca7e4\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#caece6\",\"fontStyle\":\"\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"fontStyle\":\"\",\"foreground\":\"#c5e478\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"background\":\"#ff2c83\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#d3423e\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#919f9f\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#cdebf7\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#ffffff\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#80cbc4\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#57eaf1\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#fad430\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#faf39f\",\"fontStyle\":\"\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#82aaff\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Comparison, returns, imports, and Keyword Operator Ruby\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#989da0\",\"background\":\"#F78C6C\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"background\":\"#8BD649\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#ec5f67\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#ef787f\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#31e1eb\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Unquoted String Zone\",\"scope\":[\"string.unquoted.preprocessor.message.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Region\",\"scope\":[\"punctuation.separator.hash.cs\",\"keyword.preprocessor.region.cs\",\"keyword.preprocessor.endregion.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"bold\"}},{\"name\":\"C# Other Variables\",\"scope\":[\"variable.other.object.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Enum\",\"scope\":[\"entity.name.type.enum.cs\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Dart String\",\"scope\":[\"string.interpolated.single.dart\",\"string.interpolated.double.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Dart Class\",\"scope\":[\"support.class.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#ff6d6d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#dddddd\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#e0dec6\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#6ae9f0\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#78ccf0\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#8b96ea\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#c789d6\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#ff869a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#d6deeb\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#8eace3\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#d7dbe0\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-6)\",\"terminalBackground\":\"var(--sl-color-gray-6)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-6)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#ffffff17\",\"markBorderColor\":\"#ffffff40\"}}},{\"name\":\"Night Owl Light\",\"type\":\"light\",\"colors\":{\"focusBorder\":\"#93a1a1\",\"foreground\":\"#403f53\",\"disabledForeground\":\"#61616180\",\"descriptionForeground\":\"#403f53\",\"errorForeground\":\"#403f53\",\"icon.foreground\":\"#424242\",\"contrastActiveBorder\":null,\"contrastBorder\":null,\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#dcdcdc66\",\"textLink.activeForeground\":\"#006ab1\",\"textLink.foreground\":\"#006ab1\",\"textPreformat.foreground\":\"#a31515\",\"textSeparator.foreground\":\"#0000002e\",\"editor.background\":\"#f6f7f9\",\"editor.foreground\":\"#403f53\",\"editorLineNumber.foreground\":\"#90a7b2\",\"editorLineNumber.activeForeground\":\"#403f53\",\"editorActiveLineNumber.foreground\":\"#0b216f\",\"editor.selectionBackground\":\"#e0e0e0\",\"editor.inactiveSelectionBackground\":\"#e0e0e080\",\"editor.selectionHighlightBackground\":\"#339cec33\",\"editorError.foreground\":\"#e64d49\",\"editorWarning.foreground\":\"#daaa01\",\"editorInfo.foreground\":\"#1a85ff\",\"editorHint.foreground\":\"#6c6c6c\",\"problemsErrorIcon.foreground\":\"#e64d49\",\"problemsWarningIcon.foreground\":\"#daaa01\",\"problemsInfoIcon.foreground\":\"#1a85ff\",\"editor.findMatchBackground\":\"#93a1a16c\",\"editor.findMatchHighlightBackground\":\"#93a1a16c\",\"editor.findRangeHighlightBackground\":\"#7497a633\",\"editorLink.activeForeground\":\"#0000ff\",\"editorLightBulb.foreground\":\"#ddb100\",\"editorLightBulbAutoFix.foreground\":\"#007acc\",\"diffEditor.insertedTextBackground\":\"#9ccc2c40\",\"diffEditor.insertedTextBorder\":null,\"diffEditor.removedTextBackground\":\"#ff000033\",\"diffEditor.removedTextBorder\":null,\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#fbfbfb\",\"editorStickyScrollHover.background\":\"#f0f0f0\",\"editorInlayHint.background\":\"#2aa29899\",\"editorInlayHint.foreground\":\"#f0f0f0\",\"editorInlayHint.typeBackground\":\"#2aa29899\",\"editorInlayHint.typeForeground\":\"#f0f0f0\",\"editorInlayHint.parameterBackground\":\"#2aa29899\",\"editorInlayHint.parameterForeground\":\"#f0f0f0\",\"editorPane.background\":\"#fbfbfb\",\"editorGroup.emptyBackground\":null,\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-gray-6)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#f0f0f0\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#f0f0f0\",\"editorGroup.dropBackground\":\"#2677cb2d\",\"editorGroup.dropIntoPromptForeground\":\"#403f53\",\"editorGroup.dropIntoPromptBackground\":\"#f0f0f0\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#f0f0f0\",\"sideBySideEditor.verticalBorder\":\"#f0f0f0\",\"scrollbar.shadow\":\"#cccccc\",\"scrollbarSlider.background\":\"#0000001a\",\"scrollbarSlider.hoverBackground\":\"#00000055\",\"scrollbarSlider.activeBackground\":\"#00000099\",\"panel.background\":\"#f0f0f0\",\"panel.border\":\"#d9d9d9\",\"panelTitle.activeBorder\":\"#424242\",\"panelTitle.activeForeground\":\"#424242\",\"panelTitle.inactiveForeground\":\"#424242bf\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#f6f6f6\",\"widget.shadow\":\"#d9d9d9\",\"editorWidget.background\":\"#f0f0f0\",\"editorWidget.foreground\":\"#403f53\",\"editorWidget.border\":\"#d9d9d9\",\"quickInput.background\":\"#f0f0f0\",\"quickInput.foreground\":\"#403f53\",\"quickInputTitle.background\":\"#0000000f\",\"pickerGroup.foreground\":\"#403f53\",\"pickerGroup.border\":\"#d9d9d9\",\"editor.hoverHighlightBackground\":\"#339cec33\",\"editorHoverWidget.background\":\"#f0f0f0\",\"editorHoverWidget.foreground\":\"#403f53\",\"editorHoverWidget.border\":\"#d9d9d9\",\"editorHoverWidget.statusBarBackground\":\"#e4e4e4\",\"titleBar.activeBackground\":\"var(--sl-color-gray-6)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#f0f0f099\",\"titleBar.inactiveForeground\":\"#33333399\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#b8b8b850\",\"toolbar.activeBackground\":\"#a6a6a650\",\"tab.activeBackground\":\"#f6f6f6\",\"tab.unfocusedActiveBackground\":\"#f6f6f6\",\"tab.inactiveBackground\":\"#f0f0f0\",\"tab.unfocusedInactiveBackground\":\"#f0f0f0\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#403f53\",\"tab.unfocusedActiveForeground\":\"#403f53b3\",\"tab.unfocusedInactiveForeground\":\"#403f5380\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#f0f0f0\",\"tab.lastPinnedBorder\":\"#a9a9a9\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":null,\"tab.activeBorderTop\":\"var(--sl-color-accent)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#2aa298\",\"tab.inactiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedActiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedInactiveModifiedBorder\":\"#93a1a1\",\"badge.background\":\"#2aa298\",\"badge.foreground\":\"#f0f0f0\",\"button.background\":\"#2aa298\",\"button.foreground\":\"#f0f0f0\",\"button.border\":null,\"button.separator\":\"#f0f0f066\",\"button.hoverBackground\":\"#22827a\",\"button.secondaryBackground\":\"#5f6a79\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#4c5561\",\"dropdown.background\":\"#f0f0f0\",\"dropdown.foreground\":\"#403f53\",\"dropdown.border\":\"#d9d9d9\",\"list.activeSelectionBackground\":\"#d3e8f8\",\"list.activeSelectionForeground\":\"#403f53\",\"tree.indentGuidesStroke\":\"#a9a9a9\",\"input.background\":\"#f0f0f0\",\"input.foreground\":\"#403f53\",\"input.placeholderForeground\":\"#93a1a1\",\"inputOption.activeBorder\":\"#2aa298\",\"inputOption.hoverBackground\":\"#b8b8b850\",\"inputOption.activeBackground\":\"#93a1a133\",\"inputOption.activeForeground\":\"#000000\",\"inputValidation.infoBackground\":\"#f0f0f0\",\"inputValidation.infoBorder\":\"#d0d0d0\",\"inputValidation.warningBackground\":\"#daaa01\",\"inputValidation.warningBorder\":\"#e0af02\",\"inputValidation.errorBackground\":\"#f76e6e\",\"inputValidation.errorBorder\":\"#de3d3b\",\"keybindingLabel.background\":\"#dddddd66\",\"keybindingLabel.foreground\":\"#555555\",\"keybindingLabel.border\":\"#cccccc66\",\"keybindingLabel.bottomBorder\":\"#bbbbbb66\",\"menu.foreground\":\"#403f53\",\"menu.background\":\"#f0f0f0\",\"menu.selectionForeground\":\"#403f53\",\"menu.selectionBackground\":\"#d3e8f8\",\"menu.separatorBackground\":\"#d4d4d4\",\"editor.snippetTabstopHighlightBackground\":\"#0a326433\",\"editor.snippetFinalTabstopHighlightBorder\":\"#0a326480\",\"terminal.ansiBlack\":\"#403f53\",\"terminal.ansiRed\":\"#de3d3b\",\"terminal.ansiGreen\":\"#08916a\",\"terminal.ansiYellow\":\"#e0af02\",\"terminal.ansiBlue\":\"#288ed7\",\"terminal.ansiMagenta\":\"#d6438a\",\"terminal.ansiCyan\":\"#2aa298\",\"terminal.ansiWhite\":\"#f0f0f0\",\"terminal.ansiBrightBlack\":\"#403f53\",\"terminal.ansiBrightRed\":\"#de3d3b\",\"terminal.ansiBrightGreen\":\"#08916a\",\"terminal.ansiBrightYellow\":\"#daaa01\",\"terminal.ansiBrightBlue\":\"#288ed7\",\"terminal.ansiBrightMagenta\":\"#d6438a\",\"terminal.ansiBrightCyan\":\"#2aa298\",\"terminal.ansiBrightWhite\":\"#f0f0f0\",\"selection.background\":\"#7a8181ad\",\"notifications.background\":\"#f0f0f0\",\"notifications.foreground\":\"#403f53\",\"notificationLink.foreground\":\"#994cc3\",\"notifications.border\":\"#cccccc\",\"notificationCenter.border\":\"#cccccc\",\"notificationToast.border\":\"#cccccc\",\"notificationCenterHeader.foreground\":\"#403f53\",\"notificationCenterHeader.background\":\"#f0f0f0\",\"input.border\":\"#d9d9d9\",\"progressBar.background\":\"#2aa298\",\"list.inactiveSelectionBackground\":\"#e0e7ea\",\"list.inactiveSelectionForeground\":\"#403f53\",\"list.focusBackground\":\"#d3e8f8\",\"list.hoverBackground\":\"#d3e8f8\",\"list.focusForeground\":\"#403f53\",\"list.hoverForeground\":\"#403f53\",\"list.highlightForeground\":\"#403f53\",\"list.errorForeground\":\"#e64d49\",\"list.warningForeground\":\"#daaa01\",\"activityBar.background\":\"#f0f0f0\",\"activityBar.foreground\":\"#403f53\",\"activityBar.dropBackground\":\"#d0d0d0\",\"activityBarBadge.background\":\"#403f53\",\"activityBarBadge.foreground\":\"#f0f0f0\",\"activityBar.border\":\"#f0f0f0\",\"sideBar.background\":\"#f0f0f0\",\"sideBar.foreground\":\"#403f53\",\"sideBarTitle.foreground\":\"#403f53\",\"sideBar.border\":\"#f0f0f0\",\"editorGroup.background\":\"#f6f6f6\",\"editorCursor.foreground\":\"#90a7b2\",\"editor.wordHighlightBackground\":\"#339cec33\",\"editor.wordHighlightStrongBackground\":\"#007dd659\",\"editor.lineHighlightBackground\":\"#f0f0f0\",\"editor.rangeHighlightBackground\":\"#7497a633\",\"editorWhitespace.foreground\":\"#d9d9d9\",\"editorIndentGuide.background\":\"#d9d9d9\",\"editorCodeLens.foreground\":\"#403f53\",\"editorBracketMatch.background\":\"#d3e8f8\",\"editorBracketMatch.border\":\"#2aa298\",\"editorError.border\":\"#fbfbfb\",\"editorWarning.border\":\"#daaa01\",\"editorGutter.addedBackground\":\"#49d0c5\",\"editorGutter.modifiedBackground\":\"#6fbef6\",\"editorGutter.deletedBackground\":\"#f76e6e\",\"editorRuler.foreground\":\"#d9d9d9\",\"editorOverviewRuler.errorForeground\":\"#e64d49\",\"editorOverviewRuler.warningForeground\":\"#daaa01\",\"editorSuggestWidget.background\":\"#f0f0f0\",\"editorSuggestWidget.foreground\":\"#403f53\",\"editorSuggestWidget.highlightForeground\":\"#403f53\",\"editorSuggestWidget.selectedBackground\":\"#d3e8f8\",\"editorSuggestWidget.border\":\"#d9d9d9\",\"debugExceptionWidget.background\":\"#f0f0f0\",\"debugExceptionWidget.border\":\"#d9d9d9\",\"editorMarkerNavigation.background\":\"#d0d0d0\",\"editorMarkerNavigationError.background\":\"#f76e6e\",\"editorMarkerNavigationWarning.background\":\"#daaa01\",\"debugToolBar.background\":\"#f0f0f0\",\"extensionButton.prominentBackground\":\"#2aa298\",\"extensionButton.prominentForeground\":\"#f0f0f0\",\"statusBar.background\":\"#f0f0f0\",\"statusBar.border\":\"#f0f0f0\",\"statusBar.debuggingBackground\":\"#f0f0f0\",\"statusBar.debuggingForeground\":\"#403f53\",\"statusBar.foreground\":\"#403f53\",\"statusBar.noFolderBackground\":\"#f0f0f0\",\"statusBar.noFolderForeground\":\"#403f53\",\"peekView.border\":\"#d9d9d9\",\"peekViewEditor.background\":\"#f6f6f6\",\"peekViewEditorGutter.background\":\"#f6f6f6\",\"peekViewEditor.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.background\":\"#f0f0f0\",\"peekViewResult.fileForeground\":\"#403f53\",\"peekViewResult.lineForeground\":\"#403f53\",\"peekViewResult.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.selectionBackground\":\"#e0e7ea\",\"peekViewResult.selectionForeground\":\"#403f53\",\"peekViewTitle.background\":\"#f0f0f0\",\"peekViewTitleLabel.foreground\":\"#403f53\",\"peekViewTitleDescription.foreground\":\"#403f53\",\"terminal.foreground\":\"#403f53\"},\"fg\":\"#403f53\",\"bg\":\"#f6f7f9\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#556484\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#ae3c3afd\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#403f53\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#5f636f\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#3a688f\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"foreground\":\"#bb2060\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#5d6376\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#58656a\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#646464\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#286d70\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword Operator Comparison, imports, returns and Keyword Operator Ruby\",\"scope\":[\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.module.ruby\",\"keyword.control.class.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Control Conditional\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"foreground\":\"#486e26\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#a64348\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#186e73\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#984e4d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#3e697c\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#555ea2\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#111111\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#7c5686\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Symbols\",\"scope\":[\"constant.language.symbol.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#954f5a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#403f53\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\",\"variable.other.property.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#76578b\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-7)\",\"terminalBackground\":\"var(--sl-color-gray-7)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-7)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#0000001a\",\"markBorderColor\":\"#00000055\"}}}],\"defaultLocale\":\"en\",\"cascadeLayer\":\"starlight.components\",\"styleOverrides\":{\"borderRadius\":\"0px\",\"borderWidth\":\"1px\",\"codePaddingBlock\":\"0.75rem\",\"codePaddingInline\":\"1rem\",\"codeFontFamily\":\"var(--__sl-font-mono)\",\"codeFontSize\":\"var(--sl-text-code)\",\"codeLineHeight\":\"var(--sl-line-height)\",\"uiFontFamily\":\"var(--__sl-font)\",\"textMarkers\":{\"lineDiffIndicatorMarginLeft\":\"0.25rem\",\"defaultChroma\":\"45\",\"backgroundOpacity\":\"60%\"}},\"plugins\":[{\"name\":\"Starlight Plugin\",\"hooks\":{}},{\"name\":\"astro-expressive-code\",\"hooks\":{}}]}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[{\"codes\":[\"en\"],\"path\":\"en\"},{\"codes\":[\"ru\"],\"path\":\"ru\"}],\"routing\":{\"prefixDefaultLocale\":true,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","docs",["Map",11,12,25,26],"en",{"id":11,"data":13,"body":22,"filePath":23,"digest":24,"deferredRender":16},{"title":14,"description":15,"editUrl":16,"head":17,"template":18,"sidebar":19,"pagefind":16,"draft":20},"Welcome","Get started building your docs site with Starlight.",true,[],"splash",{"hidden":20,"attrs":21},false,{},"import { Card, CardGrid } from '@astrojs/starlight/components';\n\n## Next steps\n\n\u003CCardGrid stagger>\n\t\u003CCard title=\"Update content\" icon=\"pencil\">\n\t\tEdit `src/content/docs/index.mdx` to see this page change.\n\t\u003C/Card>\n\t\u003CCard title=\"Change page layout\" icon=\"document\">\n\t\tDelete `template: splash` in `src/content/docs/index.mdx` to display a\n\t\tsidebar on this page.\n\t\u003C/Card>\n\t\u003CCard title=\"Add new content\" icon=\"add-document\">\n\t\tAdd Markdown or MDX files to `src/content/docs` to create new pages.\n\t\u003C/Card>\n\t\u003CCard title=\"Configure your site\" icon=\"setting\">\n\t\tEdit your `sidebar` and other config in `astro.config.mjs`.\n\t\u003C/Card>\n\t\u003CCard title=\"Read the docs\" icon=\"open-book\">\n\t\tLearn more in [the Starlight Docs](https://starlight.astro.build/).\n\t\u003C/Card>\n\u003C/CardGrid>","src/content/docs/en/index.mdx","576d77b3cc95d789","en/guides/example",{"id":25,"data":27,"body":34,"filePath":35,"digest":36,"rendered":37},{"title":28,"description":29,"editUrl":16,"head":30,"template":31,"sidebar":32,"pagefind":16,"draft":20},"Example Guide","A guide in my new Starlight docs site.",[],"doc",{"hidden":20,"attrs":33},{},"# Hello","src/content/docs/en/guides/example.md","46be7f85f8f2de8e",{"html":38,"metadata":39},"\u003Cdiv class=\"sl-heading-wrapper level-h1\">\u003Ch1 id=\"hello\">Hello\u003C/h1>\u003Ca class=\"sl-anchor-link\" href=\"#hello\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\">\u003Cpath fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\">Section titled “Hello”\u003C/span>\u003C/a>\u003C/div>",{"headings":40,"localImagePaths":45,"remoteImagePaths":46,"frontmatter":47,"imagePaths":48},[41],{"depth":42,"slug":43,"text":44},1,"hello","Hello",[],[],{"title":28,"description":29},[]] \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs index 820c59b245..b3caa065da 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -7,6 +7,15 @@ export default defineConfig({ integrations: [ starlight({ title: 'Feature-Sliced Design', + defaultLocale: 'en', + locales: { + en: { + label: 'English', + }, + ru: { + label: 'Русский', + } + }, social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }], sidebar: [ { diff --git a/src/content/docs/guides/example.md b/src/content/docs/en/guides/example.md similarity index 100% rename from src/content/docs/guides/example.md rename to src/content/docs/en/guides/example.md diff --git a/src/content/docs/index.mdx b/src/content/docs/en/index.mdx similarity index 100% rename from src/content/docs/index.mdx rename to src/content/docs/en/index.mdx From 934d8a15b652f76bc5db1470df315254fde7f5ff Mon Sep 17 00:00:00 2001 From: Solant Date: Mon, 2 Feb 2026 22:34:57 +0100 Subject: [PATCH 03/45] add get-started pages --- .astro/content-modules.mjs | 4 +- .astro/data-store.json | 2 +- astro.config.mjs | 20 ++- package.json | 1 + pnpm-lock.yaml | 35 ++++- src/content/docs/en/guides/example.md | 6 - src/content/docs/get-started/faq.mdx | 68 +++++++++ src/content/docs/get-started/overview.mdx | 166 ++++++++++++++++++++++ src/content/docs/{en => }/index.mdx | 0 src/pages/examples/index.tsx | 48 +++---- 10 files changed, 305 insertions(+), 45 deletions(-) delete mode 100644 src/content/docs/en/guides/example.md create mode 100644 src/content/docs/get-started/faq.mdx create mode 100644 src/content/docs/get-started/overview.mdx rename src/content/docs/{en => }/index.mdx (100%) diff --git a/.astro/content-modules.mjs b/.astro/content-modules.mjs index 1cdaaaae6a..8e20da3e68 100644 --- a/.astro/content-modules.mjs +++ b/.astro/content-modules.mjs @@ -1,4 +1,6 @@ export default new Map([ -["src/content/docs/en/index.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Fen%2Findex.mdx&astroContentModuleFlag=true")]]); +["src/content/docs/get-started/overview.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Fget-started%2Foverview.mdx&astroContentModuleFlag=true")], +["src/content/docs/get-started/faq.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Fget-started%2Ffaq.mdx&astroContentModuleFlag=true")], +["src/content/docs/index.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Findex.mdx&astroContentModuleFlag=true")]]); \ No newline at end of file diff --git a/.astro/data-store.json b/.astro/data-store.json index aed938fbd2..6f9276164c 100644 --- a/.astro/data-store.json +++ b/.astro/data-store.json @@ -1 +1 @@ -[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.16.16","content-config-digest","28e7bc87492477d9","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[null,null,null],\"rehypePlugins\":[[null,{\"experimentalHeadingIdCompat\":false}],null,[null,{\"themes\":[{\"name\":\"Night Owl No Italics\",\"type\":\"dark\",\"colors\":{\"focusBorder\":\"#122d42\",\"foreground\":\"#d6deeb\",\"disabledForeground\":\"#cccccc80\",\"descriptionForeground\":\"#d6deebb3\",\"errorForeground\":\"#ef5350\",\"icon.foreground\":\"#c5c5c5\",\"contrastActiveBorder\":null,\"contrastBorder\":\"#122d42\",\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#4f4f4f\",\"textLink.activeForeground\":\"#3794ff\",\"textLink.foreground\":\"#3794ff\",\"textPreformat.foreground\":\"#d7ba7d\",\"textSeparator.foreground\":\"#ffffff2e\",\"editor.background\":\"#23262f\",\"editor.foreground\":\"#d6deeb\",\"editorLineNumber.foreground\":\"#4b6479\",\"editorLineNumber.activeForeground\":\"#c5e4fd\",\"editorActiveLineNumber.foreground\":\"#c6c6c6\",\"editor.selectionBackground\":\"#1d3b53\",\"editor.inactiveSelectionBackground\":\"#7e57c25a\",\"editor.selectionHighlightBackground\":\"#5f7e9779\",\"editorError.foreground\":\"#ef5350\",\"editorWarning.foreground\":\"#b39554\",\"editorInfo.foreground\":\"#3794ff\",\"editorHint.foreground\":\"#eeeeeeb2\",\"problemsErrorIcon.foreground\":\"#ef5350\",\"problemsWarningIcon.foreground\":\"#b39554\",\"problemsInfoIcon.foreground\":\"#3794ff\",\"editor.findMatchBackground\":\"#5f7e9779\",\"editor.findMatchHighlightBackground\":\"#1085bb5d\",\"editor.findRangeHighlightBackground\":\"#3a3d4166\",\"editorLink.activeForeground\":\"#4e94ce\",\"editorLightBulb.foreground\":\"#ffcc00\",\"editorLightBulbAutoFix.foreground\":\"#75beff\",\"diffEditor.insertedTextBackground\":\"#99b76d23\",\"diffEditor.insertedTextBorder\":\"#c5e47833\",\"diffEditor.removedTextBackground\":\"#ef535033\",\"diffEditor.removedTextBorder\":\"#ef53504d\",\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#011627\",\"editorStickyScrollHover.background\":\"#2a2d2e\",\"editorInlayHint.background\":\"#5f7e97cc\",\"editorInlayHint.foreground\":\"#ffffff\",\"editorInlayHint.typeBackground\":\"#5f7e97cc\",\"editorInlayHint.typeForeground\":\"#ffffff\",\"editorInlayHint.parameterBackground\":\"#5f7e97cc\",\"editorInlayHint.parameterForeground\":\"#ffffff\",\"editorPane.background\":\"#011627\",\"editorGroup.emptyBackground\":\"#011627\",\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-black)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#011627\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#011627\",\"editorGroup.dropBackground\":\"#7e57c273\",\"editorGroup.dropIntoPromptForeground\":\"#d6deeb\",\"editorGroup.dropIntoPromptBackground\":\"#021320\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#011627\",\"sideBySideEditor.verticalBorder\":\"#011627\",\"scrollbar.shadow\":\"#010b14\",\"scrollbarSlider.background\":\"#ffffff17\",\"scrollbarSlider.hoverBackground\":\"#ffffff40\",\"scrollbarSlider.activeBackground\":\"#084d8180\",\"panel.background\":\"#011627\",\"panel.border\":\"#5f7e97\",\"panelTitle.activeBorder\":\"#5f7e97\",\"panelTitle.activeForeground\":\"#ffffffcc\",\"panelTitle.inactiveForeground\":\"#d6deeb80\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#011627\",\"widget.shadow\":\"#011627\",\"editorWidget.background\":\"#021320\",\"editorWidget.foreground\":\"#d6deeb\",\"editorWidget.border\":\"#5f7e97\",\"quickInput.background\":\"#021320\",\"quickInput.foreground\":\"#d6deeb\",\"quickInputTitle.background\":\"#ffffff1a\",\"pickerGroup.foreground\":\"#d1aaff\",\"pickerGroup.border\":\"#011627\",\"editor.hoverHighlightBackground\":\"#7e57c25a\",\"editorHoverWidget.background\":\"#011627\",\"editorHoverWidget.foreground\":\"#d6deeb\",\"editorHoverWidget.border\":\"#5f7e97\",\"editorHoverWidget.statusBarBackground\":\"#011a2f\",\"titleBar.activeBackground\":\"var(--sl-color-black)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#010e1a\",\"titleBar.inactiveForeground\":\"#eeefff99\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#5a5d5e50\",\"toolbar.activeBackground\":\"#63666750\",\"tab.activeBackground\":\"#0b2942\",\"tab.unfocusedActiveBackground\":\"#0b2942\",\"tab.inactiveBackground\":\"#01111d\",\"tab.unfocusedInactiveBackground\":\"#01111d\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#5f7e97\",\"tab.unfocusedActiveForeground\":\"#5f7e97\",\"tab.unfocusedInactiveForeground\":\"#5f7e97\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#272b3b\",\"tab.lastPinnedBorder\":\"#585858\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":\"#262a39\",\"tab.activeBorderTop\":\"var(--sl-color-accent-high)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#3399cc\",\"tab.inactiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedActiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedInactiveModifiedBorder\":\"#3399cc40\",\"badge.background\":\"#5f7e97\",\"badge.foreground\":\"#ffffff\",\"button.background\":\"#7e57c2cc\",\"button.foreground\":\"#ffffffcc\",\"button.border\":\"#122d42\",\"button.separator\":\"#ffffff52\",\"button.hoverBackground\":\"#7e57c2\",\"button.secondaryBackground\":\"#3a3d41\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#46494e\",\"dropdown.background\":\"#011627\",\"dropdown.foreground\":\"#ffffffcc\",\"dropdown.border\":\"#5f7e97\",\"list.activeSelectionBackground\":\"#234d708c\",\"list.activeSelectionForeground\":\"#ffffff\",\"tree.indentGuidesStroke\":\"#585858\",\"input.background\":\"#0b253a\",\"input.foreground\":\"#ffffffcc\",\"input.placeholderForeground\":\"#5f7e97\",\"inputOption.activeBorder\":\"#ffffffcc\",\"inputOption.hoverBackground\":\"#5a5d5e80\",\"inputOption.activeBackground\":\"#122d4266\",\"inputOption.activeForeground\":\"#ffffff\",\"inputValidation.infoBackground\":\"#00589ef2\",\"inputValidation.infoBorder\":\"#64b5f6\",\"inputValidation.warningBackground\":\"#675700f2\",\"inputValidation.warningBorder\":\"#ffca28\",\"inputValidation.errorBackground\":\"#ab0300f2\",\"inputValidation.errorBorder\":\"#ef5350\",\"keybindingLabel.background\":\"#8080802b\",\"keybindingLabel.foreground\":\"#cccccc\",\"keybindingLabel.border\":\"#33333399\",\"keybindingLabel.bottomBorder\":\"#44444499\",\"menu.foreground\":\"#ffffffcc\",\"menu.background\":\"#011627\",\"menu.selectionForeground\":\"#ffffff\",\"menu.selectionBackground\":\"#234d708c\",\"menu.separatorBackground\":\"#606060\",\"editor.snippetTabstopHighlightBackground\":\"#7c7c74c\",\"editor.snippetFinalTabstopHighlightBorder\":\"#525252\",\"terminal.ansiBlack\":\"#011627\",\"terminal.ansiRed\":\"#ef5350\",\"terminal.ansiGreen\":\"#22da6e\",\"terminal.ansiYellow\":\"#c5e478\",\"terminal.ansiBlue\":\"#82aaff\",\"terminal.ansiMagenta\":\"#c792ea\",\"terminal.ansiCyan\":\"#21c7a8\",\"terminal.ansiWhite\":\"#ffffff\",\"terminal.ansiBrightBlack\":\"#575656\",\"terminal.ansiBrightRed\":\"#ef5350\",\"terminal.ansiBrightGreen\":\"#22da6e\",\"terminal.ansiBrightYellow\":\"#ffeb95\",\"terminal.ansiBrightBlue\":\"#82aaff\",\"terminal.ansiBrightMagenta\":\"#c792ea\",\"terminal.ansiBrightCyan\":\"#7fdbca\",\"terminal.ansiBrightWhite\":\"#ffffff\",\"selection.background\":\"#4373c2\",\"input.border\":\"#5f7e97\",\"punctuation.definition.generic.begin.html\":\"#ef5350f2\",\"progress.background\":\"#7e57c2\",\"breadcrumb.foreground\":\"#a599e9\",\"breadcrumb.focusForeground\":\"#ffffff\",\"breadcrumb.activeSelectionForeground\":\"#ffffff\",\"breadcrumbPicker.background\":\"#001122\",\"list.invalidItemForeground\":\"#975f94\",\"list.dropBackground\":\"#011627\",\"list.focusBackground\":\"#010d18\",\"list.focusForeground\":\"#ffffff\",\"list.highlightForeground\":\"#ffffff\",\"list.hoverBackground\":\"#011627\",\"list.hoverForeground\":\"#ffffff\",\"list.inactiveSelectionBackground\":\"#0e293f\",\"list.inactiveSelectionForeground\":\"#5f7e97\",\"activityBar.background\":\"#011627\",\"activityBar.dropBackground\":\"#5f7e97\",\"activityBar.foreground\":\"#5f7e97\",\"activityBar.border\":\"#011627\",\"activityBarBadge.background\":\"#44596b\",\"activityBarBadge.foreground\":\"#ffffff\",\"sideBar.background\":\"#011627\",\"sideBar.foreground\":\"#89a4bb\",\"sideBar.border\":\"#011627\",\"sideBarTitle.foreground\":\"#5f7e97\",\"sideBarSectionHeader.background\":\"#011627\",\"sideBarSectionHeader.foreground\":\"#5f7e97\",\"editorCursor.foreground\":\"#80a4c2\",\"editor.wordHighlightBackground\":\"#f6bbe533\",\"editor.wordHighlightStrongBackground\":\"#e2a2f433\",\"editor.lineHighlightBackground\":\"#0003\",\"editor.rangeHighlightBackground\":\"#7e57c25a\",\"editorIndentGuide.background\":\"#5e81ce52\",\"editorIndentGuide.activeBackground\":\"#7e97ac\",\"editorRuler.foreground\":\"#5e81ce52\",\"editorCodeLens.foreground\":\"#5e82ceb4\",\"editorBracketMatch.background\":\"#5f7e974d\",\"editorOverviewRuler.currentContentForeground\":\"#7e57c2\",\"editorOverviewRuler.incomingContentForeground\":\"#7e57c2\",\"editorOverviewRuler.commonContentForeground\":\"#7e57c2\",\"editorGutter.background\":\"#011627\",\"editorGutter.modifiedBackground\":\"#e2b93d\",\"editorGutter.addedBackground\":\"#9ccc65\",\"editorGutter.deletedBackground\":\"#ef5350\",\"editorSuggestWidget.background\":\"#2c3043\",\"editorSuggestWidget.border\":\"#2b2f40\",\"editorSuggestWidget.foreground\":\"#d6deeb\",\"editorSuggestWidget.highlightForeground\":\"#ffffff\",\"editorSuggestWidget.selectedBackground\":\"#5f7e97\",\"debugExceptionWidget.background\":\"#011627\",\"debugExceptionWidget.border\":\"#5f7e97\",\"editorMarkerNavigation.background\":\"#0b2942\",\"editorMarkerNavigationError.background\":\"#ef5350\",\"editorMarkerNavigationWarning.background\":\"#ffca28\",\"peekView.border\":\"#5f7e97\",\"peekViewEditor.background\":\"#011627\",\"peekViewEditor.matchHighlightBackground\":\"#7e57c25a\",\"peekViewResult.background\":\"#011627\",\"peekViewResult.fileForeground\":\"#5f7e97\",\"peekViewResult.lineForeground\":\"#5f7e97\",\"peekViewResult.matchHighlightBackground\":\"#ffffffcc\",\"peekViewResult.selectionBackground\":\"#2e3250\",\"peekViewResult.selectionForeground\":\"#5f7e97\",\"peekViewTitle.background\":\"#011627\",\"peekViewTitleDescription.foreground\":\"#697098\",\"peekViewTitleLabel.foreground\":\"#5f7e97\",\"merge.currentHeaderBackground\":\"#5f7e97\",\"merge.incomingHeaderBackground\":\"#7e57c25a\",\"statusBar.background\":\"#011627\",\"statusBar.foreground\":\"#5f7e97\",\"statusBar.border\":\"#262a39\",\"statusBar.debuggingBackground\":\"#202431\",\"statusBar.debuggingBorder\":\"#1f2330\",\"statusBar.noFolderBackground\":\"#011627\",\"statusBar.noFolderBorder\":\"#25293a\",\"statusBarItem.activeBackground\":\"#202431\",\"statusBarItem.hoverBackground\":\"#202431\",\"statusBarItem.prominentBackground\":\"#202431\",\"statusBarItem.prominentHoverBackground\":\"#202431\",\"notifications.background\":\"#01111d\",\"notifications.border\":\"#262a39\",\"notificationCenter.border\":\"#262a39\",\"notificationToast.border\":\"#262a39\",\"notifications.foreground\":\"#ffffffcc\",\"notificationLink.foreground\":\"#80cbc4\",\"extensionButton.prominentForeground\":\"#ffffffcc\",\"extensionButton.prominentBackground\":\"#7e57c2cc\",\"extensionButton.prominentHoverBackground\":\"#7e57c2\",\"terminal.selectionBackground\":\"#1b90dd4d\",\"terminalCursor.background\":\"#234d70\",\"debugToolBar.background\":\"#011627\",\"welcomePage.buttonBackground\":\"#011627\",\"welcomePage.buttonHoverBackground\":\"#011627\",\"walkThrough.embeddedEditorBackground\":\"#011627\",\"gitDecoration.modifiedResourceForeground\":\"#a2bffc\",\"gitDecoration.deletedResourceForeground\":\"#ef535090\",\"gitDecoration.untrackedResourceForeground\":\"#c5e478ff\",\"gitDecoration.ignoredResourceForeground\":\"#395a75\",\"gitDecoration.conflictingResourceForeground\":\"#ffeb95cc\",\"source.elm\":\"#5f7e97\",\"string.quoted.single.js\":\"#ffffff\",\"meta.objectliteral.js\":\"#82aaff\"},\"fg\":\"#d6deeb\",\"bg\":\"#23262f\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#a2bffc\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#f27775fe\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#d6deeb\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#919f9f\",\"fontStyle\":\"\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#5ca7e4\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#caece6\",\"fontStyle\":\"\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"fontStyle\":\"\",\"foreground\":\"#c5e478\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"background\":\"#ff2c83\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#d3423e\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#919f9f\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#cdebf7\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#ffffff\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#80cbc4\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#57eaf1\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#fad430\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#faf39f\",\"fontStyle\":\"\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#82aaff\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Comparison, returns, imports, and Keyword Operator Ruby\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#989da0\",\"background\":\"#F78C6C\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"background\":\"#8BD649\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#ec5f67\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#ef787f\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#31e1eb\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Unquoted String Zone\",\"scope\":[\"string.unquoted.preprocessor.message.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Region\",\"scope\":[\"punctuation.separator.hash.cs\",\"keyword.preprocessor.region.cs\",\"keyword.preprocessor.endregion.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"bold\"}},{\"name\":\"C# Other Variables\",\"scope\":[\"variable.other.object.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Enum\",\"scope\":[\"entity.name.type.enum.cs\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Dart String\",\"scope\":[\"string.interpolated.single.dart\",\"string.interpolated.double.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Dart Class\",\"scope\":[\"support.class.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#ff6d6d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#dddddd\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#e0dec6\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#6ae9f0\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#78ccf0\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#8b96ea\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#c789d6\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#ff869a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#d6deeb\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#8eace3\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#d7dbe0\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-6)\",\"terminalBackground\":\"var(--sl-color-gray-6)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-6)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#ffffff17\",\"markBorderColor\":\"#ffffff40\"}}},{\"name\":\"Night Owl Light\",\"type\":\"light\",\"colors\":{\"focusBorder\":\"#93a1a1\",\"foreground\":\"#403f53\",\"disabledForeground\":\"#61616180\",\"descriptionForeground\":\"#403f53\",\"errorForeground\":\"#403f53\",\"icon.foreground\":\"#424242\",\"contrastActiveBorder\":null,\"contrastBorder\":null,\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#dcdcdc66\",\"textLink.activeForeground\":\"#006ab1\",\"textLink.foreground\":\"#006ab1\",\"textPreformat.foreground\":\"#a31515\",\"textSeparator.foreground\":\"#0000002e\",\"editor.background\":\"#f6f7f9\",\"editor.foreground\":\"#403f53\",\"editorLineNumber.foreground\":\"#90a7b2\",\"editorLineNumber.activeForeground\":\"#403f53\",\"editorActiveLineNumber.foreground\":\"#0b216f\",\"editor.selectionBackground\":\"#e0e0e0\",\"editor.inactiveSelectionBackground\":\"#e0e0e080\",\"editor.selectionHighlightBackground\":\"#339cec33\",\"editorError.foreground\":\"#e64d49\",\"editorWarning.foreground\":\"#daaa01\",\"editorInfo.foreground\":\"#1a85ff\",\"editorHint.foreground\":\"#6c6c6c\",\"problemsErrorIcon.foreground\":\"#e64d49\",\"problemsWarningIcon.foreground\":\"#daaa01\",\"problemsInfoIcon.foreground\":\"#1a85ff\",\"editor.findMatchBackground\":\"#93a1a16c\",\"editor.findMatchHighlightBackground\":\"#93a1a16c\",\"editor.findRangeHighlightBackground\":\"#7497a633\",\"editorLink.activeForeground\":\"#0000ff\",\"editorLightBulb.foreground\":\"#ddb100\",\"editorLightBulbAutoFix.foreground\":\"#007acc\",\"diffEditor.insertedTextBackground\":\"#9ccc2c40\",\"diffEditor.insertedTextBorder\":null,\"diffEditor.removedTextBackground\":\"#ff000033\",\"diffEditor.removedTextBorder\":null,\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#fbfbfb\",\"editorStickyScrollHover.background\":\"#f0f0f0\",\"editorInlayHint.background\":\"#2aa29899\",\"editorInlayHint.foreground\":\"#f0f0f0\",\"editorInlayHint.typeBackground\":\"#2aa29899\",\"editorInlayHint.typeForeground\":\"#f0f0f0\",\"editorInlayHint.parameterBackground\":\"#2aa29899\",\"editorInlayHint.parameterForeground\":\"#f0f0f0\",\"editorPane.background\":\"#fbfbfb\",\"editorGroup.emptyBackground\":null,\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-gray-6)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#f0f0f0\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#f0f0f0\",\"editorGroup.dropBackground\":\"#2677cb2d\",\"editorGroup.dropIntoPromptForeground\":\"#403f53\",\"editorGroup.dropIntoPromptBackground\":\"#f0f0f0\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#f0f0f0\",\"sideBySideEditor.verticalBorder\":\"#f0f0f0\",\"scrollbar.shadow\":\"#cccccc\",\"scrollbarSlider.background\":\"#0000001a\",\"scrollbarSlider.hoverBackground\":\"#00000055\",\"scrollbarSlider.activeBackground\":\"#00000099\",\"panel.background\":\"#f0f0f0\",\"panel.border\":\"#d9d9d9\",\"panelTitle.activeBorder\":\"#424242\",\"panelTitle.activeForeground\":\"#424242\",\"panelTitle.inactiveForeground\":\"#424242bf\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#f6f6f6\",\"widget.shadow\":\"#d9d9d9\",\"editorWidget.background\":\"#f0f0f0\",\"editorWidget.foreground\":\"#403f53\",\"editorWidget.border\":\"#d9d9d9\",\"quickInput.background\":\"#f0f0f0\",\"quickInput.foreground\":\"#403f53\",\"quickInputTitle.background\":\"#0000000f\",\"pickerGroup.foreground\":\"#403f53\",\"pickerGroup.border\":\"#d9d9d9\",\"editor.hoverHighlightBackground\":\"#339cec33\",\"editorHoverWidget.background\":\"#f0f0f0\",\"editorHoverWidget.foreground\":\"#403f53\",\"editorHoverWidget.border\":\"#d9d9d9\",\"editorHoverWidget.statusBarBackground\":\"#e4e4e4\",\"titleBar.activeBackground\":\"var(--sl-color-gray-6)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#f0f0f099\",\"titleBar.inactiveForeground\":\"#33333399\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#b8b8b850\",\"toolbar.activeBackground\":\"#a6a6a650\",\"tab.activeBackground\":\"#f6f6f6\",\"tab.unfocusedActiveBackground\":\"#f6f6f6\",\"tab.inactiveBackground\":\"#f0f0f0\",\"tab.unfocusedInactiveBackground\":\"#f0f0f0\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#403f53\",\"tab.unfocusedActiveForeground\":\"#403f53b3\",\"tab.unfocusedInactiveForeground\":\"#403f5380\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#f0f0f0\",\"tab.lastPinnedBorder\":\"#a9a9a9\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":null,\"tab.activeBorderTop\":\"var(--sl-color-accent)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#2aa298\",\"tab.inactiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedActiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedInactiveModifiedBorder\":\"#93a1a1\",\"badge.background\":\"#2aa298\",\"badge.foreground\":\"#f0f0f0\",\"button.background\":\"#2aa298\",\"button.foreground\":\"#f0f0f0\",\"button.border\":null,\"button.separator\":\"#f0f0f066\",\"button.hoverBackground\":\"#22827a\",\"button.secondaryBackground\":\"#5f6a79\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#4c5561\",\"dropdown.background\":\"#f0f0f0\",\"dropdown.foreground\":\"#403f53\",\"dropdown.border\":\"#d9d9d9\",\"list.activeSelectionBackground\":\"#d3e8f8\",\"list.activeSelectionForeground\":\"#403f53\",\"tree.indentGuidesStroke\":\"#a9a9a9\",\"input.background\":\"#f0f0f0\",\"input.foreground\":\"#403f53\",\"input.placeholderForeground\":\"#93a1a1\",\"inputOption.activeBorder\":\"#2aa298\",\"inputOption.hoverBackground\":\"#b8b8b850\",\"inputOption.activeBackground\":\"#93a1a133\",\"inputOption.activeForeground\":\"#000000\",\"inputValidation.infoBackground\":\"#f0f0f0\",\"inputValidation.infoBorder\":\"#d0d0d0\",\"inputValidation.warningBackground\":\"#daaa01\",\"inputValidation.warningBorder\":\"#e0af02\",\"inputValidation.errorBackground\":\"#f76e6e\",\"inputValidation.errorBorder\":\"#de3d3b\",\"keybindingLabel.background\":\"#dddddd66\",\"keybindingLabel.foreground\":\"#555555\",\"keybindingLabel.border\":\"#cccccc66\",\"keybindingLabel.bottomBorder\":\"#bbbbbb66\",\"menu.foreground\":\"#403f53\",\"menu.background\":\"#f0f0f0\",\"menu.selectionForeground\":\"#403f53\",\"menu.selectionBackground\":\"#d3e8f8\",\"menu.separatorBackground\":\"#d4d4d4\",\"editor.snippetTabstopHighlightBackground\":\"#0a326433\",\"editor.snippetFinalTabstopHighlightBorder\":\"#0a326480\",\"terminal.ansiBlack\":\"#403f53\",\"terminal.ansiRed\":\"#de3d3b\",\"terminal.ansiGreen\":\"#08916a\",\"terminal.ansiYellow\":\"#e0af02\",\"terminal.ansiBlue\":\"#288ed7\",\"terminal.ansiMagenta\":\"#d6438a\",\"terminal.ansiCyan\":\"#2aa298\",\"terminal.ansiWhite\":\"#f0f0f0\",\"terminal.ansiBrightBlack\":\"#403f53\",\"terminal.ansiBrightRed\":\"#de3d3b\",\"terminal.ansiBrightGreen\":\"#08916a\",\"terminal.ansiBrightYellow\":\"#daaa01\",\"terminal.ansiBrightBlue\":\"#288ed7\",\"terminal.ansiBrightMagenta\":\"#d6438a\",\"terminal.ansiBrightCyan\":\"#2aa298\",\"terminal.ansiBrightWhite\":\"#f0f0f0\",\"selection.background\":\"#7a8181ad\",\"notifications.background\":\"#f0f0f0\",\"notifications.foreground\":\"#403f53\",\"notificationLink.foreground\":\"#994cc3\",\"notifications.border\":\"#cccccc\",\"notificationCenter.border\":\"#cccccc\",\"notificationToast.border\":\"#cccccc\",\"notificationCenterHeader.foreground\":\"#403f53\",\"notificationCenterHeader.background\":\"#f0f0f0\",\"input.border\":\"#d9d9d9\",\"progressBar.background\":\"#2aa298\",\"list.inactiveSelectionBackground\":\"#e0e7ea\",\"list.inactiveSelectionForeground\":\"#403f53\",\"list.focusBackground\":\"#d3e8f8\",\"list.hoverBackground\":\"#d3e8f8\",\"list.focusForeground\":\"#403f53\",\"list.hoverForeground\":\"#403f53\",\"list.highlightForeground\":\"#403f53\",\"list.errorForeground\":\"#e64d49\",\"list.warningForeground\":\"#daaa01\",\"activityBar.background\":\"#f0f0f0\",\"activityBar.foreground\":\"#403f53\",\"activityBar.dropBackground\":\"#d0d0d0\",\"activityBarBadge.background\":\"#403f53\",\"activityBarBadge.foreground\":\"#f0f0f0\",\"activityBar.border\":\"#f0f0f0\",\"sideBar.background\":\"#f0f0f0\",\"sideBar.foreground\":\"#403f53\",\"sideBarTitle.foreground\":\"#403f53\",\"sideBar.border\":\"#f0f0f0\",\"editorGroup.background\":\"#f6f6f6\",\"editorCursor.foreground\":\"#90a7b2\",\"editor.wordHighlightBackground\":\"#339cec33\",\"editor.wordHighlightStrongBackground\":\"#007dd659\",\"editor.lineHighlightBackground\":\"#f0f0f0\",\"editor.rangeHighlightBackground\":\"#7497a633\",\"editorWhitespace.foreground\":\"#d9d9d9\",\"editorIndentGuide.background\":\"#d9d9d9\",\"editorCodeLens.foreground\":\"#403f53\",\"editorBracketMatch.background\":\"#d3e8f8\",\"editorBracketMatch.border\":\"#2aa298\",\"editorError.border\":\"#fbfbfb\",\"editorWarning.border\":\"#daaa01\",\"editorGutter.addedBackground\":\"#49d0c5\",\"editorGutter.modifiedBackground\":\"#6fbef6\",\"editorGutter.deletedBackground\":\"#f76e6e\",\"editorRuler.foreground\":\"#d9d9d9\",\"editorOverviewRuler.errorForeground\":\"#e64d49\",\"editorOverviewRuler.warningForeground\":\"#daaa01\",\"editorSuggestWidget.background\":\"#f0f0f0\",\"editorSuggestWidget.foreground\":\"#403f53\",\"editorSuggestWidget.highlightForeground\":\"#403f53\",\"editorSuggestWidget.selectedBackground\":\"#d3e8f8\",\"editorSuggestWidget.border\":\"#d9d9d9\",\"debugExceptionWidget.background\":\"#f0f0f0\",\"debugExceptionWidget.border\":\"#d9d9d9\",\"editorMarkerNavigation.background\":\"#d0d0d0\",\"editorMarkerNavigationError.background\":\"#f76e6e\",\"editorMarkerNavigationWarning.background\":\"#daaa01\",\"debugToolBar.background\":\"#f0f0f0\",\"extensionButton.prominentBackground\":\"#2aa298\",\"extensionButton.prominentForeground\":\"#f0f0f0\",\"statusBar.background\":\"#f0f0f0\",\"statusBar.border\":\"#f0f0f0\",\"statusBar.debuggingBackground\":\"#f0f0f0\",\"statusBar.debuggingForeground\":\"#403f53\",\"statusBar.foreground\":\"#403f53\",\"statusBar.noFolderBackground\":\"#f0f0f0\",\"statusBar.noFolderForeground\":\"#403f53\",\"peekView.border\":\"#d9d9d9\",\"peekViewEditor.background\":\"#f6f6f6\",\"peekViewEditorGutter.background\":\"#f6f6f6\",\"peekViewEditor.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.background\":\"#f0f0f0\",\"peekViewResult.fileForeground\":\"#403f53\",\"peekViewResult.lineForeground\":\"#403f53\",\"peekViewResult.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.selectionBackground\":\"#e0e7ea\",\"peekViewResult.selectionForeground\":\"#403f53\",\"peekViewTitle.background\":\"#f0f0f0\",\"peekViewTitleLabel.foreground\":\"#403f53\",\"peekViewTitleDescription.foreground\":\"#403f53\",\"terminal.foreground\":\"#403f53\"},\"fg\":\"#403f53\",\"bg\":\"#f6f7f9\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#556484\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#ae3c3afd\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#403f53\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#5f636f\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#3a688f\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"foreground\":\"#bb2060\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#5d6376\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#58656a\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#646464\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#286d70\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword Operator Comparison, imports, returns and Keyword Operator Ruby\",\"scope\":[\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.module.ruby\",\"keyword.control.class.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Control Conditional\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"foreground\":\"#486e26\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#a64348\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#186e73\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#984e4d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#3e697c\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#555ea2\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#111111\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#7c5686\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Symbols\",\"scope\":[\"constant.language.symbol.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#954f5a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#403f53\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\",\"variable.other.property.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#76578b\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-7)\",\"terminalBackground\":\"var(--sl-color-gray-7)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-7)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#0000001a\",\"markBorderColor\":\"#00000055\"}}}],\"defaultLocale\":\"en\",\"cascadeLayer\":\"starlight.components\",\"styleOverrides\":{\"borderRadius\":\"0px\",\"borderWidth\":\"1px\",\"codePaddingBlock\":\"0.75rem\",\"codePaddingInline\":\"1rem\",\"codeFontFamily\":\"var(--__sl-font-mono)\",\"codeFontSize\":\"var(--sl-text-code)\",\"codeLineHeight\":\"var(--sl-line-height)\",\"uiFontFamily\":\"var(--__sl-font)\",\"textMarkers\":{\"lineDiffIndicatorMarginLeft\":\"0.25rem\",\"defaultChroma\":\"45\",\"backgroundOpacity\":\"60%\"}},\"plugins\":[{\"name\":\"Starlight Plugin\",\"hooks\":{}},{\"name\":\"astro-expressive-code\",\"hooks\":{}}]}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[{\"codes\":[\"en\"],\"path\":\"en\"},{\"codes\":[\"ru\"],\"path\":\"ru\"}],\"routing\":{\"prefixDefaultLocale\":true,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","docs",["Map",11,12,25,26],"en",{"id":11,"data":13,"body":22,"filePath":23,"digest":24,"deferredRender":16},{"title":14,"description":15,"editUrl":16,"head":17,"template":18,"sidebar":19,"pagefind":16,"draft":20},"Welcome","Get started building your docs site with Starlight.",true,[],"splash",{"hidden":20,"attrs":21},false,{},"import { Card, CardGrid } from '@astrojs/starlight/components';\n\n## Next steps\n\n\u003CCardGrid stagger>\n\t\u003CCard title=\"Update content\" icon=\"pencil\">\n\t\tEdit `src/content/docs/index.mdx` to see this page change.\n\t\u003C/Card>\n\t\u003CCard title=\"Change page layout\" icon=\"document\">\n\t\tDelete `template: splash` in `src/content/docs/index.mdx` to display a\n\t\tsidebar on this page.\n\t\u003C/Card>\n\t\u003CCard title=\"Add new content\" icon=\"add-document\">\n\t\tAdd Markdown or MDX files to `src/content/docs` to create new pages.\n\t\u003C/Card>\n\t\u003CCard title=\"Configure your site\" icon=\"setting\">\n\t\tEdit your `sidebar` and other config in `astro.config.mjs`.\n\t\u003C/Card>\n\t\u003CCard title=\"Read the docs\" icon=\"open-book\">\n\t\tLearn more in [the Starlight Docs](https://starlight.astro.build/).\n\t\u003C/Card>\n\u003C/CardGrid>","src/content/docs/en/index.mdx","576d77b3cc95d789","en/guides/example",{"id":25,"data":27,"body":34,"filePath":35,"digest":36,"rendered":37},{"title":28,"description":29,"editUrl":16,"head":30,"template":31,"sidebar":32,"pagefind":16,"draft":20},"Example Guide","A guide in my new Starlight docs site.",[],"doc",{"hidden":20,"attrs":33},{},"# Hello","src/content/docs/en/guides/example.md","46be7f85f8f2de8e",{"html":38,"metadata":39},"\u003Cdiv class=\"sl-heading-wrapper level-h1\">\u003Ch1 id=\"hello\">Hello\u003C/h1>\u003Ca class=\"sl-anchor-link\" href=\"#hello\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\">\u003Cpath fill=\"currentcolor\" d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\">Section titled “Hello”\u003C/span>\u003C/a>\u003C/div>",{"headings":40,"localImagePaths":45,"remoteImagePaths":46,"frontmatter":47,"imagePaths":48},[41],{"depth":42,"slug":43,"text":44},1,"hello","Hello",[],[],{"title":28,"description":29},[]] \ No newline at end of file +[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.16.16","content-config-digest","28e7bc87492477d9","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[null,null,null,null],\"rehypePlugins\":[[null,{\"experimentalHeadingIdCompat\":false}],null,[null,{\"themes\":[{\"name\":\"Night Owl No Italics\",\"type\":\"dark\",\"colors\":{\"focusBorder\":\"#122d42\",\"foreground\":\"#d6deeb\",\"disabledForeground\":\"#cccccc80\",\"descriptionForeground\":\"#d6deebb3\",\"errorForeground\":\"#ef5350\",\"icon.foreground\":\"#c5c5c5\",\"contrastActiveBorder\":null,\"contrastBorder\":\"#122d42\",\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#4f4f4f\",\"textLink.activeForeground\":\"#3794ff\",\"textLink.foreground\":\"#3794ff\",\"textPreformat.foreground\":\"#d7ba7d\",\"textSeparator.foreground\":\"#ffffff2e\",\"editor.background\":\"#23262f\",\"editor.foreground\":\"#d6deeb\",\"editorLineNumber.foreground\":\"#4b6479\",\"editorLineNumber.activeForeground\":\"#c5e4fd\",\"editorActiveLineNumber.foreground\":\"#c6c6c6\",\"editor.selectionBackground\":\"#1d3b53\",\"editor.inactiveSelectionBackground\":\"#7e57c25a\",\"editor.selectionHighlightBackground\":\"#5f7e9779\",\"editorError.foreground\":\"#ef5350\",\"editorWarning.foreground\":\"#b39554\",\"editorInfo.foreground\":\"#3794ff\",\"editorHint.foreground\":\"#eeeeeeb2\",\"problemsErrorIcon.foreground\":\"#ef5350\",\"problemsWarningIcon.foreground\":\"#b39554\",\"problemsInfoIcon.foreground\":\"#3794ff\",\"editor.findMatchBackground\":\"#5f7e9779\",\"editor.findMatchHighlightBackground\":\"#1085bb5d\",\"editor.findRangeHighlightBackground\":\"#3a3d4166\",\"editorLink.activeForeground\":\"#4e94ce\",\"editorLightBulb.foreground\":\"#ffcc00\",\"editorLightBulbAutoFix.foreground\":\"#75beff\",\"diffEditor.insertedTextBackground\":\"#99b76d23\",\"diffEditor.insertedTextBorder\":\"#c5e47833\",\"diffEditor.removedTextBackground\":\"#ef535033\",\"diffEditor.removedTextBorder\":\"#ef53504d\",\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#011627\",\"editorStickyScrollHover.background\":\"#2a2d2e\",\"editorInlayHint.background\":\"#5f7e97cc\",\"editorInlayHint.foreground\":\"#ffffff\",\"editorInlayHint.typeBackground\":\"#5f7e97cc\",\"editorInlayHint.typeForeground\":\"#ffffff\",\"editorInlayHint.parameterBackground\":\"#5f7e97cc\",\"editorInlayHint.parameterForeground\":\"#ffffff\",\"editorPane.background\":\"#011627\",\"editorGroup.emptyBackground\":\"#011627\",\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-black)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#011627\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#011627\",\"editorGroup.dropBackground\":\"#7e57c273\",\"editorGroup.dropIntoPromptForeground\":\"#d6deeb\",\"editorGroup.dropIntoPromptBackground\":\"#021320\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#011627\",\"sideBySideEditor.verticalBorder\":\"#011627\",\"scrollbar.shadow\":\"#010b14\",\"scrollbarSlider.background\":\"#ffffff17\",\"scrollbarSlider.hoverBackground\":\"#ffffff40\",\"scrollbarSlider.activeBackground\":\"#084d8180\",\"panel.background\":\"#011627\",\"panel.border\":\"#5f7e97\",\"panelTitle.activeBorder\":\"#5f7e97\",\"panelTitle.activeForeground\":\"#ffffffcc\",\"panelTitle.inactiveForeground\":\"#d6deeb80\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#011627\",\"widget.shadow\":\"#011627\",\"editorWidget.background\":\"#021320\",\"editorWidget.foreground\":\"#d6deeb\",\"editorWidget.border\":\"#5f7e97\",\"quickInput.background\":\"#021320\",\"quickInput.foreground\":\"#d6deeb\",\"quickInputTitle.background\":\"#ffffff1a\",\"pickerGroup.foreground\":\"#d1aaff\",\"pickerGroup.border\":\"#011627\",\"editor.hoverHighlightBackground\":\"#7e57c25a\",\"editorHoverWidget.background\":\"#011627\",\"editorHoverWidget.foreground\":\"#d6deeb\",\"editorHoverWidget.border\":\"#5f7e97\",\"editorHoverWidget.statusBarBackground\":\"#011a2f\",\"titleBar.activeBackground\":\"var(--sl-color-black)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#010e1a\",\"titleBar.inactiveForeground\":\"#eeefff99\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#5a5d5e50\",\"toolbar.activeBackground\":\"#63666750\",\"tab.activeBackground\":\"#0b2942\",\"tab.unfocusedActiveBackground\":\"#0b2942\",\"tab.inactiveBackground\":\"#01111d\",\"tab.unfocusedInactiveBackground\":\"#01111d\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#5f7e97\",\"tab.unfocusedActiveForeground\":\"#5f7e97\",\"tab.unfocusedInactiveForeground\":\"#5f7e97\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#272b3b\",\"tab.lastPinnedBorder\":\"#585858\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":\"#262a39\",\"tab.activeBorderTop\":\"var(--sl-color-accent-high)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#3399cc\",\"tab.inactiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedActiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedInactiveModifiedBorder\":\"#3399cc40\",\"badge.background\":\"#5f7e97\",\"badge.foreground\":\"#ffffff\",\"button.background\":\"#7e57c2cc\",\"button.foreground\":\"#ffffffcc\",\"button.border\":\"#122d42\",\"button.separator\":\"#ffffff52\",\"button.hoverBackground\":\"#7e57c2\",\"button.secondaryBackground\":\"#3a3d41\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#46494e\",\"dropdown.background\":\"#011627\",\"dropdown.foreground\":\"#ffffffcc\",\"dropdown.border\":\"#5f7e97\",\"list.activeSelectionBackground\":\"#234d708c\",\"list.activeSelectionForeground\":\"#ffffff\",\"tree.indentGuidesStroke\":\"#585858\",\"input.background\":\"#0b253a\",\"input.foreground\":\"#ffffffcc\",\"input.placeholderForeground\":\"#5f7e97\",\"inputOption.activeBorder\":\"#ffffffcc\",\"inputOption.hoverBackground\":\"#5a5d5e80\",\"inputOption.activeBackground\":\"#122d4266\",\"inputOption.activeForeground\":\"#ffffff\",\"inputValidation.infoBackground\":\"#00589ef2\",\"inputValidation.infoBorder\":\"#64b5f6\",\"inputValidation.warningBackground\":\"#675700f2\",\"inputValidation.warningBorder\":\"#ffca28\",\"inputValidation.errorBackground\":\"#ab0300f2\",\"inputValidation.errorBorder\":\"#ef5350\",\"keybindingLabel.background\":\"#8080802b\",\"keybindingLabel.foreground\":\"#cccccc\",\"keybindingLabel.border\":\"#33333399\",\"keybindingLabel.bottomBorder\":\"#44444499\",\"menu.foreground\":\"#ffffffcc\",\"menu.background\":\"#011627\",\"menu.selectionForeground\":\"#ffffff\",\"menu.selectionBackground\":\"#234d708c\",\"menu.separatorBackground\":\"#606060\",\"editor.snippetTabstopHighlightBackground\":\"#7c7c74c\",\"editor.snippetFinalTabstopHighlightBorder\":\"#525252\",\"terminal.ansiBlack\":\"#011627\",\"terminal.ansiRed\":\"#ef5350\",\"terminal.ansiGreen\":\"#22da6e\",\"terminal.ansiYellow\":\"#c5e478\",\"terminal.ansiBlue\":\"#82aaff\",\"terminal.ansiMagenta\":\"#c792ea\",\"terminal.ansiCyan\":\"#21c7a8\",\"terminal.ansiWhite\":\"#ffffff\",\"terminal.ansiBrightBlack\":\"#575656\",\"terminal.ansiBrightRed\":\"#ef5350\",\"terminal.ansiBrightGreen\":\"#22da6e\",\"terminal.ansiBrightYellow\":\"#ffeb95\",\"terminal.ansiBrightBlue\":\"#82aaff\",\"terminal.ansiBrightMagenta\":\"#c792ea\",\"terminal.ansiBrightCyan\":\"#7fdbca\",\"terminal.ansiBrightWhite\":\"#ffffff\",\"selection.background\":\"#4373c2\",\"input.border\":\"#5f7e97\",\"punctuation.definition.generic.begin.html\":\"#ef5350f2\",\"progress.background\":\"#7e57c2\",\"breadcrumb.foreground\":\"#a599e9\",\"breadcrumb.focusForeground\":\"#ffffff\",\"breadcrumb.activeSelectionForeground\":\"#ffffff\",\"breadcrumbPicker.background\":\"#001122\",\"list.invalidItemForeground\":\"#975f94\",\"list.dropBackground\":\"#011627\",\"list.focusBackground\":\"#010d18\",\"list.focusForeground\":\"#ffffff\",\"list.highlightForeground\":\"#ffffff\",\"list.hoverBackground\":\"#011627\",\"list.hoverForeground\":\"#ffffff\",\"list.inactiveSelectionBackground\":\"#0e293f\",\"list.inactiveSelectionForeground\":\"#5f7e97\",\"activityBar.background\":\"#011627\",\"activityBar.dropBackground\":\"#5f7e97\",\"activityBar.foreground\":\"#5f7e97\",\"activityBar.border\":\"#011627\",\"activityBarBadge.background\":\"#44596b\",\"activityBarBadge.foreground\":\"#ffffff\",\"sideBar.background\":\"#011627\",\"sideBar.foreground\":\"#89a4bb\",\"sideBar.border\":\"#011627\",\"sideBarTitle.foreground\":\"#5f7e97\",\"sideBarSectionHeader.background\":\"#011627\",\"sideBarSectionHeader.foreground\":\"#5f7e97\",\"editorCursor.foreground\":\"#80a4c2\",\"editor.wordHighlightBackground\":\"#f6bbe533\",\"editor.wordHighlightStrongBackground\":\"#e2a2f433\",\"editor.lineHighlightBackground\":\"#0003\",\"editor.rangeHighlightBackground\":\"#7e57c25a\",\"editorIndentGuide.background\":\"#5e81ce52\",\"editorIndentGuide.activeBackground\":\"#7e97ac\",\"editorRuler.foreground\":\"#5e81ce52\",\"editorCodeLens.foreground\":\"#5e82ceb4\",\"editorBracketMatch.background\":\"#5f7e974d\",\"editorOverviewRuler.currentContentForeground\":\"#7e57c2\",\"editorOverviewRuler.incomingContentForeground\":\"#7e57c2\",\"editorOverviewRuler.commonContentForeground\":\"#7e57c2\",\"editorGutter.background\":\"#011627\",\"editorGutter.modifiedBackground\":\"#e2b93d\",\"editorGutter.addedBackground\":\"#9ccc65\",\"editorGutter.deletedBackground\":\"#ef5350\",\"editorSuggestWidget.background\":\"#2c3043\",\"editorSuggestWidget.border\":\"#2b2f40\",\"editorSuggestWidget.foreground\":\"#d6deeb\",\"editorSuggestWidget.highlightForeground\":\"#ffffff\",\"editorSuggestWidget.selectedBackground\":\"#5f7e97\",\"debugExceptionWidget.background\":\"#011627\",\"debugExceptionWidget.border\":\"#5f7e97\",\"editorMarkerNavigation.background\":\"#0b2942\",\"editorMarkerNavigationError.background\":\"#ef5350\",\"editorMarkerNavigationWarning.background\":\"#ffca28\",\"peekView.border\":\"#5f7e97\",\"peekViewEditor.background\":\"#011627\",\"peekViewEditor.matchHighlightBackground\":\"#7e57c25a\",\"peekViewResult.background\":\"#011627\",\"peekViewResult.fileForeground\":\"#5f7e97\",\"peekViewResult.lineForeground\":\"#5f7e97\",\"peekViewResult.matchHighlightBackground\":\"#ffffffcc\",\"peekViewResult.selectionBackground\":\"#2e3250\",\"peekViewResult.selectionForeground\":\"#5f7e97\",\"peekViewTitle.background\":\"#011627\",\"peekViewTitleDescription.foreground\":\"#697098\",\"peekViewTitleLabel.foreground\":\"#5f7e97\",\"merge.currentHeaderBackground\":\"#5f7e97\",\"merge.incomingHeaderBackground\":\"#7e57c25a\",\"statusBar.background\":\"#011627\",\"statusBar.foreground\":\"#5f7e97\",\"statusBar.border\":\"#262a39\",\"statusBar.debuggingBackground\":\"#202431\",\"statusBar.debuggingBorder\":\"#1f2330\",\"statusBar.noFolderBackground\":\"#011627\",\"statusBar.noFolderBorder\":\"#25293a\",\"statusBarItem.activeBackground\":\"#202431\",\"statusBarItem.hoverBackground\":\"#202431\",\"statusBarItem.prominentBackground\":\"#202431\",\"statusBarItem.prominentHoverBackground\":\"#202431\",\"notifications.background\":\"#01111d\",\"notifications.border\":\"#262a39\",\"notificationCenter.border\":\"#262a39\",\"notificationToast.border\":\"#262a39\",\"notifications.foreground\":\"#ffffffcc\",\"notificationLink.foreground\":\"#80cbc4\",\"extensionButton.prominentForeground\":\"#ffffffcc\",\"extensionButton.prominentBackground\":\"#7e57c2cc\",\"extensionButton.prominentHoverBackground\":\"#7e57c2\",\"terminal.selectionBackground\":\"#1b90dd4d\",\"terminalCursor.background\":\"#234d70\",\"debugToolBar.background\":\"#011627\",\"welcomePage.buttonBackground\":\"#011627\",\"welcomePage.buttonHoverBackground\":\"#011627\",\"walkThrough.embeddedEditorBackground\":\"#011627\",\"gitDecoration.modifiedResourceForeground\":\"#a2bffc\",\"gitDecoration.deletedResourceForeground\":\"#ef535090\",\"gitDecoration.untrackedResourceForeground\":\"#c5e478ff\",\"gitDecoration.ignoredResourceForeground\":\"#395a75\",\"gitDecoration.conflictingResourceForeground\":\"#ffeb95cc\",\"source.elm\":\"#5f7e97\",\"string.quoted.single.js\":\"#ffffff\",\"meta.objectliteral.js\":\"#82aaff\"},\"fg\":\"#d6deeb\",\"bg\":\"#23262f\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#a2bffc\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#f27775fe\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#d6deeb\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#919f9f\",\"fontStyle\":\"\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#5ca7e4\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#caece6\",\"fontStyle\":\"\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"fontStyle\":\"\",\"foreground\":\"#c5e478\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"background\":\"#ff2c83\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#d3423e\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#919f9f\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#cdebf7\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#ffffff\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#80cbc4\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#57eaf1\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#fad430\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#faf39f\",\"fontStyle\":\"\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#82aaff\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Comparison, returns, imports, and Keyword Operator Ruby\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#989da0\",\"background\":\"#F78C6C\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"background\":\"#8BD649\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#ec5f67\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#ef787f\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#31e1eb\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Unquoted String Zone\",\"scope\":[\"string.unquoted.preprocessor.message.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Region\",\"scope\":[\"punctuation.separator.hash.cs\",\"keyword.preprocessor.region.cs\",\"keyword.preprocessor.endregion.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"bold\"}},{\"name\":\"C# Other Variables\",\"scope\":[\"variable.other.object.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Enum\",\"scope\":[\"entity.name.type.enum.cs\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Dart String\",\"scope\":[\"string.interpolated.single.dart\",\"string.interpolated.double.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Dart Class\",\"scope\":[\"support.class.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#ff6d6d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#dddddd\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#e0dec6\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#6ae9f0\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#78ccf0\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#8b96ea\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#c789d6\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#ff869a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#d6deeb\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#8eace3\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#d7dbe0\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-6)\",\"terminalBackground\":\"var(--sl-color-gray-6)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-6)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#ffffff17\",\"markBorderColor\":\"#ffffff40\"}}},{\"name\":\"Night Owl Light\",\"type\":\"light\",\"colors\":{\"focusBorder\":\"#93a1a1\",\"foreground\":\"#403f53\",\"disabledForeground\":\"#61616180\",\"descriptionForeground\":\"#403f53\",\"errorForeground\":\"#403f53\",\"icon.foreground\":\"#424242\",\"contrastActiveBorder\":null,\"contrastBorder\":null,\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#dcdcdc66\",\"textLink.activeForeground\":\"#006ab1\",\"textLink.foreground\":\"#006ab1\",\"textPreformat.foreground\":\"#a31515\",\"textSeparator.foreground\":\"#0000002e\",\"editor.background\":\"#f6f7f9\",\"editor.foreground\":\"#403f53\",\"editorLineNumber.foreground\":\"#90a7b2\",\"editorLineNumber.activeForeground\":\"#403f53\",\"editorActiveLineNumber.foreground\":\"#0b216f\",\"editor.selectionBackground\":\"#e0e0e0\",\"editor.inactiveSelectionBackground\":\"#e0e0e080\",\"editor.selectionHighlightBackground\":\"#339cec33\",\"editorError.foreground\":\"#e64d49\",\"editorWarning.foreground\":\"#daaa01\",\"editorInfo.foreground\":\"#1a85ff\",\"editorHint.foreground\":\"#6c6c6c\",\"problemsErrorIcon.foreground\":\"#e64d49\",\"problemsWarningIcon.foreground\":\"#daaa01\",\"problemsInfoIcon.foreground\":\"#1a85ff\",\"editor.findMatchBackground\":\"#93a1a16c\",\"editor.findMatchHighlightBackground\":\"#93a1a16c\",\"editor.findRangeHighlightBackground\":\"#7497a633\",\"editorLink.activeForeground\":\"#0000ff\",\"editorLightBulb.foreground\":\"#ddb100\",\"editorLightBulbAutoFix.foreground\":\"#007acc\",\"diffEditor.insertedTextBackground\":\"#9ccc2c40\",\"diffEditor.insertedTextBorder\":null,\"diffEditor.removedTextBackground\":\"#ff000033\",\"diffEditor.removedTextBorder\":null,\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#fbfbfb\",\"editorStickyScrollHover.background\":\"#f0f0f0\",\"editorInlayHint.background\":\"#2aa29899\",\"editorInlayHint.foreground\":\"#f0f0f0\",\"editorInlayHint.typeBackground\":\"#2aa29899\",\"editorInlayHint.typeForeground\":\"#f0f0f0\",\"editorInlayHint.parameterBackground\":\"#2aa29899\",\"editorInlayHint.parameterForeground\":\"#f0f0f0\",\"editorPane.background\":\"#fbfbfb\",\"editorGroup.emptyBackground\":null,\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-gray-6)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#f0f0f0\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#f0f0f0\",\"editorGroup.dropBackground\":\"#2677cb2d\",\"editorGroup.dropIntoPromptForeground\":\"#403f53\",\"editorGroup.dropIntoPromptBackground\":\"#f0f0f0\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#f0f0f0\",\"sideBySideEditor.verticalBorder\":\"#f0f0f0\",\"scrollbar.shadow\":\"#cccccc\",\"scrollbarSlider.background\":\"#0000001a\",\"scrollbarSlider.hoverBackground\":\"#00000055\",\"scrollbarSlider.activeBackground\":\"#00000099\",\"panel.background\":\"#f0f0f0\",\"panel.border\":\"#d9d9d9\",\"panelTitle.activeBorder\":\"#424242\",\"panelTitle.activeForeground\":\"#424242\",\"panelTitle.inactiveForeground\":\"#424242bf\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#f6f6f6\",\"widget.shadow\":\"#d9d9d9\",\"editorWidget.background\":\"#f0f0f0\",\"editorWidget.foreground\":\"#403f53\",\"editorWidget.border\":\"#d9d9d9\",\"quickInput.background\":\"#f0f0f0\",\"quickInput.foreground\":\"#403f53\",\"quickInputTitle.background\":\"#0000000f\",\"pickerGroup.foreground\":\"#403f53\",\"pickerGroup.border\":\"#d9d9d9\",\"editor.hoverHighlightBackground\":\"#339cec33\",\"editorHoverWidget.background\":\"#f0f0f0\",\"editorHoverWidget.foreground\":\"#403f53\",\"editorHoverWidget.border\":\"#d9d9d9\",\"editorHoverWidget.statusBarBackground\":\"#e4e4e4\",\"titleBar.activeBackground\":\"var(--sl-color-gray-6)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#f0f0f099\",\"titleBar.inactiveForeground\":\"#33333399\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#b8b8b850\",\"toolbar.activeBackground\":\"#a6a6a650\",\"tab.activeBackground\":\"#f6f6f6\",\"tab.unfocusedActiveBackground\":\"#f6f6f6\",\"tab.inactiveBackground\":\"#f0f0f0\",\"tab.unfocusedInactiveBackground\":\"#f0f0f0\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#403f53\",\"tab.unfocusedActiveForeground\":\"#403f53b3\",\"tab.unfocusedInactiveForeground\":\"#403f5380\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#f0f0f0\",\"tab.lastPinnedBorder\":\"#a9a9a9\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":null,\"tab.activeBorderTop\":\"var(--sl-color-accent)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#2aa298\",\"tab.inactiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedActiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedInactiveModifiedBorder\":\"#93a1a1\",\"badge.background\":\"#2aa298\",\"badge.foreground\":\"#f0f0f0\",\"button.background\":\"#2aa298\",\"button.foreground\":\"#f0f0f0\",\"button.border\":null,\"button.separator\":\"#f0f0f066\",\"button.hoverBackground\":\"#22827a\",\"button.secondaryBackground\":\"#5f6a79\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#4c5561\",\"dropdown.background\":\"#f0f0f0\",\"dropdown.foreground\":\"#403f53\",\"dropdown.border\":\"#d9d9d9\",\"list.activeSelectionBackground\":\"#d3e8f8\",\"list.activeSelectionForeground\":\"#403f53\",\"tree.indentGuidesStroke\":\"#a9a9a9\",\"input.background\":\"#f0f0f0\",\"input.foreground\":\"#403f53\",\"input.placeholderForeground\":\"#93a1a1\",\"inputOption.activeBorder\":\"#2aa298\",\"inputOption.hoverBackground\":\"#b8b8b850\",\"inputOption.activeBackground\":\"#93a1a133\",\"inputOption.activeForeground\":\"#000000\",\"inputValidation.infoBackground\":\"#f0f0f0\",\"inputValidation.infoBorder\":\"#d0d0d0\",\"inputValidation.warningBackground\":\"#daaa01\",\"inputValidation.warningBorder\":\"#e0af02\",\"inputValidation.errorBackground\":\"#f76e6e\",\"inputValidation.errorBorder\":\"#de3d3b\",\"keybindingLabel.background\":\"#dddddd66\",\"keybindingLabel.foreground\":\"#555555\",\"keybindingLabel.border\":\"#cccccc66\",\"keybindingLabel.bottomBorder\":\"#bbbbbb66\",\"menu.foreground\":\"#403f53\",\"menu.background\":\"#f0f0f0\",\"menu.selectionForeground\":\"#403f53\",\"menu.selectionBackground\":\"#d3e8f8\",\"menu.separatorBackground\":\"#d4d4d4\",\"editor.snippetTabstopHighlightBackground\":\"#0a326433\",\"editor.snippetFinalTabstopHighlightBorder\":\"#0a326480\",\"terminal.ansiBlack\":\"#403f53\",\"terminal.ansiRed\":\"#de3d3b\",\"terminal.ansiGreen\":\"#08916a\",\"terminal.ansiYellow\":\"#e0af02\",\"terminal.ansiBlue\":\"#288ed7\",\"terminal.ansiMagenta\":\"#d6438a\",\"terminal.ansiCyan\":\"#2aa298\",\"terminal.ansiWhite\":\"#f0f0f0\",\"terminal.ansiBrightBlack\":\"#403f53\",\"terminal.ansiBrightRed\":\"#de3d3b\",\"terminal.ansiBrightGreen\":\"#08916a\",\"terminal.ansiBrightYellow\":\"#daaa01\",\"terminal.ansiBrightBlue\":\"#288ed7\",\"terminal.ansiBrightMagenta\":\"#d6438a\",\"terminal.ansiBrightCyan\":\"#2aa298\",\"terminal.ansiBrightWhite\":\"#f0f0f0\",\"selection.background\":\"#7a8181ad\",\"notifications.background\":\"#f0f0f0\",\"notifications.foreground\":\"#403f53\",\"notificationLink.foreground\":\"#994cc3\",\"notifications.border\":\"#cccccc\",\"notificationCenter.border\":\"#cccccc\",\"notificationToast.border\":\"#cccccc\",\"notificationCenterHeader.foreground\":\"#403f53\",\"notificationCenterHeader.background\":\"#f0f0f0\",\"input.border\":\"#d9d9d9\",\"progressBar.background\":\"#2aa298\",\"list.inactiveSelectionBackground\":\"#e0e7ea\",\"list.inactiveSelectionForeground\":\"#403f53\",\"list.focusBackground\":\"#d3e8f8\",\"list.hoverBackground\":\"#d3e8f8\",\"list.focusForeground\":\"#403f53\",\"list.hoverForeground\":\"#403f53\",\"list.highlightForeground\":\"#403f53\",\"list.errorForeground\":\"#e64d49\",\"list.warningForeground\":\"#daaa01\",\"activityBar.background\":\"#f0f0f0\",\"activityBar.foreground\":\"#403f53\",\"activityBar.dropBackground\":\"#d0d0d0\",\"activityBarBadge.background\":\"#403f53\",\"activityBarBadge.foreground\":\"#f0f0f0\",\"activityBar.border\":\"#f0f0f0\",\"sideBar.background\":\"#f0f0f0\",\"sideBar.foreground\":\"#403f53\",\"sideBarTitle.foreground\":\"#403f53\",\"sideBar.border\":\"#f0f0f0\",\"editorGroup.background\":\"#f6f6f6\",\"editorCursor.foreground\":\"#90a7b2\",\"editor.wordHighlightBackground\":\"#339cec33\",\"editor.wordHighlightStrongBackground\":\"#007dd659\",\"editor.lineHighlightBackground\":\"#f0f0f0\",\"editor.rangeHighlightBackground\":\"#7497a633\",\"editorWhitespace.foreground\":\"#d9d9d9\",\"editorIndentGuide.background\":\"#d9d9d9\",\"editorCodeLens.foreground\":\"#403f53\",\"editorBracketMatch.background\":\"#d3e8f8\",\"editorBracketMatch.border\":\"#2aa298\",\"editorError.border\":\"#fbfbfb\",\"editorWarning.border\":\"#daaa01\",\"editorGutter.addedBackground\":\"#49d0c5\",\"editorGutter.modifiedBackground\":\"#6fbef6\",\"editorGutter.deletedBackground\":\"#f76e6e\",\"editorRuler.foreground\":\"#d9d9d9\",\"editorOverviewRuler.errorForeground\":\"#e64d49\",\"editorOverviewRuler.warningForeground\":\"#daaa01\",\"editorSuggestWidget.background\":\"#f0f0f0\",\"editorSuggestWidget.foreground\":\"#403f53\",\"editorSuggestWidget.highlightForeground\":\"#403f53\",\"editorSuggestWidget.selectedBackground\":\"#d3e8f8\",\"editorSuggestWidget.border\":\"#d9d9d9\",\"debugExceptionWidget.background\":\"#f0f0f0\",\"debugExceptionWidget.border\":\"#d9d9d9\",\"editorMarkerNavigation.background\":\"#d0d0d0\",\"editorMarkerNavigationError.background\":\"#f76e6e\",\"editorMarkerNavigationWarning.background\":\"#daaa01\",\"debugToolBar.background\":\"#f0f0f0\",\"extensionButton.prominentBackground\":\"#2aa298\",\"extensionButton.prominentForeground\":\"#f0f0f0\",\"statusBar.background\":\"#f0f0f0\",\"statusBar.border\":\"#f0f0f0\",\"statusBar.debuggingBackground\":\"#f0f0f0\",\"statusBar.debuggingForeground\":\"#403f53\",\"statusBar.foreground\":\"#403f53\",\"statusBar.noFolderBackground\":\"#f0f0f0\",\"statusBar.noFolderForeground\":\"#403f53\",\"peekView.border\":\"#d9d9d9\",\"peekViewEditor.background\":\"#f6f6f6\",\"peekViewEditorGutter.background\":\"#f6f6f6\",\"peekViewEditor.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.background\":\"#f0f0f0\",\"peekViewResult.fileForeground\":\"#403f53\",\"peekViewResult.lineForeground\":\"#403f53\",\"peekViewResult.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.selectionBackground\":\"#e0e7ea\",\"peekViewResult.selectionForeground\":\"#403f53\",\"peekViewTitle.background\":\"#f0f0f0\",\"peekViewTitleLabel.foreground\":\"#403f53\",\"peekViewTitleDescription.foreground\":\"#403f53\",\"terminal.foreground\":\"#403f53\"},\"fg\":\"#403f53\",\"bg\":\"#f6f7f9\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#556484\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#ae3c3afd\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#403f53\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#5f636f\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#3a688f\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"foreground\":\"#bb2060\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#5d6376\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#58656a\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#646464\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#286d70\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword Operator Comparison, imports, returns and Keyword Operator Ruby\",\"scope\":[\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.module.ruby\",\"keyword.control.class.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Control Conditional\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"foreground\":\"#486e26\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#a64348\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#186e73\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#984e4d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#3e697c\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#555ea2\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#111111\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#7c5686\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Symbols\",\"scope\":[\"constant.language.symbol.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#954f5a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#403f53\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\",\"variable.other.property.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#76578b\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-7)\",\"terminalBackground\":\"var(--sl-color-gray-7)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-7)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#0000001a\",\"markBorderColor\":\"#00000055\"}}}],\"defaultLocale\":\"en\",\"cascadeLayer\":\"starlight.components\",\"styleOverrides\":{\"borderRadius\":\"0px\",\"borderWidth\":\"1px\",\"codePaddingBlock\":\"0.75rem\",\"codePaddingInline\":\"1rem\",\"codeFontFamily\":\"var(--__sl-font-mono)\",\"codeFontSize\":\"var(--sl-text-code)\",\"codeLineHeight\":\"var(--sl-line-height)\",\"uiFontFamily\":\"var(--__sl-font)\",\"textMarkers\":{\"lineDiffIndicatorMarginLeft\":\"0.25rem\",\"defaultChroma\":\"45\",\"backgroundOpacity\":\"60%\"}},\"plugins\":[{\"name\":\"Starlight Plugin\",\"hooks\":{}},{\"name\":\"astro-expressive-code\",\"hooks\":{}}]}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[{\"codes\":[\"en\"],\"path\":\"en\"},{\"codes\":[\"ru\"],\"path\":\"ru\"}],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","docs",["Map",11,12,25,26,37,38],"index",{"id":11,"data":13,"body":22,"filePath":23,"digest":24,"deferredRender":16},{"title":14,"description":15,"editUrl":16,"head":17,"template":18,"sidebar":19,"pagefind":16,"draft":20},"Welcome","Get started building your docs site with Starlight.",true,[],"splash",{"hidden":20,"attrs":21},false,{},"import { Card, CardGrid } from '@astrojs/starlight/components';\n\n## Next steps\n\n\u003CCardGrid stagger>\n\t\u003CCard title=\"Update content\" icon=\"pencil\">\n\t\tEdit `src/content/docs/index.mdx` to see this page change.\n\t\u003C/Card>\n\t\u003CCard title=\"Change page layout\" icon=\"document\">\n\t\tDelete `template: splash` in `src/content/docs/index.mdx` to display a\n\t\tsidebar on this page.\n\t\u003C/Card>\n\t\u003CCard title=\"Add new content\" icon=\"add-document\">\n\t\tAdd Markdown or MDX files to `src/content/docs` to create new pages.\n\t\u003C/Card>\n\t\u003CCard title=\"Configure your site\" icon=\"setting\">\n\t\tEdit your `sidebar` and other config in `astro.config.mjs`.\n\t\u003C/Card>\n\t\u003CCard title=\"Read the docs\" icon=\"open-book\">\n\t\tLearn more in [the Starlight Docs](https://starlight.astro.build/).\n\t\u003C/Card>\n\u003C/CardGrid>","src/content/docs/index.mdx","576d77b3cc95d789","get-started/overview",{"id":25,"data":27,"body":34,"filePath":35,"digest":36,"deferredRender":16},{"title":28,"editUrl":16,"head":29,"template":30,"sidebar":31,"pagefind":16,"draft":20},"Overview",[],"doc",{"order":32,"hidden":20,"attrs":33},1,{},"import { FileTree, LinkCard } from '@astrojs/starlight/components';\n\n**Feature-Sliced Design** (FSD) is an architectural methodology for scaffolding front-end applications. Simply put, it's a compilation of rules and conventions on organizing code. The main purpose of this methodology is to make the project more understandable and stable in the face of ever-changing business requirements.\n\nApart from a set of conventions, FSD is also a toolchain. We have a [linter][ext-steiger] to check your project's architecture, [folder generators][ext-tools] through a CLI or IDEs, as well as a rich library of [examples][examples].\n\n## Is it right for me? \\{#is-it-right-for-me\\}\n\nFSD can be implemented in projects and teams of any size. It is right for your project if:\n\n- You're doing **frontend** (UI on web, mobile, desktop, etc.)\n- You're building an **application**, not a library\n\nAnd that's it! There are no restrictions on what programming language, UI framework, or state manager you use. You can also adopt FSD incrementally, use it in monorepos, and scale to great lengths by breaking your app into packages and implementing FSD individually within them.\n\nIf you already have an architecture and you're considering a switch to FSD, make sure that the current architecture is **causing trouble** in your team. For example, if your project has grown too large and inter-connected to efficiently implement new features, or if you're expecting a lot of new members to join the team. If the current architecture works, maybe it's not worth changing. But if you do decide to migrate, see the [Migration][migration] section for guidance.\n\n## Basic example \\{#basic-example\\}\n\nHere is a simple project that implements FSD:\n\n\u003CFileTree>\n\n- app/\n- pages/\n- shared/\n\n\u003C/FileTree>\n\nThese top-level folders are called _layers_. Let's look deeper:\n\n\u003CFileTree>\n\n- app/\n - routes/\n - analytics/\n- pages/\n - home/\n - article-reader/\n - ui/\n - api/\n - setings/\n- shared/\n - ui/\n - api/\n\n\u003C/FileTree>\n\nFolders inside `📂 pages` are called _slices_. They divide the layer by domain (in this case, by pages). \n\nFolders inside `📂 app`, `📂 shared`, and `📂 pages/article-reader` are called _segments_, and they divide slices (or layers) by technical purpose, i.e. what the code is for.\n\n## Concepts \\{#concepts\\}\n\nLayers, slices, and segments form a hierarchy like this:\n\n\u003Cfigure>\n ![Hierarchy of FSD concepts, described below](../../../../static/img/visual_schema.jpg)\n\n \u003Cfigcaption style={{ fontStyle: \"italic\", fontSize: \"0.9em\" }}>\n \u003Cp>Pictured above: three pillars, labeled left to right as \"Layers\", \"Slices\", and \"Segments\" respectively.\u003C/p>\n \u003Cp>The \"Layers\" pillar contains seven divisions arranged top to bottom and labeled \"app\", \"processes\", \"pages\", \"widgets\", \"features\", \"entities\", and \"shared\". The \"processes\" division is crossed out. The \"entities\" division is connected to the second pillar \"Slices\" in a way that conveys that the second pillar is the content of \"entities\".\u003C/p>\n \u003Cp>The \"Slices\" pillar contains three divisions arranged top to bottom and labeled \"user\", \"post\", and \"comment\". The \"post\" division is connected to the third pillar \"Segments\" in the same way such that it's the content of \"post\".\u003C/p>\n \u003Cp>The \"Segments\" pillar contains three divisions, arranged top to bottom and labeled \"ui\", \"model\", and \"api\".\u003C/p>\n \u003C/figcaption>\n\u003C/figure>\n\n### Layers \\{#layers\\}\n\nLayers are standardized across all FSD projects. You don't have to use all of the layers, but their names are important. There are currently seven of them (from top to bottom):\n\n1. **App** — everything that makes the app run — routing, entrypoints, global styles, providers.\n2. **Processes** (deprecated) — complex inter-page scenarios.\n3. **Pages** — full pages or large parts of a page in nested routing.\n4. **Widgets** — large self-contained chunks of functionality or UI, usually delivering an entire use case.\n5. **Features** — _reused_ implementations of entire product features, i.e. actions that bring business value to the user.\n6. **Entities** — business entities that the project works with, like `user` or `product`.\n7. **Shared** — reusable functionality, especially when it's detached from the specifics of the project/business, though not necessarily.\n\n:::warning\n\nLayers **App** and **Shared**, unlike other layers, do not have slices and are divided into segments directly.\n\nHowever, all other layers — **Entities**, **Features**, **Widgets**, and **Pages**, retain the structure in which you must first create slices, inside which you create the segments.\n\n:::\nThe trick with layers is that modules on one layer can only know about and import from modules from the layers strictly below.\n\n### Slices \\{#slices\\}\n\nNext up are slices, which partition the code by business domain. You're free to choose any names for them, and create as many as you wish. Slices make your codebase easier to navigate by keeping logically related modules close together.\n\nSlices cannot use other slices on the same layer, and that helps with high cohesion and low coupling.\n\n### Segments \\{#segments\\}\n\nSlices, as well as layers App and Shared, consist of segments, and segments group your code by its purpose. Segment names are not constrained by the standard, but there are several conventional names for the most common purposes:\n\n- `ui` — everything related to UI display: UI components, date formatters, styles, etc.\n- `api` — backend interactions: request functions, data types, mappers, etc.\n- `model` — the data model: schemas, interfaces, stores, and business logic.\n- `lib` — library code that other modules on this slice need.\n- `config` — configuration files and feature flags.\n\nUsually these segments are enough for most layers, you would only create your own segments in Shared or App, but this is not a rule.\n\n## Advantages \\{#advantages\\}\n\n- **Uniformity** \n Since the structure is standardized, projects become more uniform, which makes onboarding new members easier for the team.\n\n- **Stability in face of changes and refactoring** \n A module on one layer cannot use other modules on the same layer, or the layers above. \n This allows you to make isolated modifications without unforeseen consequences to the rest of the app.\n\n- **Controlled reuse of logic** \n Depending on the layer, you can make code very reusable or very local. \n This keeps a balance between following the **DRY** principle and practicality. \n\n- **Orientation to business and users needs** \n The app is split into business domains and usage of the business language is encouraged in naming, so that you can do useful product work without fully understanding all other unrelated parts of the project.\n\n## Incremental adoption \\{#incremental-adoption\\}\n\nIf you have an existing codebase that you want to migrate to FSD, we suggest the following strategy. We found it useful in our own migration experience.\n\n1. Start by slowly shaping up the App and Shared layers module-by-module to create a foundation.\n\n2. Distribute all of the existing UI across Widgets and Pages using broad strokes, even if they have dependencies that violate the rules of FSD.\n\n3. Start gradually resolving import violations and also extracting Entities and possibly even Features.\n\nIt's advised to refrain from adding new large entities while refactoring or refactoring only certain parts of the project.\n\n## Next steps \\{#next-steps\\}\n\n\u003CLinkCard\n title=\"Want to get a good grasp of how to think in FSD?\"\n href=\"/get-started/tutorial\"\n description=\"Check out the Tutorial\"\n/>\n\n\u003CLinkCard\n title=\"Prefer to learn from examples?\"\n href=\"/examples\"\n description=\"We have a lot in the Examples section\"\n/>\n\n\u003CLinkCard\n title=\"Have questions?\"\n href=\"https://t.me/feature_sliced\"\n description=\"Drop by our Telegram chat and get help from the community\"\n/>\n\n[tutorial]: /docs/get-started/tutorial\n[examples]: /examples\n[migration]: /docs/guides/migration/from-custom\n[ext-steiger]: https://github.com/feature-sliced/steiger\n[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools\n[ext-telegram]: https://t.me/feature_sliced","src/content/docs/get-started/overview.mdx","8c2d79b4ffec811f","get-started/faq",{"id":37,"data":39,"body":45,"filePath":46,"digest":47,"deferredRender":16},{"title":40,"editUrl":16,"head":41,"template":30,"sidebar":42,"pagefind":16,"draft":20},"FAQ",[],{"order":43,"hidden":20,"attrs":44},20,{},"import { Aside } from '@astrojs/starlight/components';\n\n\u003CAside>\nYou can ask your question in our [Telegram chat][telegram], [Discord community][discord], and [GitHub Discussions][github-discussions].\n\u003C/Aside>\n\n### Is there a toolkit or a linter?\n\nYes! We have a linter called [Steiger][ext-steiger] to check your project's architecture and [folder generators][ext-tools] through a CLI or IDEs.\n\n### Where to store the layout/template of pages?\n\nIf you need plain markup layouts, you can keep them in `shared/ui`. If you need to use higher layers inside, there are a few options:\n\n- Perhaps you don't need layouts at all? If the layout is only a few lines, it might be reasonable to duplicate the code in each page rather than try to abstract it.\n- If you do need layouts, you can have them as separate widgets or pages, and compose them in your router configuration in App. Nested routing is another option.\n\n### What is the difference between a feature and an entity?\n\nAn _entity_ is a real-life concept that your app is working with. A _feature_ is an interaction that provides real-life value to your app’s users, the thing people want to do with your entities.\n\nFor more information, along with examples, see the Reference page on [slices][reference-entities].\n\n### Can I embed pages/features/entities into each other?\n\nYes, but this embedding should happen in higher layers. For example, inside a widget, you can import both features and then insert one feature into another as props/children.\n\nYou cannot import one feature from another feature, this is prohibited by the [**import rule on layers**][import-rule-layers].\n\n### What about Atomic Design?\n\nThe current version of the methodology does not require nor prohibit the use of Atomic Design together with Feature-Sliced Design.\n\nFor example, Atomic Design [can be applied well](https://t.me/feature_sliced/1653) for the `ui` segment of modules.\n\n### Are there any useful resources/articles/etc. about FSD?\n\nYes! https://github.com/feature-sliced/awesome\n\n### Why do I need Feature-Sliced Design?\n\nIt helps you and your team to quickly overview the project in terms of its main value-bringing components. A standardized architecture helps to speed up onboarding and resolves debates about code structure. See the [motivation][motivation] page to learn more about why FSD was created.\n\n### Does a novice developer need an architecture/methodology?\n\nRather yes than no\n\n*Usually, when you design and develop a project in one person, everything goes smoothly. But if there are pauses in development, new developers are added to the team - then problems come*\n\n### How do I work with the authorization context?\n\nAnswered [here](/docs/guides/examples/auth)\n\n[ext-steiger]: https://github.com/feature-sliced/steiger\n[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools\n[import-rule-layers]: /docs/reference/layers#import-rule-on-layers\n[reference-entities]: /docs/reference/layers#entities\n[motivation]: /docs/about/motivation\n[telegram]: https://t.me/feature_sliced\n[discord]: https://discord.gg/S8MzWTUsmp\n[github-discussions]: https://github.com/feature-sliced/documentation/discussions","src/content/docs/get-started/faq.mdx","75c7081b5c5388dc"] \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs index b3caa065da..238713b10c 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,16 +1,21 @@ // @ts-check import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; +import remarkHeaderId from 'remark-heading-id'; // https://astro.build/config export default defineConfig({ + markdown: { + remarkPlugins: [remarkHeaderId], + }, integrations: [ starlight({ title: 'Feature-Sliced Design', - defaultLocale: 'en', + defaultLocale: 'root', locales: { - en: { + root: { label: 'English', + lang: 'en', }, ru: { label: 'Русский', @@ -19,15 +24,8 @@ export default defineConfig({ social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }], sidebar: [ { - label: 'Guides', - items: [ - // Each item here is one entry in the navigation menu. - { label: 'Example Guide', slug: 'guides/example' }, - ], - }, - { - label: 'Reference', - autogenerate: { directory: 'reference' }, + label: '🚀 Get Started', + autogenerate: { directory: 'get-started' } }, ], }), diff --git a/package.json b/package.json index a429d2288e..773d62c7fd 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "eslint-config-prettier": "^9.1.2", "eslint-import-resolver-alias": "1.1.2", "prettier": "^3.6.2", + "remark-heading-id": "^1.0.1", "sass": "^1.93.2", "stylelint": "^16.24.0", "stylelint-config-recess-order": "^5.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c6060fc4e..4451d4549c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,9 @@ importers: prettier: specifier: ^3.6.2 version: 3.6.2 + remark-heading-id: + specifier: ^1.0.1 + version: 1.0.1 sass: specifier: ^1.93.2 version: 1.93.2 @@ -6752,6 +6755,10 @@ packages: remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + remark-heading-id@1.0.1: + resolution: {integrity: sha512-GmJjuCeEkYvwFlvn/Skjc/1Qafj71412gbQnrwUmP/tKskmAf1cMRlZRNoovV+aIvsSRkTb2rCmGv2b9RdoJbQ==} + engines: {node: '>=8'} + remark-mdx@3.1.1: resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} @@ -7597,6 +7604,9 @@ packages: unist-util-find-after@5.0.0: resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + unist-util-is@3.0.0: + resolution: {integrity: sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==} + unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} @@ -7618,12 +7628,18 @@ packages: unist-util-visit-children@3.0.0: resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + unist-util-visit-parents@2.1.2: + resolution: {integrity: sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==} + unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} unist-util-visit-parents@6.0.2: resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + unist-util-visit@1.4.1: + resolution: {integrity: sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==} + unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -10411,7 +10427,7 @@ snapshots: postcss: 8.5.6 postcss-nested: 6.2.0(postcss@8.5.6) unist-util-visit: 5.0.0 - unist-util-visit-parents: 6.0.1 + unist-util-visit-parents: 6.0.2 '@expressive-code/plugin-frames@0.41.6': dependencies: @@ -14484,7 +14500,7 @@ snapshots: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 + unist-util-visit-parents: 6.0.2 mdast-util-from-markdown@2.0.2: dependencies: @@ -16280,6 +16296,11 @@ snapshots: transitivePeerDependencies: - supports-color + remark-heading-id@1.0.1: + dependencies: + lodash: 4.17.21 + unist-util-visit: 1.4.1 + remark-mdx@3.1.1: dependencies: mdast-util-mdx: 3.0.0 @@ -17349,6 +17370,8 @@ snapshots: '@types/unist': 3.0.3 unist-util-is: 6.0.0 + unist-util-is@3.0.0: {} + unist-util-is@6.0.0: dependencies: '@types/unist': 3.0.3 @@ -17379,6 +17402,10 @@ snapshots: dependencies: '@types/unist': 3.0.3 + unist-util-visit-parents@2.1.2: + dependencies: + unist-util-is: 3.0.0 + unist-util-visit-parents@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -17389,6 +17416,10 @@ snapshots: '@types/unist': 3.0.3 unist-util-is: 6.0.0 + unist-util-visit@1.4.1: + dependencies: + unist-util-visit-parents: 2.1.2 + unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 diff --git a/src/content/docs/en/guides/example.md b/src/content/docs/en/guides/example.md deleted file mode 100644 index 29415f984e..0000000000 --- a/src/content/docs/en/guides/example.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Example Guide -description: A guide in my new Starlight docs site. ---- - -# Hello diff --git a/src/content/docs/get-started/faq.mdx b/src/content/docs/get-started/faq.mdx new file mode 100644 index 0000000000..9f1f00c4fb --- /dev/null +++ b/src/content/docs/get-started/faq.mdx @@ -0,0 +1,68 @@ +--- +# pagination_next: guides/examples/auth +title: FAQ +sidebar: + order: 20 +--- + +import { Aside } from '@astrojs/starlight/components'; + + + +### Is there a toolkit or a linter? + +Yes! We have a linter called [Steiger][ext-steiger] to check your project's architecture and [folder generators][ext-tools] through a CLI or IDEs. + +### Where to store the layout/template of pages? + +If you need plain markup layouts, you can keep them in `shared/ui`. If you need to use higher layers inside, there are a few options: + +- Perhaps you don't need layouts at all? If the layout is only a few lines, it might be reasonable to duplicate the code in each page rather than try to abstract it. +- If you do need layouts, you can have them as separate widgets or pages, and compose them in your router configuration in App. Nested routing is another option. + +### What is the difference between a feature and an entity? + +An _entity_ is a real-life concept that your app is working with. A _feature_ is an interaction that provides real-life value to your app’s users, the thing people want to do with your entities. + +For more information, along with examples, see the Reference page on [slices][reference-entities]. + +### Can I embed pages/features/entities into each other? + +Yes, but this embedding should happen in higher layers. For example, inside a widget, you can import both features and then insert one feature into another as props/children. + +You cannot import one feature from another feature, this is prohibited by the [**import rule on layers**][import-rule-layers]. + +### What about Atomic Design? + +The current version of the methodology does not require nor prohibit the use of Atomic Design together with Feature-Sliced Design. + +For example, Atomic Design [can be applied well](https://t.me/feature_sliced/1653) for the `ui` segment of modules. + +### Are there any useful resources/articles/etc. about FSD? + +Yes! https://github.com/feature-sliced/awesome + +### Why do I need Feature-Sliced Design? + +It helps you and your team to quickly overview the project in terms of its main value-bringing components. A standardized architecture helps to speed up onboarding and resolves debates about code structure. See the [motivation][motivation] page to learn more about why FSD was created. + +### Does a novice developer need an architecture/methodology? + +Rather yes than no + +*Usually, when you design and develop a project in one person, everything goes smoothly. But if there are pauses in development, new developers are added to the team - then problems come* + +### How do I work with the authorization context? + +Answered [here](/docs/guides/examples/auth) + +[ext-steiger]: https://github.com/feature-sliced/steiger +[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools +[import-rule-layers]: /docs/reference/layers#import-rule-on-layers +[reference-entities]: /docs/reference/layers#entities +[motivation]: /docs/about/motivation +[telegram]: https://t.me/feature_sliced +[discord]: https://discord.gg/S8MzWTUsmp +[github-discussions]: https://github.com/feature-sliced/documentation/discussions diff --git a/src/content/docs/get-started/overview.mdx b/src/content/docs/get-started/overview.mdx new file mode 100644 index 0000000000..12e94c65a5 --- /dev/null +++ b/src/content/docs/get-started/overview.mdx @@ -0,0 +1,166 @@ +--- +title: Overview +sidebar: + order: 1 +--- + +import { FileTree, LinkCard } from '@astrojs/starlight/components'; + +**Feature-Sliced Design** (FSD) is an architectural methodology for scaffolding front-end applications. Simply put, it's a compilation of rules and conventions on organizing code. The main purpose of this methodology is to make the project more understandable and stable in the face of ever-changing business requirements. + +Apart from a set of conventions, FSD is also a toolchain. We have a [linter][ext-steiger] to check your project's architecture, [folder generators][ext-tools] through a CLI or IDEs, as well as a rich library of [examples][examples]. + +## Is it right for me? \{#is-it-right-for-me\} + +FSD can be implemented in projects and teams of any size. It is right for your project if: + +- You're doing **frontend** (UI on web, mobile, desktop, etc.) +- You're building an **application**, not a library + +And that's it! There are no restrictions on what programming language, UI framework, or state manager you use. You can also adopt FSD incrementally, use it in monorepos, and scale to great lengths by breaking your app into packages and implementing FSD individually within them. + +If you already have an architecture and you're considering a switch to FSD, make sure that the current architecture is **causing trouble** in your team. For example, if your project has grown too large and inter-connected to efficiently implement new features, or if you're expecting a lot of new members to join the team. If the current architecture works, maybe it's not worth changing. But if you do decide to migrate, see the [Migration][migration] section for guidance. + +## Basic example \{#basic-example\} + +Here is a simple project that implements FSD: + + + +- app/ +- pages/ +- shared/ + + + +These top-level folders are called _layers_. Let's look deeper: + + + +- app/ + - routes/ + - analytics/ +- pages/ + - home/ + - article-reader/ + - ui/ + - api/ + - setings/ +- shared/ + - ui/ + - api/ + + + +Folders inside `📂 pages` are called _slices_. They divide the layer by domain (in this case, by pages). + +Folders inside `📂 app`, `📂 shared`, and `📂 pages/article-reader` are called _segments_, and they divide slices (or layers) by technical purpose, i.e. what the code is for. + +## Concepts \{#concepts\} + +Layers, slices, and segments form a hierarchy like this: + +
+ ![Hierarchy of FSD concepts, described below](../../../../static/img/visual_schema.jpg) + +
+

Pictured above: three pillars, labeled left to right as "Layers", "Slices", and "Segments" respectively.

+

The "Layers" pillar contains seven divisions arranged top to bottom and labeled "app", "processes", "pages", "widgets", "features", "entities", and "shared". The "processes" division is crossed out. The "entities" division is connected to the second pillar "Slices" in a way that conveys that the second pillar is the content of "entities".

+

The "Slices" pillar contains three divisions arranged top to bottom and labeled "user", "post", and "comment". The "post" division is connected to the third pillar "Segments" in the same way such that it's the content of "post".

+

The "Segments" pillar contains three divisions, arranged top to bottom and labeled "ui", "model", and "api".

+
+
+ +### Layers \{#layers\} + +Layers are standardized across all FSD projects. You don't have to use all of the layers, but their names are important. There are currently seven of them (from top to bottom): + +1. **App** — everything that makes the app run — routing, entrypoints, global styles, providers. +2. **Processes** (deprecated) — complex inter-page scenarios. +3. **Pages** — full pages or large parts of a page in nested routing. +4. **Widgets** — large self-contained chunks of functionality or UI, usually delivering an entire use case. +5. **Features** — _reused_ implementations of entire product features, i.e. actions that bring business value to the user. +6. **Entities** — business entities that the project works with, like `user` or `product`. +7. **Shared** — reusable functionality, especially when it's detached from the specifics of the project/business, though not necessarily. + +:::warning + +Layers **App** and **Shared**, unlike other layers, do not have slices and are divided into segments directly. + +However, all other layers — **Entities**, **Features**, **Widgets**, and **Pages**, retain the structure in which you must first create slices, inside which you create the segments. + +::: +The trick with layers is that modules on one layer can only know about and import from modules from the layers strictly below. + +### Slices \{#slices\} + +Next up are slices, which partition the code by business domain. You're free to choose any names for them, and create as many as you wish. Slices make your codebase easier to navigate by keeping logically related modules close together. + +Slices cannot use other slices on the same layer, and that helps with high cohesion and low coupling. + +### Segments \{#segments\} + +Slices, as well as layers App and Shared, consist of segments, and segments group your code by its purpose. Segment names are not constrained by the standard, but there are several conventional names for the most common purposes: + +- `ui` — everything related to UI display: UI components, date formatters, styles, etc. +- `api` — backend interactions: request functions, data types, mappers, etc. +- `model` — the data model: schemas, interfaces, stores, and business logic. +- `lib` — library code that other modules on this slice need. +- `config` — configuration files and feature flags. + +Usually these segments are enough for most layers, you would only create your own segments in Shared or App, but this is not a rule. + +## Advantages \{#advantages\} + +- **Uniformity** + Since the structure is standardized, projects become more uniform, which makes onboarding new members easier for the team. + +- **Stability in face of changes and refactoring** + A module on one layer cannot use other modules on the same layer, or the layers above. + This allows you to make isolated modifications without unforeseen consequences to the rest of the app. + +- **Controlled reuse of logic** + Depending on the layer, you can make code very reusable or very local. + This keeps a balance between following the **DRY** principle and practicality. + +- **Orientation to business and users needs** + The app is split into business domains and usage of the business language is encouraged in naming, so that you can do useful product work without fully understanding all other unrelated parts of the project. + +## Incremental adoption \{#incremental-adoption\} + +If you have an existing codebase that you want to migrate to FSD, we suggest the following strategy. We found it useful in our own migration experience. + +1. Start by slowly shaping up the App and Shared layers module-by-module to create a foundation. + +2. Distribute all of the existing UI across Widgets and Pages using broad strokes, even if they have dependencies that violate the rules of FSD. + +3. Start gradually resolving import violations and also extracting Entities and possibly even Features. + +It's advised to refrain from adding new large entities while refactoring or refactoring only certain parts of the project. + +## Next steps \{#next-steps\} + + + + + + + +[tutorial]: /docs/get-started/tutorial +[examples]: /examples +[migration]: /docs/guides/migration/from-custom +[ext-steiger]: https://github.com/feature-sliced/steiger +[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools +[ext-telegram]: https://t.me/feature_sliced diff --git a/src/content/docs/en/index.mdx b/src/content/docs/index.mdx similarity index 100% rename from src/content/docs/en/index.mdx rename to src/content/docs/index.mdx diff --git a/src/pages/examples/index.tsx b/src/pages/examples/index.tsx index eeb30e6846..5ed36a7db1 100644 --- a/src/pages/examples/index.tsx +++ b/src/pages/examples/index.tsx @@ -9,30 +9,30 @@ const TITLE = translate({ id: "pages.examples.title" }); const DESCRIPTION = translate({ id: "pages.examples.subtitle" }); function ExamplesPage() { - return ( - -
-
-

{TITLE}

-

{DESCRIPTION}

-
-
-
-
- {examples.map((data) => ( -
- -
- ))} -
-
-
-
-
- ); + return ( + +
+
+

{TITLE}

+

{DESCRIPTION}

+
+
+
+
+ {examples.map((data) => ( +
+ +
+ ))} +
+
+
+
+
+ ); } export default ExamplesPage; From f73c21844342411235d62d99a6805e2a69c2c0e8 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 00:15:26 +0100 Subject: [PATCH 04/45] docs: migrate /get-started/tutorial page --- src/content/docs/get-started/tutorial.mdx | 2265 +++++++++++++++++++++ 1 file changed, 2265 insertions(+) create mode 100644 src/content/docs/get-started/tutorial.mdx diff --git a/src/content/docs/get-started/tutorial.mdx b/src/content/docs/get-started/tutorial.mdx new file mode 100644 index 0000000000..fb4a543b48 --- /dev/null +++ b/src/content/docs/get-started/tutorial.mdx @@ -0,0 +1,2265 @@ +--- +title: Tutorial +sidebar: + order: 2 +--- + +import { FileTree, Code } from '@astrojs/starlight/components'; + +## Part 1. On paper + +This tutorial will examine the Real World App, also known as Conduit. Conduit is a basic [Medium](https://medium.com/) clone — it lets you read and write articles as well as comment on the articles of others. + +![Conduit home page](../../../../static/img/tutorial/realworld-feed-anonymous.jpg) + +This is a pretty small application, so we will keep it simple and avoid excessive decomposition. It’s highly likely that the entire app will fit into just three layers: **App**, **Pages**, and **Shared**. If not, we will introduce additional layers as we go. Ready? + +### Start by listing the pages + +If we look at the screenshot above, we can assume at least the following pages: + +- Home (article feed) +- Sign in and sign up +- Article reader +- Article editor +- User profile viewer +- User profile editor (user settings) + +Every one of these pages will become its own *slice* on the Pages *layer*. Recall from the overview that slices are simply folders inside of layers and layers are simply folders with predefined names like `pages`. + +As such, our Pages folder will look like this: + + +- pages/ + - feed/ + - sign-in/ + - article-read/ + - article-edit/ + - profile/ + - settings/ + + +The key difference of Feature-Sliced Design from an unregulated code structure is that pages cannot reference each other. That is, one page cannot import code from another page. This is due to the **import rule on layers**: + +*A module (file) in a slice can only import other slices when they are located on layers strictly below.* + +In this case, a page is a slice, so modules (files) inside this page can only reference code from layers below, not from the same layer, Pages. + +### Close look at the feed + +
+ ![Anonymous user’s perspective](../../../../static/img/tutorial/realworld-feed-anonymous.jpg) +
+ _Anonymous user’s perspective_ +
+
+ +
+ ![Authenticated user’s perspective](../../../../static/img/tutorial/realworld-feed-authenticated.jpg) +
+ _Authenticated user’s perspective_ +
+
+ +There are three dynamic areas on the feed page: + +1. Sign-in links with an indication if you are signed in +2. List of tags that triggers filtering in the feed +3. One/two feeds of articles, each article with a like button + +The sign-in links are a part of a header that is common to all pages, we will revisit it separately. + +#### List of tags + +To build the list of tags, we need to fetch the available tags, render each tag as a chip, and store the selected tags in a client-side storage. These operations fall into categories “API interaction”, “user interface”, and “storage”, respectively. In Feature-Sliced Design, code is separated by purpose using *segments*. Segments are folders in slices, and they can have arbitrary names that describe the purpose, but some purposes are so common that there’s a convention for certain segment names: + +- 📂 `api/` for backend interactions +- 📂 `ui/` for code that handles rendering and appearance +- 📂 `model/` for storage and business logic +- 📂 `config/` for feature flags, environment variables and other forms of configuration + +We will place code that fetches tags into `api`, the tag component into `ui`, and the storage interaction into `model`. + +#### Articles + +Using the same grouping principles, we can decompose the feed of articles into the same three segments: + +- 📂 `api/`: fetch paginated articles with like count; like an article +- 📂 `ui/`: + - tab list that can render an extra tab if a tag is selected + - individual article + - functional pagination +- 📂 `model/`: client-side storage of the currently loaded articles and current page (if needed) + +### Reuse generic code + +Most pages are very different in intent, but certain things stay the same across the entire app — for example, the UI kit that conforms to the design language, or the convention on the backend that everything is done with a REST API with the same authentication method. Since slices are meant to be isolated, code reuse is facilitated by a lower layer, **Shared**. + +Shared is different from other layers in the sense that it contains segments, not slices. In this way, the Shared layer can be thought of as a hybrid between a layer and a slice. + +Usually, the code in Shared is not planned ahead of time, but rather extracted during development, because only during development does it become clear which parts of code are actually shared. However, it’s still helpful to keep a mental note of what kind of code naturally belongs in Shared: + +- 📂 `ui/` — the UI kit, pure appearance, no business logic. For example, buttons, modal dialogs, form inputs. +- 📂 `api/` — convenience wrappers around request making primitives (like `fetch()` on the Web) and, optionally, functions for triggering particular requests according to the backend specification. +- 📂 `config/` — parsing environment variables +- 📂 `i18n/` — configuration of language support +- 📂 `router/` — routing primitives and route constants + +Those are just a few examples of segment names in Shared, but you can omit any of them or create your own. The only important thing to remember when creating new segments is that segment names should describe **purpose (the why), not essence (the what)**. Names like “components”, “hooks”, “modals” *should not* be used because they describe what these files are, but don’t help to navigate the code inside. This requires people on the team to dig through every file in such folders and also keeps unrelated code close, which leads to broad areas of code being affected by refactoring and thus makes code review and testing harder. + +### Define a strict public API + +In the context of Feature-Sliced Design, the term *public API* refers to a slice or segment declaring what can be imported from it by other modules in the project. For example, in JavaScript that can be an `index.js` file re-exporting objects from other files in the slice. This enables freedom in refactoring code inside a slice as long as the contract with the outside world (i.e. the public API) stays the same. + +For the Shared layer that has no slices, it’s usually more convenient to define a separate public API for each segment as opposed to defining one single index of everything in Shared. This keeps imports from Shared naturally organized by intent. For other layers that have slices, the opposite is true — it’s usually more practical to define one index per slice and let the slice decide its own set of segments that is unknown to the outside world because other layers usually have a lot less exports. + +Our slices/segments will appear to each other as follows: + + +- pages/ + - feed/ + - index + - sign-in/ + - index + - article-read/ + - index + - ... +- shared/ + - ui/ + - index + - api/ + - index + - ... + + +Whatever is inside folders like `pages/feed` or `shared/ui` is only known to those folders, and other files should not rely on the internal structure of these folders. + +### Large reused blocks in the UI + +Earlier we made a note to revisit the header that appears on every page. Rebuilding it from scratch on every page would be impractical, so it’s only natural to want to reuse it. We already have Shared to facilitate code reuse, however, there’s a caveat to putting large blocks of UI in Shared — the Shared layer is not supposed to know about any of the layers above. + +Between Shared and Pages there are three other layers: Entities, Features, and Widgets. Some projects may have something in those layers that they need in a large reusable block, and that means we can’t put that reusable block in Shared, or else it would be importing from upper layers, which is prohibited. That’s where the Widgets layer comes in. It is located above Shared, Entities, and Features, so it can use them all. + +In our case, the header is very simple — it’s a static logo and top-level navigation. The navigation needs to make a request to the API to determine if the user is currently logged in or not, but that can be handled by a simple import from the `api` segment. Therefore, we will keep our header in Shared. + +### Close look at a page with a form + +Let’s also examine a page that’s intended for editing, not reading. For example, the article writer: + +![Conduit post editor](../../../../static/img/tutorial/realworld-editor-authenticated.jpg) + +It looks trivial, but contains several aspects of application development that we haven’t explored yet — form validation, error states, and data persistence. + +If we were to build this page, we would grab some inputs and buttons from Shared and put together a form in the `ui` segment of this page. Then, in the `api` segment, we would define a mutation request to create the article on the backend. + +To validate the request before sending, we need a validation schema, and a good place for it is the `model` segment, since it’s the data model. There we will produce error messages and display them using another component in the `ui` segment. + +To improve user experience, we could also persist the inputs to prevent accidental data loss. This is also a job of the `model` segment. + +### Summary + +We have examined several pages and outlined a preliminary structure for our application: + +1. Shared layer + 1. `ui` will contain our reusable UI kit + 2. `api` will contain our primitive interactions with the backend + 3. The rest will be arranged on demand +2. Pages layer — each page is a separate slice + 1. `ui` will contain the page itself and all of its parts + 2. `api` will contain more specialized data fetching, using `shared/api` + 3. `model` might contain client-side storage of the data that we will display + +Let’s get building! + +## Part 2. In code + +Now that we have a plan, let’s put it to practice. We will use React and [Remix](https://remix.run). + +There's a template ready for this project, clone it from GitHub to get a headstart: [https://github.com/feature-sliced/tutorial-conduit/tree/clean](https://github.com/feature-sliced/tutorial-conduit/tree/clean). + +Install dependencies with `npm install` and start the development server with `npm run dev`. Open [http://localhost:3000](http://localhost:3000) and you should see a blank app. + +### Lay out the pages + +Let’s start by creating blank components for all our pages. Run the following command in your project: + +```bash +npx fsd pages feed sign-in article-read article-edit profile settings --segments ui +``` + +This will create folders like `pages/feed/ui/` and an index file, `pages/feed/index.ts`, for every page. + +### Connect the feed page + +Let’s connect the root route of our application to the feed page. Create a component, `FeedPage.tsx` in `pages/feed/ui` and put the following inside it: + + +```tsx title="pages/feed/ui/FeedPage.tsx" +export function FeedPage() { + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+
+ ); +} +``` + +Then re-export this component in the feed page’s public API, the `pages/feed/index.ts` file: + + + +```ts title="pages/feed/index.ts" +export { FeedPage } from "./ui/FeedPage"; +``` + +Now connect it to the root route. In Remix, routing is file-based, and the route files are located in the `app/routes` folder, which nicely coincides with Feature-Sliced Design. + +Use the `FeedPage` component in `app/routes/_index.tsx`: + +```tsx title="app/routes/_index.tsx" +import type { MetaFunction } from "@remix-run/node"; +import { FeedPage } from "pages/feed"; + +export const meta: MetaFunction = () => { + return [{ title: "Conduit" }]; +}; + +export default FeedPage; +``` + +Then, if you run the dev server and open the application, you should see the Conduit banner! + +![The banner of Conduit](../../../../static/img/tutorial/conduit-banner.jpg) + +### API client + +To talk to the RealWorld backend, let’s create a convenient API client in Shared. Create two segments, `api` for the client and `config` for variables like the backend base URL: + +```bash +npx fsd shared --segments api config +``` + +Then create `shared/config/backend.ts`: + +```tsx title="shared/config/backend.ts" +export { mockBackendUrl as backendBaseUrl } from "mocks/handlers"; +``` + +```tsx title="shared/config/index.ts" +export { backendBaseUrl } from "./backend"; +``` + +Since the RealWorld project conveniently provides an [OpenAPI specification](https://github.com/gothinkster/realworld/blob/main/api/openapi.yml), we can take advantage of auto-generated types for our client. We will use [the `openapi-fetch` package](https://openapi-ts.pages.dev/openapi-fetch/) that comes with an additional type generator. + +Run the following command to generate up-to-date API typings: + +```bash +npm run generate-api-types +``` + +This will create a file `shared/api/v1.d.ts`. We will use this file to create a typed API client in `shared/api/client.ts`: + +```tsx title="shared/api/client.ts" +import createClient from "openapi-fetch"; + +import { backendBaseUrl } from "shared/config"; +import type { paths } from "./v1"; + +export const { GET, POST, PUT, DELETE } = createClient({ baseUrl: backendBaseUrl }); +``` + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; +``` + +### Real data in the feed + +We can now proceed to adding articles to the feed, fetched from the backend. Let’s begin by implementing an article preview component. + +Create `pages/feed/ui/ArticlePreview.tsx` with the following content: + +```tsx title="pages/feed/ui/ArticlePreview.tsx" +export function ArticlePreview({ article }) { /* TODO */ } +``` + +Since we’re writing in TypeScript, it would be nice to have a typed article object. If we explore the generated `v1.d.ts`, we can see that the article object is available through `components["schemas"]["Article"]`. So let’s create a file with our data models in Shared and export the models: + +```tsx title="shared/api/models.ts" +import type { components } from "./v1"; + +export type Article = components["schemas"]["Article"]; +``` + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; + +export type { Article } from "./models"; +``` + +Now we can come back to the article preview component and fill the markup with data. Update the component with the following content: + +```tsx title="pages/feed/ui/ArticlePreview.tsx" +import { Link } from "@remix-run/react"; +import type { Article } from "shared/api"; + +interface ArticlePreviewProps { + article: Article; +} + +export function ArticlePreview({ article }: ArticlePreviewProps) { + return ( +
+
+ + + +
+ + {article.author.username} + + + {new Date(article.createdAt).toLocaleDateString(undefined, { + dateStyle: "long", + })} + +
+ +
+ +

{article.title}

+

{article.description}

+ Read more... +
    + {article.tagList.map((tag) => ( +
  • + {tag} +
  • + ))} +
+ +
+ ); +} +``` + +The like button doesn’t do anything for now, we will fix that when we get to the article reader page and implement the liking functionality. + +Now we can fetch the articles and render out a bunch of these cards. Fetching data in Remix is done with *loaders* — server-side functions that fetch exactly what a page needs. Loaders interact with the API on the page’s behalf, so we will put them in the `api` segment of a page: + +```tsx title="pages/feed/api/loader.ts" +import { json } from "@remix-run/node"; + +import { GET } from "shared/api"; + +export const loader = async () => { + const { data: articles, error, response } = await GET("/articles"); + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return json({ articles }); +}; +``` + +To connect it to the page, we need to export it with the name `loader` from the route file: + +```tsx title="pages/feed/index.ts" +export { FeedPage } from "./ui/FeedPage"; +export { loader } from "./api/loader"; +``` + +```tsx title="app/routes/_index.tsx" +import type { MetaFunction } from "@remix-run/node"; +import { FeedPage } from "pages/feed"; + +export { loader } from "pages/feed"; + +export const meta: MetaFunction = () => { + return [{ title: "Conduit" }]; +}; + +export default FeedPage; +``` + +And the final step is to render these cards in the feed. Update your `FeedPage` with the following code: + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; + +export function FeedPage() { + const { articles } = useLoaderData(); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ {articles.articles.map((article) => ( + + ))} +
+
+
+
+ ); +} +``` + +### Filtering by tag + +Regarding the tags, our job is to fetch them from the backend and to store the currently selected tag. We already know how to do fetching — it’s another request from the loader. We will use a convenience function `promiseHash` from a package `remix-utils`, which is already installed. + +Update the loader file, `pages/feed/api/loader.ts`, with the following code: + +```tsx title="pages/feed/api/loader.ts" +import { json } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async () => { + return json( + await promiseHash({ + articles: throwAnyErrors(GET("/articles")), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +You might notice that we extracted the error handling into a generic function `throwAnyErrors`. It looks pretty useful, so we might want to reuse it later, but for now let’s just keep an eye on it. + +Now, to the list of tags. It needs to be interactive — clicking on a tag should make that tag selected. By Remix convention, we will use the URL search parameters as our storage for the selected tag. Let the browser take care of storage while we focus on more important things. + +Update `pages/feed/ui/FeedPage.tsx` with the following code: + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { Form, useLoaderData } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import type { loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; + +export function FeedPage() { + const { articles, tags } = useLoaderData(); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ {articles.articles.map((article) => ( + + ))} +
+ +
+
+

Popular Tags

+ +
+ +
+ {tags.tags.map((tag) => ( + + ))} +
+ +
+
+
+
+
+ ); +} +``` + +Then we need to use the `tag` search parameter in our loader. Change the `loader` function in `pages/feed/api/loader.ts` to the following: + +```tsx title="pages/feed/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const selectedTag = url.searchParams.get("tag") ?? undefined; + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles", { params: { query: { tag: selectedTag } } }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +That’s it, no `model` segment necessary. Remix is pretty neat. + +### Pagination + +In a similar fashion, we can implement the pagination. Feel free to give it a shot yourself or just copy the code below. There’s no one to judge you anyway. + +```tsx title="pages/feed/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +/** Amount of articles on one page. */ +export const LIMIT = 20; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const selectedTag = url.searchParams.get("tag") ?? undefined; + const page = parseInt(url.searchParams.get("page") ?? "", 10); + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles", { + params: { + query: { + tag: selectedTag, + limit: LIMIT, + offset: !Number.isNaN(page) ? page * LIMIT : undefined, + }, + }, + }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { Form, useLoaderData, useSearchParams } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import { LIMIT, type loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; + +export function FeedPage() { + const [searchParams] = useSearchParams(); + const { articles, tags } = useLoaderData(); + const pageAmount = Math.ceil(articles.articlesCount / LIMIT); + const currentPage = parseInt(searchParams.get("page") ?? "1", 10); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ {articles.articles.map((article) => ( + + ))} + +
+ +
    + {Array(pageAmount) + .fill(null) + .map((_, index) => + index + 1 === currentPage ? ( +
  • + {index + 1} +
  • + ) : ( +
  • + +
  • + ), + )} +
+ +
+ +
+
+

Popular Tags

+ +
+ +
+ {tags.tags.map((tag) => ( + + ))} +
+ +
+
+
+
+
+ ); +} +``` + +So that’s also done. There’s also the tab list that can be similarly implemented, but let’s hold on to that until we implement authentication. Speaking of which! + +### Authentication + +Authentication involves two pages — one to login and another to register. They are mostly the same, so it makes sense to keep them in the same slice, `sign-in`, so that they can reuse code if needed. + +Create `RegisterPage.tsx` in the `ui` segment of `pages/sign-in` with the following content: + +```tsx title="pages/sign-in/ui/RegisterPage.tsx" +import { Form, Link, useActionData } from "@remix-run/react"; + +import type { register } from "../api/register"; + +export function RegisterPage() { + const registerData = useActionData(); + + return ( +
+
+
+
+

Sign up

+

+ Have an account? +

+ + {registerData?.error && ( +
    + {registerData.error.errors.body.map((error) => ( +
  • {error}
  • + ))} +
+ )} + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ ); +} +``` + +We have a broken import to fix now. It involves a new segment, so create that: + +```bash +npx fsd pages sign-in -s api +``` + +However, before we can implement the backend part of registering, we need some infrastructure code for Remix to handle sessions. That goes to Shared, in case any other page needs it. + +Put the following code in `shared/api/auth.server.ts`. This is highly Remix-specific, so don’t worry too much about it, just copy-paste: + +```tsx title="shared/api/auth.server.ts" +import { createCookieSessionStorage, redirect } from "@remix-run/node"; +import invariant from "tiny-invariant"; + +import type { User } from "./models"; + +invariant( + process.env.SESSION_SECRET, + "SESSION_SECRET must be set for authentication to work", +); + +const sessionStorage = createCookieSessionStorage<{ + user: User; +}>({ + cookie: { + name: "__session", + httpOnly: true, + path: "/", + sameSite: "lax", + secrets: [process.env.SESSION_SECRET], + secure: process.env.NODE_ENV === "production", + }, +}); + +export async function createUserSession({ + request, + user, + redirectTo, +}: { + request: Request; + user: User; + redirectTo: string; +}) { + const cookie = request.headers.get("Cookie"); + const session = await sessionStorage.getSession(cookie); + + session.set("user", user); + + return redirect(redirectTo, { + headers: { + "Set-Cookie": await sessionStorage.commitSession(session, { + maxAge: 60 * 60 * 24 * 7, // 7 days + }), + }, + }); +} + +export async function getUserFromSession(request: Request) { + const cookie = request.headers.get("Cookie"); + const session = await sessionStorage.getSession(cookie); + + return session.get("user") ?? null; +} + +export async function requireUser(request: Request) { + const user = await getUserFromSession(request); + + if (user === null) { + throw redirect("/login"); + } + + return user; +} +``` + +And also export the `User` model from the `models.ts` file right next to it: + +```tsx title="shared/api/models.ts" +import type { components } from "./v1"; + +export type Article = components["schemas"]["Article"]; +export type User = components["schemas"]["User"]; +``` + +Before this code can work, the `SESSION_SECRET` environment variable needs to be set. Create a file called `.env` in the root of the project, write `SESSION_SECRET=` and then mash some keys on your keyboard to create a long random string. You should get something like this: + +```bash title=".env" +SESSION_SECRET=dontyoudarecopypastethis +``` + +Finally, add some exports to the public API to make use of this code: + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; + +export type { Article } from "./models"; + +export { createUserSession, getUserFromSession, requireUser } from "./auth.server"; +``` + +Now we can write the code that will talk to the RealWorld backend to actually do the registration. We will keep that in `pages/sign-in/api`. Create a file called `register.ts` and put the following code inside: + +```tsx title="pages/sign-in/api/register.ts" +import { json, type ActionFunctionArgs } from "@remix-run/node"; + +import { POST, createUserSession } from "shared/api"; + +export const register = async ({ request }: ActionFunctionArgs) => { + const formData = await request.formData(); + const username = formData.get("username")?.toString() ?? ""; + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + + const { data, error } = await POST("/users", { + body: { user: { email, password, username } }, + }); + + if (error) { + return json({ error }, { status: 400 }); + } else { + return createUserSession({ + request: request, + user: data.user, + redirectTo: "/", + }); + } +}; +``` + +```tsx title="pages/sign-in/index.ts" +export { RegisterPage } from './ui/RegisterPage'; +export { register } from './api/register'; +``` + +Almost done! Just need to connect the page and action to the `/register` route. Create `register.tsx` in `app/routes`: + +```tsx title="app/routes/register.tsx" +import { RegisterPage, register } from "pages/sign-in"; + +export { register as action }; + +export default RegisterPage; +``` + +Now if you go to [http://localhost:3000/register](http://localhost:3000/register), you should be able to create a user! The rest of the application won’t react to this yet, we’ll address that momentarily. + +In a very similar way, we can implement the login page. Give it a try or just grab the code and move on: + +```tsx title="pages/sign-in/api/sign-in.ts" +import { json, type ActionFunctionArgs } from "@remix-run/node"; + +import { POST, createUserSession } from "shared/api"; + +export const signIn = async ({ request }: ActionFunctionArgs) => { + const formData = await request.formData(); + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + + const { data, error } = await POST("/users/login", { + body: { user: { email, password } }, + }); + + if (error) { + return json({ error }, { status: 400 }); + } else { + return createUserSession({ + request: request, + user: data.user, + redirectTo: "/", + }); + } +}; +``` + +```tsx title="pages/sign-in/ui/SignInPage.tsx" +import { Form, Link, useActionData } from "@remix-run/react"; + +import type { signIn } from "../api/sign-in"; + +export function SignInPage() { + const signInData = useActionData(); + + return ( +
+
+
+
+

Sign in

+

+ Need an account? +

+ + {signInData?.error && ( +
    + {signInData.error.errors.body.map((error) => ( +
  • {error}
  • + ))} +
+ )} + +
+
+ +
+
+ +
+ +
+
+
+
+
+ ); +} +``` + +```tsx title="pages/sign-in/index.ts" +export { RegisterPage } from './ui/RegisterPage'; +export { register } from './api/register'; +export { SignInPage } from './ui/SignInPage'; +export { signIn } from './api/sign-in'; +``` + +```tsx title="app/routes/login.tsx" +import { SignInPage, signIn } from "pages/sign-in"; + +export { signIn as action }; + +export default SignInPage; +``` + +Now let’s give the users a way to actually get to these pages. + +### Header + +As we discussed in part 1, the app header is commonly placed either in Widgets or in Shared. We will put it in Shared because it’s very simple and all the business logic can be kept outside of it. Let’s create a place for it: + +```bash +npx fsd shared ui +``` + +Now create `shared/ui/Header.tsx` with the following contents: + +```tsx title="shared/ui/Header.tsx" +import { useContext } from "react"; +import { Link, useLocation } from "@remix-run/react"; + +import { CurrentUser } from "../api/currentUser"; + +export function Header() { + const currentUser = useContext(CurrentUser); + const { pathname } = useLocation(); + + return ( + + ); +} +``` + +Export this component from `shared/ui`: + +```tsx title="shared/ui/index.ts" +export { Header } from "./Header"; +``` + +In the header, we rely on the context that’s kept in `shared/api`. Create that as well: + +```tsx title="shared/api/currentUser.ts" +import { createContext } from "react"; + +import type { User } from "./models"; + +export const CurrentUser = createContext(null); +``` + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; + +export type { Article } from "./models"; + +export { createUserSession, getUserFromSession, requireUser } from "./auth.server"; +export { CurrentUser } from "./currentUser"; +``` + +Now let’s add the header to the page. We want it to be on every single page, so it makes sense to simply add it to the root route and wrap the outlet (the place where the page will be rendered) with the `CurrentUser` context provider. This way our entire app and also the header has access to the current user object. We will also add a loader to actually obtain the current user object from cookies. Drop the following into `app/root.tsx`: + +```tsx title="app/root.tsx" +import { cssBundleHref } from "@remix-run/css-bundle"; +import type { LinksFunction, LoaderFunctionArgs } from "@remix-run/node"; +import { + Links, + LiveReload, + Meta, + Outlet, + Scripts, + ScrollRestoration, + useLoaderData, +} from "@remix-run/react"; + +import { Header } from "shared/ui"; +import { getUserFromSession, CurrentUser } from "shared/api"; + +export const links: LinksFunction = () => [ + ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), +]; + +export const loader = ({ request }: LoaderFunctionArgs) => + getUserFromSession(request); + +export default function App() { + const user = useLoaderData(); + + return ( + + + + + + + + + + + + + +
+ + + + + + + + ); +} +``` + +At this point, you should end up with the following on the home page: + +
+ ![The feed page of Conduit, including the header, the feed, and the tags. The tabs are still missing.](../../../../static/img/tutorial/realworld-feed-without-tabs.jpg) + +
The feed page of Conduit, including the header, the feed, and the tags. The tabs are still missing.
+
+ +### Tabs + +Now that we can detect the authentication state, let’s also quickly implement the tabs and post likes to be done with the feed page. We need another form, but this page file is getting kind of large, so let’s move these forms into adjacent files. We will create `Tabs.tsx`, `PopularTags.tsx`, and `Pagination.tsx` with the following content: + +```tsx title="pages/feed/ui/Tabs.tsx" +import { useContext } from "react"; +import { Form, useSearchParams } from "@remix-run/react"; + +import { CurrentUser } from "shared/api"; + +export function Tabs() { + const [searchParams] = useSearchParams(); + const currentUser = useContext(CurrentUser); + + return ( +
+
+
    + {currentUser !== null && ( +
  • + +
  • + )} +
  • + +
  • + {searchParams.has("tag") && ( +
  • + + {searchParams.get("tag")} + +
  • + )} +
+
+
+ ); +} +``` + +```tsx title="pages/feed/ui/PopularTags.tsx" +import { Form, useLoaderData } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import type { loader } from "../api/loader"; + +export function PopularTags() { + const { tags } = useLoaderData(); + + return ( +
+

Popular Tags

+ +
+ +
+ {tags.tags.map((tag) => ( + + ))} +
+ +
+ ); +} +``` + +```tsx title="pages/feed/ui/Pagination.tsx" +import { Form, useLoaderData, useSearchParams } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import { LIMIT, type loader } from "../api/loader"; + +export function Pagination() { + const [searchParams] = useSearchParams(); + const { articles } = useLoaderData(); + const pageAmount = Math.ceil(articles.articlesCount / LIMIT); + const currentPage = parseInt(searchParams.get("page") ?? "1", 10); + + return ( +
+ +
    + {Array(pageAmount) + .fill(null) + .map((_, index) => + index + 1 === currentPage ? ( +
  • + {index + 1} +
  • + ) : ( +
  • + +
  • + ), + )} +
+ + ); +} +``` + +And now we can significantly simplify the feed page itself: + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; +import { Tabs } from "./Tabs"; +import { PopularTags } from "./PopularTags"; +import { Pagination } from "./Pagination"; + +export function FeedPage() { + const { articles } = useLoaderData(); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ + + {articles.articles.map((article) => ( + + ))} + + +
+ +
+ +
+
+
+
+ ); +} +``` + +We also need to account for the new tab in the loader function: + +```tsx title="pages/feed/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET, requireUser } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + /* unchanged */ +} + +/** Amount of articles on one page. */ +export const LIMIT = 20; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const selectedTag = url.searchParams.get("tag") ?? undefined; + const page = parseInt(url.searchParams.get("page") ?? "", 10); + + if (url.searchParams.get("source") === "my-feed") { + const userSession = await requireUser(request); + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles/feed", { + params: { + query: { + limit: LIMIT, + offset: !Number.isNaN(page) ? page * LIMIT : undefined, + }, + }, + headers: { Authorization: `Token ${userSession.token}` }, + }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); + } + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles", { + params: { + query: { + tag: selectedTag, + limit: LIMIT, + offset: !Number.isNaN(page) ? page * LIMIT : undefined, + }, + }, + }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +Before we leave the feed page, let’s add some code that handles likes to posts. Change your `ArticlePreview.tsx` to the following: + +```tsx title="pages/feed/ui/ArticlePreview.tsx" +import { Form, Link } from "@remix-run/react"; +import type { Article } from "shared/api"; + +interface ArticlePreviewProps { + article: Article; +} + +export function ArticlePreview({ article }: ArticlePreviewProps) { + return ( +
+
+ + + +
+ + {article.author.username} + + + {new Date(article.createdAt).toLocaleDateString(undefined, { + dateStyle: "long", + })} + +
+
+ +
+
+ +

{article.title}

+

{article.description}

+ Read more... +
    + {article.tagList.map((tag) => ( +
  • + {tag} +
  • + ))} +
+ +
+ ); +} +``` + +This code will send a POST request to `/article/:slug` with `_action=favorite` to mark the article as favorite. It won’t work yet, but as we start working on the article reader, we will implement this too. + +And with that we are officially done with the feed! Yay! + +### Article reader + +First, we need data. Let’s create a loader: + +```bash +npx fsd pages article-read -s api +``` + +```tsx title="pages/article-read/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import invariant from "tiny-invariant"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET, getUserFromSession } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async ({ request, params }: LoaderFunctionArgs) => { + invariant(params.slug, "Expected a slug parameter"); + const currentUser = await getUserFromSession(request); + const authorization = currentUser + ? { Authorization: `Token ${currentUser.token}` } + : undefined; + + return json( + await promiseHash({ + article: throwAnyErrors( + GET("/articles/{slug}", { + params: { + path: { slug: params.slug }, + }, + headers: authorization, + }), + ), + comments: throwAnyErrors( + GET("/articles/{slug}/comments", { + params: { + path: { slug: params.slug }, + }, + headers: authorization, + }), + ), + }), + ); +}; +``` + +```tsx title="pages/article-read/index.ts" +export { loader } from "./api/loader"; +``` + +Now we can connect it to the route `/article/:slug` by creating the a route file called `article.$slug.tsx`: + +```tsx title="app/routes/article.$slug.tsx" +export { loader } from "pages/article-read"; +``` + +The page itself consists of three main blocks — the article header with actions (repeated twice), the article body, and the comments section. This is the markup for the page, it’s not particularly interesting: + +```tsx title="pages/article-read/ui/ArticleReadPage.tsx" +import { useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { ArticleMeta } from "./ArticleMeta"; +import { Comments } from "./Comments"; + +export function ArticleReadPage() { + const { article } = useLoaderData(); + + return ( +
+
+
+

{article.article.title}

+ + +
+
+ +
+
+
+

{article.article.body}

+
    + {article.article.tagList.map((tag) => ( +
  • + {tag} +
  • + ))} +
+
+
+ +
+ +
+ +
+ +
+ +
+
+
+ ); +} +``` + +What’s more interesting is the `ArticleMeta` and `Comments`. They contain write operations such as liking an article, leaving a comment, etc. To get them to work, we first need to implement the backend part. Create `action.ts` in the `api` segment of the page: + +```tsx title="pages/article-read/api/action.ts" +import { redirect, type ActionFunctionArgs } from "@remix-run/node"; +import { namedAction } from "remix-utils/named-action"; +import { redirectBack } from "remix-utils/redirect-back"; +import invariant from "tiny-invariant"; + +import { DELETE, POST, requireUser } from "shared/api"; + +export const action = async ({ request, params }: ActionFunctionArgs) => { + const currentUser = await requireUser(request); + + const authorization = { Authorization: `Token ${currentUser.token}` }; + + const formData = await request.formData(); + + return namedAction(formData, { + async delete() { + invariant(params.slug, "Expected a slug parameter"); + await DELETE("/articles/{slug}", { + params: { path: { slug: params.slug } }, + headers: authorization, + }); + return redirect("/"); + }, + async favorite() { + invariant(params.slug, "Expected a slug parameter"); + await POST("/articles/{slug}/favorite", { + params: { path: { slug: params.slug } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async unfavorite() { + invariant(params.slug, "Expected a slug parameter"); + await DELETE("/articles/{slug}/favorite", { + params: { path: { slug: params.slug } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async createComment() { + invariant(params.slug, "Expected a slug parameter"); + const comment = formData.get("comment"); + invariant(typeof comment === "string", "Expected a comment parameter"); + await POST("/articles/{slug}/comments", { + params: { path: { slug: params.slug } }, + headers: { ...authorization, "Content-Type": "application/json" }, + body: { comment: { body: comment } }, + }); + return redirectBack(request, { fallback: "/" }); + }, + async deleteComment() { + invariant(params.slug, "Expected a slug parameter"); + const commentId = formData.get("id"); + invariant(typeof commentId === "string", "Expected an id parameter"); + const commentIdNumeric = parseInt(commentId, 10); + invariant( + !Number.isNaN(commentIdNumeric), + "Expected a numeric id parameter", + ); + await DELETE("/articles/{slug}/comments/{id}", { + params: { path: { slug: params.slug, id: commentIdNumeric } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async followAuthor() { + const authorUsername = formData.get("username"); + invariant( + typeof authorUsername === "string", + "Expected a username parameter", + ); + await POST("/profiles/{username}/follow", { + params: { path: { username: authorUsername } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async unfollowAuthor() { + const authorUsername = formData.get("username"); + invariant( + typeof authorUsername === "string", + "Expected a username parameter", + ); + await DELETE("/profiles/{username}/follow", { + params: { path: { username: authorUsername } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + }); +}; +``` + +Export that from the slice and then from the route. While we’re at it, let’s also connect the page itself: + +```tsx title="pages/article-read/index.ts" +export { ArticleReadPage } from "./ui/ArticleReadPage"; +export { loader } from "./api/loader"; +export { action } from "./api/action"; +``` + +```tsx title="app/routes/article.$slug.tsx" +import { ArticleReadPage } from "pages/article-read"; + +export { loader, action } from "pages/article-read"; + +export default ArticleReadPage; +``` + +Now, even though we haven’t implemented the like button on the reader page yet, the like button in the feed will start working! That’s because it’s been sending “like” requests to this route. Give that a try. + +`ArticleMeta` and `Comments` are, again, a bunch of forms. We’ve done this before, let’s grab their code and move on: + +```tsx title="pages/article-read/ui/ArticleMeta.tsx" +import { Form, Link, useLoaderData } from "@remix-run/react"; +import { useContext } from "react"; + +import { CurrentUser } from "shared/api"; +import type { loader } from "../api/loader"; + +export function ArticleMeta() { + const currentUser = useContext(CurrentUser); + const { article } = useLoaderData(); + + return ( +
+
+ + + + +
+ + {article.article.author.username} + + {article.article.createdAt} +
+ + {article.article.author.username == currentUser?.username ? ( + <> + + Edit Article + +    + + + ) : ( + <> + + +    + + + )} +
+
+ ); +} +``` + +```tsx title="pages/article-read/ui/Comments.tsx" +import { useContext } from "react"; +import { Form, Link, useLoaderData } from "@remix-run/react"; + +import { CurrentUser } from "shared/api"; +import type { loader } from "../api/loader"; + +export function Comments() { + const { comments } = useLoaderData(); + const currentUser = useContext(CurrentUser); + + return ( +
+ {currentUser !== null ? ( +
+
+ +
+
+ + +
+
+ ) : ( +
+
+

+ Sign in +   or   + Sign up +   to add comments on this article. +

+
+
+ )} + + {comments.comments.map((comment) => ( +
+
+

{comment.body}

+
+ +
+ + + +   + + {comment.author.username} + + {comment.createdAt} + {comment.author.username === currentUser?.username && ( + +
+ + +
+
+ )} +
+
+ ))} +
+ ); +} +``` + +And with that our article reader is also complete! The buttons to follow the author, like a post, and leave a comment should now function as expected. + +
+ ![Article reader with functioning buttons to like and follow](../../../../static/img/tutorial/realworld-article-reader.jpg) + +
Article reader with functioning buttons to like and follow
+
+ +### Article editor + +This is the last page that we will cover in this tutorial, and the most interesting part here is how we’re going to validate form data. + +The page itself, `article-edit/ui/ArticleEditPage.tsx`, will be quite simple, extra complexity stowed away into two other components: + +```tsx title="pages/article-edit/ui/ArticleEditPage.tsx" +import { Form, useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { TagsInput } from "./TagsInput"; +import { FormErrors } from "./FormErrors"; + +export function ArticleEditPage() { + const article = useLoaderData(); + + return ( +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+
+
+
+
+
+ ); +} +``` + +This page gets the current article (unless we’re writing from scratch) and fills in the corresponding form fields. We’ve seen this before. The interesting part is `FormErrors`, because it will receive the validation result and display it to the user. Let’s take a look: + +```tsx title="pages/article-edit/ui/FormErrors.tsx" +import { useActionData } from "@remix-run/react"; +import type { action } from "../api/action"; + +export function FormErrors() { + const actionData = useActionData(); + + return actionData?.errors != null ? ( +
    + {actionData.errors.map((error) => ( +
  • {error}
  • + ))} +
+ ) : null; +} +``` + +Here we are assuming that our action will return the `errors` field, an array of human-readable error messages. We will get to the action shortly. + +Another component is the tags input. It’s just a plain input field with an additional preview of chosen tags. Not much to see here: + +```tsx title="pages/article-edit/ui/TagsInput.tsx" +import { useEffect, useRef, useState } from "react"; + +export function TagsInput({ + name, + defaultValue, +}: { + name: string; + defaultValue?: Array; +}) { + const [tagListState, setTagListState] = useState(defaultValue ?? []); + + function removeTag(tag: string): void { + const newTagList = tagListState.filter((t) => t !== tag); + setTagListState(newTagList); + } + + const tagsInput = useRef(null); + useEffect(() => { + tagsInput.current && (tagsInput.current.value = tagListState.join(",")); + }, [tagListState]); + + return ( + <> + + setTagListState(e.target.value.split(",").filter(Boolean)) + } + /> +
+ {tagListState.map((tag) => ( + + + [" ", "Enter"].includes(e.key) && removeTag(tag) + } + onClick={() => removeTag(tag)} + >{" "} + {tag} + + ))} +
+ + ); +} +``` + +Now, for the API part. The loader should look at the URL, and if it contains an article slug, that means we’re editing an existing article, and its data should be loaded. Otherwise, return nothing. Let’s create that loader: + +```ts title="pages/article-edit/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; + +import { GET, requireUser } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async ({ params, request }: LoaderFunctionArgs) => { + const currentUser = await requireUser(request); + + if (!params.slug) { + return { article: null }; + } + + return throwAnyErrors( + GET("/articles/{slug}", { + params: { path: { slug: params.slug } }, + headers: { Authorization: `Token ${currentUser.token}` }, + }), + ); +}; +``` + +The action will take the new field values, run them through our data schema, and if everything is correct, commit those changes to the backend, either by updating an existing article or creating a new one: + +```tsx title="pages/article-edit/api/action.ts" +import { json, redirect, type ActionFunctionArgs } from "@remix-run/node"; + +import { POST, PUT, requireUser } from "shared/api"; +import { parseAsArticle } from "../model/parseAsArticle"; + +export const action = async ({ request, params }: ActionFunctionArgs) => { + try { + const { body, description, title, tags } = parseAsArticle( + await request.formData(), + ); + const tagList = tags?.split(",") ?? []; + + const currentUser = await requireUser(request); + const payload = { + body: { + article: { + title, + description, + body, + tagList, + }, + }, + headers: { Authorization: `Token ${currentUser.token}` }, + }; + + const { data, error } = await (params.slug + ? PUT("/articles/{slug}", { + params: { path: { slug: params.slug } }, + ...payload, + }) + : POST("/articles", payload)); + + if (error) { + return json({ errors: error }, { status: 422 }); + } + + return redirect(`/article/${data.article.slug ?? ""}`); + } catch (errors) { + return json({ errors }, { status: 400 }); + } +}; +``` + +The schema doubles as a parsing function for `FormData`, which allows us to conveniently get the clean fields or just throw the errors to handle at the end. Here’s how that parsing function could look: + +```tsx title="pages/article-edit/model/parseAsArticle.ts" +export function parseAsArticle(data: FormData) { + const errors = []; + + const title = data.get("title"); + if (typeof title !== "string" || title === "") { + errors.push("Give this article a title"); + } + + const description = data.get("description"); + if (typeof description !== "string" || description === "") { + errors.push("Describe what this article is about"); + } + + const body = data.get("body"); + if (typeof body !== "string" || body === "") { + errors.push("Write the article itself"); + } + + const tags = data.get("tags"); + if (typeof tags !== "string") { + errors.push("The tags must be a string"); + } + + if (errors.length > 0) { + throw errors; + } + + return { title, description, body, tags: data.get("tags") ?? "" } as { + title: string; + description: string; + body: string; + tags: string; + }; +} +``` + +Arguably, it’s a bit lengthy and repetitive, but that’s the price we pay for human-readable errors. This could also be a Zod schema, for example, but then we would have to render error messages on the frontend, and this form is not worth the complication. + +One last step — connect the page, the loader, and the action to the routes. Since we neatly support both creation and editing, we can export the same thing from both `editor._index.tsx` and `editor.$slug.tsx`: + +```tsx title="pages/article-edit/index.ts" +export { ArticleEditPage } from "./ui/ArticleEditPage"; +export { loader } from "./api/loader"; +export { action } from "./api/action"; +``` + +```tsx title="app/routes/editor._index.tsx, app/routes/editor.$slug.tsx (same content)" +import { ArticleEditPage } from "pages/article-edit"; + +export { loader, action } from "pages/article-edit"; + +export default ArticleEditPage; +``` + +We’re done now! Log in and try creating a new article. Or “forget” to write the article and see the validation kick in. + +
+ ![The Conduit article editor, with the title field saying “New article” and the rest of the fields empty. Above the form there are two errors: “**Describe what this article is about” and “Write the article itself”.**](../../../../static/img/tutorial/realworld-article-editor.jpg) + +
The Conduit article editor, with the title field saying “New article” and the rest of the fields empty. Above the form there are two errors: **“Describe what this article is about”** and **“Write the article itself”**.
+
+ +The profile and settings pages are very similar to the article reader and editor, they are left as an exercise for the reader, that’s you :) From 0f402eed950b6468e0d72eb036bd6f8a61f5e2e7 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 00:19:16 +0100 Subject: [PATCH 05/45] chore: add discord link --- astro.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astro.config.mjs b/astro.config.mjs index 238713b10c..1fe4d9ee6e 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -21,7 +21,7 @@ export default defineConfig({ label: 'Русский', } }, - social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }], + social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }, { icon: 'discord', label: 'Discord', href: 'https://discord.gg/S8MzWTUsmp' }], sidebar: [ { label: '🚀 Get Started', From a687cd2dc4f549ff0f385a03b82f963e0d4629c5 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 00:27:55 +0100 Subject: [PATCH 06/45] docs: migrate /guides/examples/auth --- astro.config.mjs | 9 ++ src/content/docs/guides/examples/auth.mdx | 176 ++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 src/content/docs/guides/examples/auth.mdx diff --git a/astro.config.mjs b/astro.config.mjs index 1fe4d9ee6e..4b38fa722c 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -27,6 +27,15 @@ export default defineConfig({ label: '🚀 Get Started', autogenerate: { directory: 'get-started' } }, + { + label: '🎯 Guides', + items: [ + { + label: 'Examples', + autogenerate: { directory: 'guides/examples' } + }, + ], + } ], }), ], diff --git a/src/content/docs/guides/examples/auth.mdx b/src/content/docs/guides/examples/auth.mdx new file mode 100644 index 0000000000..df90f41a8b --- /dev/null +++ b/src/content/docs/guides/examples/auth.mdx @@ -0,0 +1,176 @@ +--- +title: Authentication +sidebar: + order: 1 +--- + +import { FileTree } from '@astrojs/starlight/components'; + +Broadly, authentication consists of the following steps: + +1. Get the credentials from the user +1. Send them to the backend +1. Store the token to make authenticated requests + +## How to get credentials from the user + +We are assuming that your app is responsible for getting credentials. If you have authentication via OAuth, you can simply create a login page with a link to the OAuth provider's login page and skip to [step 3](#how-to-store-the-token-for-authenticated-requests). + +### Dedicated page for login + +Usually, websites have dedicated pages for login, where you enter your username and password. These pages are quite simple, so they don't require decomposition. Login and registration forms are quite similar in appearance, so they can even be grouped into one page. Create a slice for your login/registration page on the Pages layer: + + +- pages/ + - login/ + - ui/ + - LoginPage.tsx + - RegisterPage.tsx + - index.ts + - ... + + +Here we created two components and exported them both in the index file of the slice. These components will contain forms that are responsible for presenting the user with understandable controls to get their credentials. + +### Dialog for login + +If your app has a dialog for login that can be used on any page, consider making that dialog a widget. That way, you can still avoid too much decomposition, but have the freedom to reuse this dialog on any page. + +- 📂 widgets + - 📂 login-dialog + - 📂 ui + - 📄 LoginDialog.tsx + - 📄 index.ts + - other widgets… + +The rest of this guide is written for the dedicated page approach, but the same principles apply to the dialog widget. + +### Client-side validation + +Sometimes, especially for registration, it makes sense to perform client-side validation to let the user know quickly that they made a mistake. Validation can take place in the `model` segment of the login page. Use a schema validation library, for example, [Zod][ext-zod] for JS/TS, and expose that schema to the `ui` segment: + +```ts title="pages/login/model/registration-schema.ts" +import { z } from "zod"; + +export const registrationData = z.object({ + email: z.string().email(), + password: z.string().min(6), + confirmPassword: z.string(), +}).refine((data) => data.password === data.confirmPassword, { + message: "Passwords do not match", + path: ["confirmPassword"], +}); +``` + +Then, in the `ui` segment, you can use this schema to validate the user input: + +```tsx title="pages/login/ui/RegisterPage.tsx" +import { registrationData } from "../model/registration-schema"; + +function validate(formData: FormData) { + const data = Object.fromEntries(formData.entries()); + try { + registrationData.parse(data); + } catch (error) { + // TODO: Show error message to the user + } +} + +export function RegisterPage() { + return ( +
validate(new FormData(e.target))}> + + + + + + + + +
+ ) +} +``` + +## How to send credentials to the backend + +Create a function that makes a request to your backend's login endpoint. This function can either be called directly in the component code using a mutation library (e.g. TanStack Query), or it can be called as a side effect in a state manager. As explained in the [guide for API requests][examples-api-requests], you can put your request either in `shared/api` or in the `api` segment of your login page. + +### Two-factor authentication + +If your app supports two-factor authentication (2FA), you might have to redirect to another page where a user can enter a one-time password. Usually your `POST /login` request would return the user object with a flag indicating that the user has 2FA enabled. If that flag is set, redirect the user to the 2FA page. + +Since this page is very related to logging in, you can also keep it in the same slice, `login` on the Pages layer. + +You would also need another request function, similar to `login()` that we created above. Place them together, either in Shared, or in the `api` segment of the `login` page. + +## How to store the token for authenticated requests \{#how-to-store-the-token-for-authenticated-requests\} + +Regardless of the authentication scheme you have, be it a simple login & password, OAuth, or two-factor authentication, at the end you will receive a token. This token should be stored so that subsequent requests can identify themselves. + +The ideal token storage for a web app is a **cookie** — it requires no manual token storage or handling. As such, cookie storage needs almost no consideration from the frontend architecture side. If your frontend framework has a server side (for example, [Remix][ext-remix]), then you should store the server-side cookie infrastructure in `shared/api`. There is an example in [the Authentication section of the tutorial][tutorial-authentication] of how to do that with Remix. + +Sometimes, however, cookie storage is not an option. In this case, you will have to store the token manually. Apart from storing the token, you may also need to set up logic for refreshing your token when it expires. With FSD, there are several places where you can store the token, as well as several ways to make it available for the rest of the app. + +### In Shared + +This approach plays well with an API client defined in `shared/api` because the token is freely available for other request functions that require authentication to succeed. You can make the API client hold state, either with a reactive store or simply a module-level variable, and update that state in your `login()`/`logout()` functions. + +Automatic token refresh can be implemented as a middleware in the API client — something that can execute every time you make any request. It can work like this: + +- Authenticate and store the access token as well as the refresh token +- Make any request that requires authentication +- If the request fails with a status code that indicates token expiration, and there is a token in the store, make a refresh request, store the new tokens, and retry the original request + +One of the drawbacks of this approach is that the logic of managing and refreshing the token doesn't have a dedicated place. This can be fine for some apps or teams, but if the token management logic is more complex, it may be preferable to separate responsibilities of making requests and managing tokens. You can do that by keeping your requests and API client in `shared/api`, but the token store and management logic in `shared/auth`. + +Another drawback of this approach is that if your backend returns an object of your current user's information along with the token, you have to store that somewhere or discard that information and request it again from an endpoint like `/me` or `/users/current`. + +### In Entities + +It's common for FSD projects to have an entity for a user and/or an entity for the current user. It can even be the same entity for both. + +:::note + +The **current user** is also sometimes called "viewer" or "me". This is to distinguish the single authenticated user, with permissions and private information, from a list of all users with publicly accessible information. + +::: + +To store the token in the User entity, create a reactive store in the `model` segment. That store can contain both the token and the user object. + +Since the API client is usually defined in `shared/api` or spreaded across the entities, the main challenge to this approach is making the token available to other requests that need it without breaking [the import rule on layers][import-rule-on-layers]: + +> A module (file) in a slice can only import other slices when they are located on layers strictly below. + +There are several solutions to this challenge: + +1. **Pass the token manually every time you make a request** + This is the simplest solution, but it quickly becomes cumbersome, and if you don't have type safety, it's easy to forget. It's also not compatible with middlewares pattern for the API client in Shared. +1. **Expose the token to the entire app with a context or a global store like `localStorage`** + The key to retrieve the token will be kept in `shared/api` so that the API client can access it. The reactive store of the token will be exported from the User entity, and the context provider (if needed) will be set up on the App layer. This gives more freedom for designing the API client, however, this creates an implicit dependency on higher layers to provide context. When following this approach, consider providing helpful error messages if the context or `localStorage` are not set up correctly. +1. **Inject the token into the API client every time it changes** + If your store is reactive, you can create a subscription that will update the API client's token store every time the store in the entity changes. This is similar to the previous solution in that they both create an implicit dependency on higher layers, but this one is more imperative ("push"), while the previous one is more declarative ("pull"). + +Once you overcome the challenge of exposing the token that is stored in the entity's model, you can encode more business logic related to token management. For example, the `model` segment can contain logic to invalidate the token after a certain period of time, or to refresh the token when it expires. To actually make requests to the backend, use the `api` segment of the User entity or `shared/api`. + +### In Pages/Widgets (not recommended) + +It is discouraged to store app-wide state like an access token in pages or widgets. Avoid placing your token store in the `model` segment of the login page, instead choose from the first two solutions, Shared or Entities. + +## Logout and token invalidation + +Usually, apps don't have an entire page for logging out, but the logout functionality is still very important. It consists of an authenticated request to the backend and an update to the token store. + +If you store all your requests in `shared/api`, keep the logout request function there, close to the login function. Otherwise, consider keeping the logout request function next to the button that triggers it. For example, if you have a header widget that appears on every page and contains the logout link, put that request in the `api` segment of that widget. + +The update to the token store will have to be triggered from the place of the logout button, like a header widget. You can combine the request and the store update in the `model` segment of that widget. + +### Automatic logout + +Don't forget to build failsafes for when a request to log out fails, or a request to refresh a login token fails. In both of these cases, you should clear the token store. If you keep your token in Entities, this code can be placed in the `model` segment as it is pure business logic. If you keep your token in Shared, placing this logic in `shared/api` might bloat the segment and dilute its purpose. If you're noticing that your API segment contains two several unrelated things, consider splitting out the token management logic into another segment, for example, `shared/auth`. + +[tutorial-authentication]: /docs/get-started/tutorial#authentication +[import-rule-on-layers]: /docs/reference/layers#import-rule-on-layers +[examples-api-requests]: /docs/guides/examples/api-requests +[ext-remix]: https://remix.run +[ext-zod]: https://zod.dev From 9c604c898a15d6b3f54e2717e28889b5eee53f8c Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 00:29:42 +0100 Subject: [PATCH 07/45] chore: ignore .astro --- .astro/collections/docs.schema.json | 646 ---------------------------- .astro/content-assets.mjs | 1 - .astro/content-modules.mjs | 6 - .astro/content.d.ts | 218 ---------- .astro/data-store.json | 1 - .astro/settings.json | 5 - .astro/types.d.ts | 2 - .gitignore | 3 + 8 files changed, 3 insertions(+), 879 deletions(-) delete mode 100644 .astro/collections/docs.schema.json delete mode 100644 .astro/content-assets.mjs delete mode 100644 .astro/content-modules.mjs delete mode 100644 .astro/content.d.ts delete mode 100644 .astro/data-store.json delete mode 100644 .astro/settings.json delete mode 100644 .astro/types.d.ts diff --git a/.astro/collections/docs.schema.json b/.astro/collections/docs.schema.json deleted file mode 100644 index 9500aa03f6..0000000000 --- a/.astro/collections/docs.schema.json +++ /dev/null @@ -1,646 +0,0 @@ -{ - "$ref": "#/definitions/docs", - "definitions": { - "docs": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "editUrl": { - "anyOf": [ - { - "type": "string", - "format": "uri" - }, - { - "type": "boolean" - } - ], - "default": true - }, - "head": { - "type": "array", - "items": { - "type": "object", - "properties": { - "tag": { - "type": "string", - "enum": [ - "title", - "base", - "link", - "style", - "meta", - "script", - "noscript", - "template" - ] - }, - "attrs": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "boolean" - }, - { - "not": {} - } - ] - } - }, - "content": { - "type": "string" - } - }, - "required": [ - "tag" - ], - "additionalProperties": false - }, - "default": [] - }, - "tableOfContents": { - "anyOf": [ - { - "type": "object", - "properties": { - "minHeadingLevel": { - "type": "integer", - "minimum": 1, - "maximum": 6, - "default": 2 - }, - "maxHeadingLevel": { - "type": "integer", - "minimum": 1, - "maximum": 6, - "default": 3 - } - }, - "additionalProperties": false - }, - { - "type": "boolean" - } - ], - "default": { - "minHeadingLevel": 2, - "maxHeadingLevel": 3 - } - }, - "template": { - "type": "string", - "enum": [ - "doc", - "splash" - ], - "default": "doc" - }, - "hero": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "tagline": { - "type": "string" - }, - "image": { - "anyOf": [ - { - "type": "object", - "properties": { - "alt": { - "type": "string", - "default": "" - }, - "file": { - "type": "string" - } - }, - "required": [ - "file" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "alt": { - "type": "string", - "default": "" - }, - "dark": { - "type": "string" - }, - "light": { - "type": "string" - } - }, - "required": [ - "dark", - "light" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "html": { - "type": "string" - } - }, - "required": [ - "html" - ], - "additionalProperties": false - } - ] - }, - "actions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "link": { - "type": "string" - }, - "variant": { - "type": "string", - "enum": [ - "primary", - "secondary", - "minimal" - ], - "default": "primary" - }, - "icon": { - "anyOf": [ - { - "type": "string", - "enum": [ - "up-caret", - "down-caret", - "right-caret", - "left-caret", - "up-arrow", - "down-arrow", - "right-arrow", - "left-arrow", - "bars", - "translate", - "pencil", - "pen", - "document", - "add-document", - "setting", - "external", - "download", - "cloud-download", - "moon", - "sun", - "laptop", - "open-book", - "information", - "magnifier", - "forward-slash", - "close", - "error", - "warning", - "approve-check-circle", - "approve-check", - "rocket", - "star", - "puzzle", - "list-format", - "random", - "comment", - "comment-alt", - "heart", - "github", - "gitlab", - "bitbucket", - "codePen", - "farcaster", - "discord", - "gitter", - "twitter", - "x.com", - "mastodon", - "codeberg", - "youtube", - "threads", - "linkedin", - "twitch", - "azureDevOps", - "microsoftTeams", - "instagram", - "stackOverflow", - "telegram", - "rss", - "facebook", - "email", - "phone", - "reddit", - "patreon", - "signal", - "slack", - "matrix", - "hackerOne", - "openCollective", - "blueSky", - "discourse", - "zulip", - "pinterest", - "tiktok", - "astro", - "alpine", - "pnpm", - "biome", - "bun", - "mdx", - "apple", - "linux", - "homebrew", - "nix", - "starlight", - "pkl", - "node", - "cloudflare", - "vercel", - "netlify", - "deno", - "jsr", - "nostr", - "backstage", - "confluence", - "jira", - "storybook", - "vscode", - "jetbrains", - "zed", - "vim", - "figma", - "sketch", - "npm", - "sourcehut", - "substack", - "seti:folder", - "seti:bsl", - "seti:mdo", - "seti:salesforce", - "seti:asm", - "seti:bicep", - "seti:bazel", - "seti:c", - "seti:c-sharp", - "seti:html", - "seti:cpp", - "seti:clojure", - "seti:coldfusion", - "seti:config", - "seti:crystal", - "seti:crystal_embedded", - "seti:json", - "seti:css", - "seti:csv", - "seti:xls", - "seti:cu", - "seti:cake", - "seti:cake_php", - "seti:d", - "seti:word", - "seti:elixir", - "seti:elixir_script", - "seti:hex", - "seti:elm", - "seti:favicon", - "seti:f-sharp", - "seti:git", - "seti:go", - "seti:godot", - "seti:gradle", - "seti:grails", - "seti:graphql", - "seti:hacklang", - "seti:haml", - "seti:mustache", - "seti:haskell", - "seti:haxe", - "seti:jade", - "seti:java", - "seti:javascript", - "seti:jinja", - "seti:julia", - "seti:karma", - "seti:kotlin", - "seti:dart", - "seti:liquid", - "seti:livescript", - "seti:lua", - "seti:markdown", - "seti:argdown", - "seti:info", - "seti:clock", - "seti:maven", - "seti:nim", - "seti:github", - "seti:notebook", - "seti:nunjucks", - "seti:npm", - "seti:ocaml", - "seti:odata", - "seti:perl", - "seti:php", - "seti:pipeline", - "seti:pddl", - "seti:plan", - "seti:happenings", - "seti:powershell", - "seti:prisma", - "seti:pug", - "seti:puppet", - "seti:purescript", - "seti:python", - "seti:react", - "seti:rescript", - "seti:R", - "seti:ruby", - "seti:rust", - "seti:sass", - "seti:spring", - "seti:slim", - "seti:smarty", - "seti:sbt", - "seti:scala", - "seti:ethereum", - "seti:stylus", - "seti:svelte", - "seti:swift", - "seti:db", - "seti:terraform", - "seti:tex", - "seti:default", - "seti:twig", - "seti:typescript", - "seti:tsconfig", - "seti:vala", - "seti:vite", - "seti:vue", - "seti:wasm", - "seti:wat", - "seti:xml", - "seti:yml", - "seti:prolog", - "seti:zig", - "seti:zip", - "seti:wgt", - "seti:illustrator", - "seti:photoshop", - "seti:pdf", - "seti:font", - "seti:image", - "seti:svg", - "seti:sublime", - "seti:code-search", - "seti:shell", - "seti:video", - "seti:audio", - "seti:windows", - "seti:jenkins", - "seti:babel", - "seti:bower", - "seti:docker", - "seti:code-climate", - "seti:eslint", - "seti:firebase", - "seti:firefox", - "seti:gitlab", - "seti:grunt", - "seti:gulp", - "seti:ionic", - "seti:platformio", - "seti:rollup", - "seti:stylelint", - "seti:yarn", - "seti:webpack", - "seti:lock", - "seti:license", - "seti:makefile", - "seti:heroku", - "seti:todo", - "seti:ignored" - ] - }, - { - "type": "string", - "pattern": "^\\ import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Fget-started%2Foverview.mdx&astroContentModuleFlag=true")], -["src/content/docs/get-started/faq.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Fget-started%2Ffaq.mdx&astroContentModuleFlag=true")], -["src/content/docs/index.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fdocs%2Findex.mdx&astroContentModuleFlag=true")]]); - \ No newline at end of file diff --git a/.astro/content.d.ts b/.astro/content.d.ts deleted file mode 100644 index f65e91bb4e..0000000000 --- a/.astro/content.d.ts +++ /dev/null @@ -1,218 +0,0 @@ -declare module 'astro:content' { - interface Render { - '.mdx': Promise<{ - Content: import('astro').MDXContent; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - components: import('astro').MDXInstance<{}>['components']; - }>; - } -} - -declare module 'astro:content' { - export interface RenderResult { - Content: import('astro/runtime/server/index.js').AstroComponentFactory; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - } - interface Render { - '.md': Promise; - } - - export interface RenderedContent { - html: string; - metadata?: { - imagePaths: Array; - [key: string]: unknown; - }; - } -} - -declare module 'astro:content' { - type Flatten = T extends { [K: string]: infer U } ? U : never; - - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; - - type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; - - export type ReferenceDataEntry< - C extends CollectionKey, - E extends keyof DataEntryMap[C] = string, - > = { - collection: C; - id: E; - }; - export type ReferenceContentEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}) = string, - > = { - collection: C; - slug: E; - }; - export type ReferenceLiveEntry = { - collection: C; - id: string; - }; - - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( - collection: C, - filter?: (entry: CollectionEntry) => entry is E, - ): Promise; - export function getCollection( - collection: C, - filter?: (entry: CollectionEntry) => unknown, - ): Promise[]>; - - export function getLiveCollection( - collection: C, - filter?: LiveLoaderCollectionFilterType, - ): Promise< - import('astro').LiveDataCollectionResult, LiveLoaderErrorType> - >; - - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - entry: ReferenceContentEntry, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - entry: ReferenceDataEntry, - ): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - collection: C, - id: E, - ): E extends keyof DataEntryMap[C] - ? string extends keyof DataEntryMap[C] - ? Promise | undefined - : Promise - : Promise | undefined>; - export function getLiveEntry( - collection: C, - filter: string | LiveLoaderEntryFilterType, - ): Promise, LiveLoaderErrorType>>; - - /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: ReferenceContentEntry>[], - ): Promise[]>; - export function getEntries( - entries: ReferenceDataEntry[], - ): Promise[]>; - - export function render( - entry: AnyEntryMap[C][string], - ): Promise; - - export function reference( - collection: C, - ): import('astro/zod').ZodEffects< - import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? ReferenceContentEntry> - : ReferenceDataEntry - >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; - - type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< - ReturnTypeOrOriginal['schema']> - >; - - type ContentEntryMap = { - - }; - - type DataEntryMap = { - "docs": Record; - rendered?: RenderedContent; - filePath?: string; -}>; - - }; - - type AnyEntryMap = ContentEntryMap & DataEntryMap; - - type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< - infer TData, - infer TEntryFilter, - infer TCollectionFilter, - infer TError - > - ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } - : { data: never; entryFilter: never; collectionFilter: never; error: never }; - type ExtractDataType = ExtractLoaderTypes['data']; - type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; - type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; - type ExtractErrorType = ExtractLoaderTypes['error']; - - type LiveLoaderDataType = - LiveContentConfig['collections'][C]['schema'] extends undefined - ? ExtractDataType - : import('astro/zod').infer< - Exclude - >; - type LiveLoaderEntryFilterType = - ExtractEntryFilterType; - type LiveLoaderCollectionFilterType = - ExtractCollectionFilterType; - type LiveLoaderErrorType = ExtractErrorType< - LiveContentConfig['collections'][C]['loader'] - >; - - export type ContentConfig = typeof import("../src/content.config.js"); - export type LiveContentConfig = never; -} diff --git a/.astro/data-store.json b/.astro/data-store.json deleted file mode 100644 index 6f9276164c..0000000000 --- a/.astro/data-store.json +++ /dev/null @@ -1 +0,0 @@ -[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.16.16","content-config-digest","28e7bc87492477d9","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[null,null,null,null],\"rehypePlugins\":[[null,{\"experimentalHeadingIdCompat\":false}],null,[null,{\"themes\":[{\"name\":\"Night Owl No Italics\",\"type\":\"dark\",\"colors\":{\"focusBorder\":\"#122d42\",\"foreground\":\"#d6deeb\",\"disabledForeground\":\"#cccccc80\",\"descriptionForeground\":\"#d6deebb3\",\"errorForeground\":\"#ef5350\",\"icon.foreground\":\"#c5c5c5\",\"contrastActiveBorder\":null,\"contrastBorder\":\"#122d42\",\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#4f4f4f\",\"textLink.activeForeground\":\"#3794ff\",\"textLink.foreground\":\"#3794ff\",\"textPreformat.foreground\":\"#d7ba7d\",\"textSeparator.foreground\":\"#ffffff2e\",\"editor.background\":\"#23262f\",\"editor.foreground\":\"#d6deeb\",\"editorLineNumber.foreground\":\"#4b6479\",\"editorLineNumber.activeForeground\":\"#c5e4fd\",\"editorActiveLineNumber.foreground\":\"#c6c6c6\",\"editor.selectionBackground\":\"#1d3b53\",\"editor.inactiveSelectionBackground\":\"#7e57c25a\",\"editor.selectionHighlightBackground\":\"#5f7e9779\",\"editorError.foreground\":\"#ef5350\",\"editorWarning.foreground\":\"#b39554\",\"editorInfo.foreground\":\"#3794ff\",\"editorHint.foreground\":\"#eeeeeeb2\",\"problemsErrorIcon.foreground\":\"#ef5350\",\"problemsWarningIcon.foreground\":\"#b39554\",\"problemsInfoIcon.foreground\":\"#3794ff\",\"editor.findMatchBackground\":\"#5f7e9779\",\"editor.findMatchHighlightBackground\":\"#1085bb5d\",\"editor.findRangeHighlightBackground\":\"#3a3d4166\",\"editorLink.activeForeground\":\"#4e94ce\",\"editorLightBulb.foreground\":\"#ffcc00\",\"editorLightBulbAutoFix.foreground\":\"#75beff\",\"diffEditor.insertedTextBackground\":\"#99b76d23\",\"diffEditor.insertedTextBorder\":\"#c5e47833\",\"diffEditor.removedTextBackground\":\"#ef535033\",\"diffEditor.removedTextBorder\":\"#ef53504d\",\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#011627\",\"editorStickyScrollHover.background\":\"#2a2d2e\",\"editorInlayHint.background\":\"#5f7e97cc\",\"editorInlayHint.foreground\":\"#ffffff\",\"editorInlayHint.typeBackground\":\"#5f7e97cc\",\"editorInlayHint.typeForeground\":\"#ffffff\",\"editorInlayHint.parameterBackground\":\"#5f7e97cc\",\"editorInlayHint.parameterForeground\":\"#ffffff\",\"editorPane.background\":\"#011627\",\"editorGroup.emptyBackground\":\"#011627\",\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-black)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#011627\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#011627\",\"editorGroup.dropBackground\":\"#7e57c273\",\"editorGroup.dropIntoPromptForeground\":\"#d6deeb\",\"editorGroup.dropIntoPromptBackground\":\"#021320\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#011627\",\"sideBySideEditor.verticalBorder\":\"#011627\",\"scrollbar.shadow\":\"#010b14\",\"scrollbarSlider.background\":\"#ffffff17\",\"scrollbarSlider.hoverBackground\":\"#ffffff40\",\"scrollbarSlider.activeBackground\":\"#084d8180\",\"panel.background\":\"#011627\",\"panel.border\":\"#5f7e97\",\"panelTitle.activeBorder\":\"#5f7e97\",\"panelTitle.activeForeground\":\"#ffffffcc\",\"panelTitle.inactiveForeground\":\"#d6deeb80\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#011627\",\"widget.shadow\":\"#011627\",\"editorWidget.background\":\"#021320\",\"editorWidget.foreground\":\"#d6deeb\",\"editorWidget.border\":\"#5f7e97\",\"quickInput.background\":\"#021320\",\"quickInput.foreground\":\"#d6deeb\",\"quickInputTitle.background\":\"#ffffff1a\",\"pickerGroup.foreground\":\"#d1aaff\",\"pickerGroup.border\":\"#011627\",\"editor.hoverHighlightBackground\":\"#7e57c25a\",\"editorHoverWidget.background\":\"#011627\",\"editorHoverWidget.foreground\":\"#d6deeb\",\"editorHoverWidget.border\":\"#5f7e97\",\"editorHoverWidget.statusBarBackground\":\"#011a2f\",\"titleBar.activeBackground\":\"var(--sl-color-black)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#010e1a\",\"titleBar.inactiveForeground\":\"#eeefff99\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#5a5d5e50\",\"toolbar.activeBackground\":\"#63666750\",\"tab.activeBackground\":\"#0b2942\",\"tab.unfocusedActiveBackground\":\"#0b2942\",\"tab.inactiveBackground\":\"#01111d\",\"tab.unfocusedInactiveBackground\":\"#01111d\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#5f7e97\",\"tab.unfocusedActiveForeground\":\"#5f7e97\",\"tab.unfocusedInactiveForeground\":\"#5f7e97\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#272b3b\",\"tab.lastPinnedBorder\":\"#585858\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":\"#262a39\",\"tab.activeBorderTop\":\"var(--sl-color-accent-high)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#3399cc\",\"tab.inactiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedActiveModifiedBorder\":\"#3399cc80\",\"tab.unfocusedInactiveModifiedBorder\":\"#3399cc40\",\"badge.background\":\"#5f7e97\",\"badge.foreground\":\"#ffffff\",\"button.background\":\"#7e57c2cc\",\"button.foreground\":\"#ffffffcc\",\"button.border\":\"#122d42\",\"button.separator\":\"#ffffff52\",\"button.hoverBackground\":\"#7e57c2\",\"button.secondaryBackground\":\"#3a3d41\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#46494e\",\"dropdown.background\":\"#011627\",\"dropdown.foreground\":\"#ffffffcc\",\"dropdown.border\":\"#5f7e97\",\"list.activeSelectionBackground\":\"#234d708c\",\"list.activeSelectionForeground\":\"#ffffff\",\"tree.indentGuidesStroke\":\"#585858\",\"input.background\":\"#0b253a\",\"input.foreground\":\"#ffffffcc\",\"input.placeholderForeground\":\"#5f7e97\",\"inputOption.activeBorder\":\"#ffffffcc\",\"inputOption.hoverBackground\":\"#5a5d5e80\",\"inputOption.activeBackground\":\"#122d4266\",\"inputOption.activeForeground\":\"#ffffff\",\"inputValidation.infoBackground\":\"#00589ef2\",\"inputValidation.infoBorder\":\"#64b5f6\",\"inputValidation.warningBackground\":\"#675700f2\",\"inputValidation.warningBorder\":\"#ffca28\",\"inputValidation.errorBackground\":\"#ab0300f2\",\"inputValidation.errorBorder\":\"#ef5350\",\"keybindingLabel.background\":\"#8080802b\",\"keybindingLabel.foreground\":\"#cccccc\",\"keybindingLabel.border\":\"#33333399\",\"keybindingLabel.bottomBorder\":\"#44444499\",\"menu.foreground\":\"#ffffffcc\",\"menu.background\":\"#011627\",\"menu.selectionForeground\":\"#ffffff\",\"menu.selectionBackground\":\"#234d708c\",\"menu.separatorBackground\":\"#606060\",\"editor.snippetTabstopHighlightBackground\":\"#7c7c74c\",\"editor.snippetFinalTabstopHighlightBorder\":\"#525252\",\"terminal.ansiBlack\":\"#011627\",\"terminal.ansiRed\":\"#ef5350\",\"terminal.ansiGreen\":\"#22da6e\",\"terminal.ansiYellow\":\"#c5e478\",\"terminal.ansiBlue\":\"#82aaff\",\"terminal.ansiMagenta\":\"#c792ea\",\"terminal.ansiCyan\":\"#21c7a8\",\"terminal.ansiWhite\":\"#ffffff\",\"terminal.ansiBrightBlack\":\"#575656\",\"terminal.ansiBrightRed\":\"#ef5350\",\"terminal.ansiBrightGreen\":\"#22da6e\",\"terminal.ansiBrightYellow\":\"#ffeb95\",\"terminal.ansiBrightBlue\":\"#82aaff\",\"terminal.ansiBrightMagenta\":\"#c792ea\",\"terminal.ansiBrightCyan\":\"#7fdbca\",\"terminal.ansiBrightWhite\":\"#ffffff\",\"selection.background\":\"#4373c2\",\"input.border\":\"#5f7e97\",\"punctuation.definition.generic.begin.html\":\"#ef5350f2\",\"progress.background\":\"#7e57c2\",\"breadcrumb.foreground\":\"#a599e9\",\"breadcrumb.focusForeground\":\"#ffffff\",\"breadcrumb.activeSelectionForeground\":\"#ffffff\",\"breadcrumbPicker.background\":\"#001122\",\"list.invalidItemForeground\":\"#975f94\",\"list.dropBackground\":\"#011627\",\"list.focusBackground\":\"#010d18\",\"list.focusForeground\":\"#ffffff\",\"list.highlightForeground\":\"#ffffff\",\"list.hoverBackground\":\"#011627\",\"list.hoverForeground\":\"#ffffff\",\"list.inactiveSelectionBackground\":\"#0e293f\",\"list.inactiveSelectionForeground\":\"#5f7e97\",\"activityBar.background\":\"#011627\",\"activityBar.dropBackground\":\"#5f7e97\",\"activityBar.foreground\":\"#5f7e97\",\"activityBar.border\":\"#011627\",\"activityBarBadge.background\":\"#44596b\",\"activityBarBadge.foreground\":\"#ffffff\",\"sideBar.background\":\"#011627\",\"sideBar.foreground\":\"#89a4bb\",\"sideBar.border\":\"#011627\",\"sideBarTitle.foreground\":\"#5f7e97\",\"sideBarSectionHeader.background\":\"#011627\",\"sideBarSectionHeader.foreground\":\"#5f7e97\",\"editorCursor.foreground\":\"#80a4c2\",\"editor.wordHighlightBackground\":\"#f6bbe533\",\"editor.wordHighlightStrongBackground\":\"#e2a2f433\",\"editor.lineHighlightBackground\":\"#0003\",\"editor.rangeHighlightBackground\":\"#7e57c25a\",\"editorIndentGuide.background\":\"#5e81ce52\",\"editorIndentGuide.activeBackground\":\"#7e97ac\",\"editorRuler.foreground\":\"#5e81ce52\",\"editorCodeLens.foreground\":\"#5e82ceb4\",\"editorBracketMatch.background\":\"#5f7e974d\",\"editorOverviewRuler.currentContentForeground\":\"#7e57c2\",\"editorOverviewRuler.incomingContentForeground\":\"#7e57c2\",\"editorOverviewRuler.commonContentForeground\":\"#7e57c2\",\"editorGutter.background\":\"#011627\",\"editorGutter.modifiedBackground\":\"#e2b93d\",\"editorGutter.addedBackground\":\"#9ccc65\",\"editorGutter.deletedBackground\":\"#ef5350\",\"editorSuggestWidget.background\":\"#2c3043\",\"editorSuggestWidget.border\":\"#2b2f40\",\"editorSuggestWidget.foreground\":\"#d6deeb\",\"editorSuggestWidget.highlightForeground\":\"#ffffff\",\"editorSuggestWidget.selectedBackground\":\"#5f7e97\",\"debugExceptionWidget.background\":\"#011627\",\"debugExceptionWidget.border\":\"#5f7e97\",\"editorMarkerNavigation.background\":\"#0b2942\",\"editorMarkerNavigationError.background\":\"#ef5350\",\"editorMarkerNavigationWarning.background\":\"#ffca28\",\"peekView.border\":\"#5f7e97\",\"peekViewEditor.background\":\"#011627\",\"peekViewEditor.matchHighlightBackground\":\"#7e57c25a\",\"peekViewResult.background\":\"#011627\",\"peekViewResult.fileForeground\":\"#5f7e97\",\"peekViewResult.lineForeground\":\"#5f7e97\",\"peekViewResult.matchHighlightBackground\":\"#ffffffcc\",\"peekViewResult.selectionBackground\":\"#2e3250\",\"peekViewResult.selectionForeground\":\"#5f7e97\",\"peekViewTitle.background\":\"#011627\",\"peekViewTitleDescription.foreground\":\"#697098\",\"peekViewTitleLabel.foreground\":\"#5f7e97\",\"merge.currentHeaderBackground\":\"#5f7e97\",\"merge.incomingHeaderBackground\":\"#7e57c25a\",\"statusBar.background\":\"#011627\",\"statusBar.foreground\":\"#5f7e97\",\"statusBar.border\":\"#262a39\",\"statusBar.debuggingBackground\":\"#202431\",\"statusBar.debuggingBorder\":\"#1f2330\",\"statusBar.noFolderBackground\":\"#011627\",\"statusBar.noFolderBorder\":\"#25293a\",\"statusBarItem.activeBackground\":\"#202431\",\"statusBarItem.hoverBackground\":\"#202431\",\"statusBarItem.prominentBackground\":\"#202431\",\"statusBarItem.prominentHoverBackground\":\"#202431\",\"notifications.background\":\"#01111d\",\"notifications.border\":\"#262a39\",\"notificationCenter.border\":\"#262a39\",\"notificationToast.border\":\"#262a39\",\"notifications.foreground\":\"#ffffffcc\",\"notificationLink.foreground\":\"#80cbc4\",\"extensionButton.prominentForeground\":\"#ffffffcc\",\"extensionButton.prominentBackground\":\"#7e57c2cc\",\"extensionButton.prominentHoverBackground\":\"#7e57c2\",\"terminal.selectionBackground\":\"#1b90dd4d\",\"terminalCursor.background\":\"#234d70\",\"debugToolBar.background\":\"#011627\",\"welcomePage.buttonBackground\":\"#011627\",\"welcomePage.buttonHoverBackground\":\"#011627\",\"walkThrough.embeddedEditorBackground\":\"#011627\",\"gitDecoration.modifiedResourceForeground\":\"#a2bffc\",\"gitDecoration.deletedResourceForeground\":\"#ef535090\",\"gitDecoration.untrackedResourceForeground\":\"#c5e478ff\",\"gitDecoration.ignoredResourceForeground\":\"#395a75\",\"gitDecoration.conflictingResourceForeground\":\"#ffeb95cc\",\"source.elm\":\"#5f7e97\",\"string.quoted.single.js\":\"#ffffff\",\"meta.objectliteral.js\":\"#82aaff\"},\"fg\":\"#d6deeb\",\"bg\":\"#23262f\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#a2bffc\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#f27775fe\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#d6deeb\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#919f9f\",\"fontStyle\":\"\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#5ca7e4\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#caece6\",\"fontStyle\":\"\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"fontStyle\":\"\",\"foreground\":\"#c5e478\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"background\":\"#ff2c83\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#d3423e\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#919f9f\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#cdebf7\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#ffffff\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#7fdbca\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#80cbc4\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#57eaf1\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#fad430\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#faf39f\",\"fontStyle\":\"\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#82aaff\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Comparison, returns, imports, and Keyword Operator Ruby\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#989da0\",\"background\":\"#F78C6C\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"background\":\"#8BD649\",\"foreground\":\"#ffffff\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#ffffff\",\"background\":\"#ec5f67\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#ef787f\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#d9f5dd\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#e2817f\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#31e1eb\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Unquoted String Zone\",\"scope\":[\"string.unquoted.preprocessor.message.cs\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C# Region\",\"scope\":[\"punctuation.separator.hash.cs\",\"keyword.preprocessor.region.cs\",\"keyword.preprocessor.endregion.cs\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"bold\"}},{\"name\":\"C# Other Variables\",\"scope\":[\"variable.other.object.cs\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"C# Enum\",\"scope\":[\"entity.name.type.enum.cs\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Dart String\",\"scope\":[\"string.interpolated.single.dart\",\"string.interpolated.double.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Dart Class\",\"scope\":[\"support.class.dart\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#ff6d6d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#dddddd\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#e0dec6\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#6ae9f0\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#78ccf0\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#8b96ea\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#ffcb8b\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#c789d6\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#ecc48d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#c792ea\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#c5e478\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#969bb7\",\"fontStyle\":\"\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#80cbc4\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#ff869a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#d6deeb\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#82b1ff\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#ff6a83\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#b2ccd6\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#8eace3\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#c5e478\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#bec5d4\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#f78c6c\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#ffeb95\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#889fb2\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#7fdbca\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#d7dbe0\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#f78c6c\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#d6deeb\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#ffcb8b\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#c792ea\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#82aaff\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-6)\",\"terminalBackground\":\"var(--sl-color-gray-6)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-6)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#ffffff17\",\"markBorderColor\":\"#ffffff40\"}}},{\"name\":\"Night Owl Light\",\"type\":\"light\",\"colors\":{\"focusBorder\":\"#93a1a1\",\"foreground\":\"#403f53\",\"disabledForeground\":\"#61616180\",\"descriptionForeground\":\"#403f53\",\"errorForeground\":\"#403f53\",\"icon.foreground\":\"#424242\",\"contrastActiveBorder\":null,\"contrastBorder\":null,\"textBlockQuote.background\":\"#7f7f7f1a\",\"textBlockQuote.border\":\"#007acc80\",\"textCodeBlock.background\":\"#dcdcdc66\",\"textLink.activeForeground\":\"#006ab1\",\"textLink.foreground\":\"#006ab1\",\"textPreformat.foreground\":\"#a31515\",\"textSeparator.foreground\":\"#0000002e\",\"editor.background\":\"#f6f7f9\",\"editor.foreground\":\"#403f53\",\"editorLineNumber.foreground\":\"#90a7b2\",\"editorLineNumber.activeForeground\":\"#403f53\",\"editorActiveLineNumber.foreground\":\"#0b216f\",\"editor.selectionBackground\":\"#e0e0e0\",\"editor.inactiveSelectionBackground\":\"#e0e0e080\",\"editor.selectionHighlightBackground\":\"#339cec33\",\"editorError.foreground\":\"#e64d49\",\"editorWarning.foreground\":\"#daaa01\",\"editorInfo.foreground\":\"#1a85ff\",\"editorHint.foreground\":\"#6c6c6c\",\"problemsErrorIcon.foreground\":\"#e64d49\",\"problemsWarningIcon.foreground\":\"#daaa01\",\"problemsInfoIcon.foreground\":\"#1a85ff\",\"editor.findMatchBackground\":\"#93a1a16c\",\"editor.findMatchHighlightBackground\":\"#93a1a16c\",\"editor.findRangeHighlightBackground\":\"#7497a633\",\"editorLink.activeForeground\":\"#0000ff\",\"editorLightBulb.foreground\":\"#ddb100\",\"editorLightBulbAutoFix.foreground\":\"#007acc\",\"diffEditor.insertedTextBackground\":\"#9ccc2c40\",\"diffEditor.insertedTextBorder\":null,\"diffEditor.removedTextBackground\":\"#ff000033\",\"diffEditor.removedTextBorder\":null,\"diffEditor.insertedLineBackground\":\"#9bb95533\",\"diffEditor.removedLineBackground\":\"#ff000033\",\"editorStickyScroll.background\":\"#fbfbfb\",\"editorStickyScrollHover.background\":\"#f0f0f0\",\"editorInlayHint.background\":\"#2aa29899\",\"editorInlayHint.foreground\":\"#f0f0f0\",\"editorInlayHint.typeBackground\":\"#2aa29899\",\"editorInlayHint.typeForeground\":\"#f0f0f0\",\"editorInlayHint.parameterBackground\":\"#2aa29899\",\"editorInlayHint.parameterForeground\":\"#f0f0f0\",\"editorPane.background\":\"#fbfbfb\",\"editorGroup.emptyBackground\":null,\"editorGroup.focusedEmptyBorder\":null,\"editorGroupHeader.tabsBackground\":\"var(--sl-color-gray-6)\",\"editorGroupHeader.tabsBorder\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"editorGroupHeader.noTabsBackground\":\"#f0f0f0\",\"editorGroupHeader.border\":null,\"editorGroup.border\":\"#f0f0f0\",\"editorGroup.dropBackground\":\"#2677cb2d\",\"editorGroup.dropIntoPromptForeground\":\"#403f53\",\"editorGroup.dropIntoPromptBackground\":\"#f0f0f0\",\"editorGroup.dropIntoPromptBorder\":null,\"sideBySideEditor.horizontalBorder\":\"#f0f0f0\",\"sideBySideEditor.verticalBorder\":\"#f0f0f0\",\"scrollbar.shadow\":\"#cccccc\",\"scrollbarSlider.background\":\"#0000001a\",\"scrollbarSlider.hoverBackground\":\"#00000055\",\"scrollbarSlider.activeBackground\":\"#00000099\",\"panel.background\":\"#f0f0f0\",\"panel.border\":\"#d9d9d9\",\"panelTitle.activeBorder\":\"#424242\",\"panelTitle.activeForeground\":\"#424242\",\"panelTitle.inactiveForeground\":\"#424242bf\",\"panelSectionHeader.background\":\"#80808051\",\"terminal.background\":\"#f6f6f6\",\"widget.shadow\":\"#d9d9d9\",\"editorWidget.background\":\"#f0f0f0\",\"editorWidget.foreground\":\"#403f53\",\"editorWidget.border\":\"#d9d9d9\",\"quickInput.background\":\"#f0f0f0\",\"quickInput.foreground\":\"#403f53\",\"quickInputTitle.background\":\"#0000000f\",\"pickerGroup.foreground\":\"#403f53\",\"pickerGroup.border\":\"#d9d9d9\",\"editor.hoverHighlightBackground\":\"#339cec33\",\"editorHoverWidget.background\":\"#f0f0f0\",\"editorHoverWidget.foreground\":\"#403f53\",\"editorHoverWidget.border\":\"#d9d9d9\",\"editorHoverWidget.statusBarBackground\":\"#e4e4e4\",\"titleBar.activeBackground\":\"var(--sl-color-gray-6)\",\"titleBar.activeForeground\":\"var(--sl-color-text)\",\"titleBar.inactiveBackground\":\"#f0f0f099\",\"titleBar.inactiveForeground\":\"#33333399\",\"titleBar.border\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"toolbar.hoverBackground\":\"#b8b8b850\",\"toolbar.activeBackground\":\"#a6a6a650\",\"tab.activeBackground\":\"#f6f6f6\",\"tab.unfocusedActiveBackground\":\"#f6f6f6\",\"tab.inactiveBackground\":\"#f0f0f0\",\"tab.unfocusedInactiveBackground\":\"#f0f0f0\",\"tab.activeForeground\":\"var(--sl-color-text)\",\"tab.inactiveForeground\":\"#403f53\",\"tab.unfocusedActiveForeground\":\"#403f53b3\",\"tab.unfocusedInactiveForeground\":\"#403f5380\",\"tab.hoverBackground\":null,\"tab.unfocusedHoverBackground\":null,\"tab.hoverForeground\":null,\"tab.unfocusedHoverForeground\":null,\"tab.border\":\"#f0f0f0\",\"tab.lastPinnedBorder\":\"#a9a9a9\",\"tab.activeBorder\":\"transparent\",\"tab.unfocusedActiveBorder\":null,\"tab.activeBorderTop\":\"var(--sl-color-accent)\",\"tab.unfocusedActiveBorderTop\":null,\"tab.hoverBorder\":null,\"tab.unfocusedHoverBorder\":null,\"tab.activeModifiedBorder\":\"#2aa298\",\"tab.inactiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedActiveModifiedBorder\":\"#93a1a1\",\"tab.unfocusedInactiveModifiedBorder\":\"#93a1a1\",\"badge.background\":\"#2aa298\",\"badge.foreground\":\"#f0f0f0\",\"button.background\":\"#2aa298\",\"button.foreground\":\"#f0f0f0\",\"button.border\":null,\"button.separator\":\"#f0f0f066\",\"button.hoverBackground\":\"#22827a\",\"button.secondaryBackground\":\"#5f6a79\",\"button.secondaryForeground\":\"#ffffff\",\"button.secondaryHoverBackground\":\"#4c5561\",\"dropdown.background\":\"#f0f0f0\",\"dropdown.foreground\":\"#403f53\",\"dropdown.border\":\"#d9d9d9\",\"list.activeSelectionBackground\":\"#d3e8f8\",\"list.activeSelectionForeground\":\"#403f53\",\"tree.indentGuidesStroke\":\"#a9a9a9\",\"input.background\":\"#f0f0f0\",\"input.foreground\":\"#403f53\",\"input.placeholderForeground\":\"#93a1a1\",\"inputOption.activeBorder\":\"#2aa298\",\"inputOption.hoverBackground\":\"#b8b8b850\",\"inputOption.activeBackground\":\"#93a1a133\",\"inputOption.activeForeground\":\"#000000\",\"inputValidation.infoBackground\":\"#f0f0f0\",\"inputValidation.infoBorder\":\"#d0d0d0\",\"inputValidation.warningBackground\":\"#daaa01\",\"inputValidation.warningBorder\":\"#e0af02\",\"inputValidation.errorBackground\":\"#f76e6e\",\"inputValidation.errorBorder\":\"#de3d3b\",\"keybindingLabel.background\":\"#dddddd66\",\"keybindingLabel.foreground\":\"#555555\",\"keybindingLabel.border\":\"#cccccc66\",\"keybindingLabel.bottomBorder\":\"#bbbbbb66\",\"menu.foreground\":\"#403f53\",\"menu.background\":\"#f0f0f0\",\"menu.selectionForeground\":\"#403f53\",\"menu.selectionBackground\":\"#d3e8f8\",\"menu.separatorBackground\":\"#d4d4d4\",\"editor.snippetTabstopHighlightBackground\":\"#0a326433\",\"editor.snippetFinalTabstopHighlightBorder\":\"#0a326480\",\"terminal.ansiBlack\":\"#403f53\",\"terminal.ansiRed\":\"#de3d3b\",\"terminal.ansiGreen\":\"#08916a\",\"terminal.ansiYellow\":\"#e0af02\",\"terminal.ansiBlue\":\"#288ed7\",\"terminal.ansiMagenta\":\"#d6438a\",\"terminal.ansiCyan\":\"#2aa298\",\"terminal.ansiWhite\":\"#f0f0f0\",\"terminal.ansiBrightBlack\":\"#403f53\",\"terminal.ansiBrightRed\":\"#de3d3b\",\"terminal.ansiBrightGreen\":\"#08916a\",\"terminal.ansiBrightYellow\":\"#daaa01\",\"terminal.ansiBrightBlue\":\"#288ed7\",\"terminal.ansiBrightMagenta\":\"#d6438a\",\"terminal.ansiBrightCyan\":\"#2aa298\",\"terminal.ansiBrightWhite\":\"#f0f0f0\",\"selection.background\":\"#7a8181ad\",\"notifications.background\":\"#f0f0f0\",\"notifications.foreground\":\"#403f53\",\"notificationLink.foreground\":\"#994cc3\",\"notifications.border\":\"#cccccc\",\"notificationCenter.border\":\"#cccccc\",\"notificationToast.border\":\"#cccccc\",\"notificationCenterHeader.foreground\":\"#403f53\",\"notificationCenterHeader.background\":\"#f0f0f0\",\"input.border\":\"#d9d9d9\",\"progressBar.background\":\"#2aa298\",\"list.inactiveSelectionBackground\":\"#e0e7ea\",\"list.inactiveSelectionForeground\":\"#403f53\",\"list.focusBackground\":\"#d3e8f8\",\"list.hoverBackground\":\"#d3e8f8\",\"list.focusForeground\":\"#403f53\",\"list.hoverForeground\":\"#403f53\",\"list.highlightForeground\":\"#403f53\",\"list.errorForeground\":\"#e64d49\",\"list.warningForeground\":\"#daaa01\",\"activityBar.background\":\"#f0f0f0\",\"activityBar.foreground\":\"#403f53\",\"activityBar.dropBackground\":\"#d0d0d0\",\"activityBarBadge.background\":\"#403f53\",\"activityBarBadge.foreground\":\"#f0f0f0\",\"activityBar.border\":\"#f0f0f0\",\"sideBar.background\":\"#f0f0f0\",\"sideBar.foreground\":\"#403f53\",\"sideBarTitle.foreground\":\"#403f53\",\"sideBar.border\":\"#f0f0f0\",\"editorGroup.background\":\"#f6f6f6\",\"editorCursor.foreground\":\"#90a7b2\",\"editor.wordHighlightBackground\":\"#339cec33\",\"editor.wordHighlightStrongBackground\":\"#007dd659\",\"editor.lineHighlightBackground\":\"#f0f0f0\",\"editor.rangeHighlightBackground\":\"#7497a633\",\"editorWhitespace.foreground\":\"#d9d9d9\",\"editorIndentGuide.background\":\"#d9d9d9\",\"editorCodeLens.foreground\":\"#403f53\",\"editorBracketMatch.background\":\"#d3e8f8\",\"editorBracketMatch.border\":\"#2aa298\",\"editorError.border\":\"#fbfbfb\",\"editorWarning.border\":\"#daaa01\",\"editorGutter.addedBackground\":\"#49d0c5\",\"editorGutter.modifiedBackground\":\"#6fbef6\",\"editorGutter.deletedBackground\":\"#f76e6e\",\"editorRuler.foreground\":\"#d9d9d9\",\"editorOverviewRuler.errorForeground\":\"#e64d49\",\"editorOverviewRuler.warningForeground\":\"#daaa01\",\"editorSuggestWidget.background\":\"#f0f0f0\",\"editorSuggestWidget.foreground\":\"#403f53\",\"editorSuggestWidget.highlightForeground\":\"#403f53\",\"editorSuggestWidget.selectedBackground\":\"#d3e8f8\",\"editorSuggestWidget.border\":\"#d9d9d9\",\"debugExceptionWidget.background\":\"#f0f0f0\",\"debugExceptionWidget.border\":\"#d9d9d9\",\"editorMarkerNavigation.background\":\"#d0d0d0\",\"editorMarkerNavigationError.background\":\"#f76e6e\",\"editorMarkerNavigationWarning.background\":\"#daaa01\",\"debugToolBar.background\":\"#f0f0f0\",\"extensionButton.prominentBackground\":\"#2aa298\",\"extensionButton.prominentForeground\":\"#f0f0f0\",\"statusBar.background\":\"#f0f0f0\",\"statusBar.border\":\"#f0f0f0\",\"statusBar.debuggingBackground\":\"#f0f0f0\",\"statusBar.debuggingForeground\":\"#403f53\",\"statusBar.foreground\":\"#403f53\",\"statusBar.noFolderBackground\":\"#f0f0f0\",\"statusBar.noFolderForeground\":\"#403f53\",\"peekView.border\":\"#d9d9d9\",\"peekViewEditor.background\":\"#f6f6f6\",\"peekViewEditorGutter.background\":\"#f6f6f6\",\"peekViewEditor.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.background\":\"#f0f0f0\",\"peekViewResult.fileForeground\":\"#403f53\",\"peekViewResult.lineForeground\":\"#403f53\",\"peekViewResult.matchHighlightBackground\":\"#49d0c5\",\"peekViewResult.selectionBackground\":\"#e0e7ea\",\"peekViewResult.selectionForeground\":\"#403f53\",\"peekViewTitle.background\":\"#f0f0f0\",\"peekViewTitleLabel.foreground\":\"#403f53\",\"peekViewTitleDescription.foreground\":\"#403f53\",\"terminal.foreground\":\"#403f53\"},\"fg\":\"#403f53\",\"bg\":\"#f6f7f9\",\"semanticHighlighting\":false,\"settings\":[{\"name\":\"Changed\",\"scope\":[\"markup.changed\",\"meta.diff.header.git\",\"meta.diff.header.from-file\",\"meta.diff.header.to-file\"],\"settings\":{\"foreground\":\"#556484\"}},{\"name\":\"Deleted\",\"scope\":[\"markup.deleted.diff\"],\"settings\":{\"foreground\":\"#ae3c3afd\"}},{\"name\":\"Inserted\",\"scope\":[\"markup.inserted.diff\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Global settings\",\"settings\":{\"background\":\"#011627\",\"foreground\":\"#403f53\"}},{\"name\":\"Comment\",\"scope\":[\"comment\"],\"settings\":{\"foreground\":\"#5f636f\"}},{\"name\":\"String\",\"scope\":[\"string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"String Quoted\",\"scope\":[\"string.quoted\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Support Constant Math\",\"scope\":[\"support.constant.math\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Number\",\"scope\":[\"constant.numeric\",\"constant.character.numeric\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Built-in constant\",\"scope\":[\"constant.language\",\"punctuation.definition.constant\",\"variable.other.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"User-defined constant\",\"scope\":[\"constant.character\",\"constant.other\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Constant Character Escape\",\"scope\":[\"constant.character.escape\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"RegExp String\",\"scope\":[\"string.regexp\",\"string.regexp keyword.other\"],\"settings\":{\"foreground\":\"#3a688f\"}},{\"name\":\"Comma in functions\",\"scope\":[\"meta.function punctuation.separator.comma\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Variable\",\"scope\":[\"variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword\",\"scope\":[\"punctuation.accessor\",\"keyword\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage\",\"scope\":[\"storage\",\"meta.var.expr\",\"meta.class meta.method.declaration meta.var.expr storage.type.js\",\"storage.type.property.js\",\"storage.type.property.ts\",\"storage.type.property.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Storage type\",\"scope\":[\"storage.type.function.arrow.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Class name\",\"scope\":[\"entity.name.class\",\"meta.class entity.name.type.class\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Inherited class\",\"scope\":[\"entity.other.inherited-class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Function name\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Meta Tag\",\"scope\":[\"punctuation.definition.tag\",\"meta.tag\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Tag names\",\"scope\":[\"entity.name.tag\",\"meta.tag.other.html\",\"meta.tag.other.js\",\"meta.tag.other.tsx\",\"entity.name.tag.tsx\",\"entity.name.tag.js\",\"entity.name.tag\",\"meta.tag.js\",\"meta.tag.tsx\",\"meta.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Tag attribute\",\"scope\":[\"entity.other.attribute-name\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Entity Name Tag Custom\",\"scope\":[\"entity.name.tag.custom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Library (function & constant)\",\"scope\":[\"support.function\",\"support.constant\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Constant Property Value meta\",\"scope\":[\"support.constant.meta.property-value\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Library class/type\",\"scope\":[\"support.type\",\"support.class\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Support Variable DOM\",\"scope\":[\"support.variable.dom\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid\",\"scope\":[\"invalid\"],\"settings\":{\"foreground\":\"#bb2060\"}},{\"name\":\"Invalid deprecated\",\"scope\":[\"invalid.deprecated\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Keyword Operator\",\"scope\":[\"keyword.operator\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Keyword Operator Relational\",\"scope\":[\"keyword.operator.relational\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Assignment\",\"scope\":[\"keyword.operator.assignment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Arithmetic\",\"scope\":[\"keyword.operator.arithmetic\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Bitwise\",\"scope\":[\"keyword.operator.bitwise\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Increment\",\"scope\":[\"keyword.operator.increment\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Operator Ternary\",\"scope\":[\"keyword.operator.ternary\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Double-Slashed Comment\",\"scope\":[\"comment.line.double-slash\"],\"settings\":{\"foreground\":\"#5d6376\"}},{\"name\":\"Object\",\"scope\":[\"object\"],\"settings\":{\"foreground\":\"#58656a\"}},{\"name\":\"Null\",\"scope\":[\"constant.language.null\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Meta Brace\",\"scope\":[\"meta.brace\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Meta Delimiter Period\",\"scope\":[\"meta.delimiter.period\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition String\",\"scope\":[\"punctuation.definition.string\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuation Definition String Markdown\",\"scope\":[\"punctuation.definition.string.begin.markdown\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Boolean\",\"scope\":[\"constant.language.boolean\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Object Comma\",\"scope\":[\"object.comma\"],\"settings\":{\"foreground\":\"#646464\"}},{\"name\":\"Variable Parameter Function\",\"scope\":[\"variable.parameter.function\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Support Type Property Name & entity name tags\",\"scope\":[\"support.type.vendor.property-name\",\"support.constant.vendor.property-value\",\"support.type.property-name\",\"meta.property-list entity.name.tag\"],\"settings\":{\"foreground\":\"#096e72\",\"fontStyle\":\"\"}},{\"name\":\"Entity Name tag reference in stylesheets\",\"scope\":[\"meta.property-list entity.name.tag.reference\"],\"settings\":{\"foreground\":\"#286d70\"}},{\"name\":\"Constant Other Color RGB Value Punctuation Definition Constant\",\"scope\":[\"constant.other.color.rgb-value punctuation.definition.constant\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Constant Other Color\",\"scope\":[\"constant.other.color\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Keyword Other Unit\",\"scope\":[\"keyword.other.unit\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Selector\",\"scope\":[\"meta.selector\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Entity Other Attribute Name Id\",\"scope\":[\"entity.other.attribute-name.id\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Meta Property Name\",\"scope\":[\"meta.property-name\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Doctypes\",\"scope\":[\"entity.name.tag.doctype\",\"meta.tag.sgml.doctype\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Punctuation Definition Parameters\",\"scope\":[\"punctuation.definition.parameters\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Keyword Control Operator\",\"scope\":[\"keyword.control.operator\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Keyword Operator Logical\",\"scope\":[\"keyword.operator.logical\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Variable Instances\",\"scope\":[\"variable.instance\",\"variable.other.instance\",\"variable.readwrite.instance\",\"variable.other.readwrite.instance\",\"variable.other.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Property Other object property\",\"scope\":[\"variable.other.object.property\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Variable Property Other object\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"fontStyle\":\"\"}},{\"name\":\"Entity Name Function\",\"scope\":[\"entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Keyword Operator Comparison, imports, returns and Keyword Operator Ruby\",\"scope\":[\"keyword.operator.comparison\",\"keyword.control.flow.js\",\"keyword.control.flow.ts\",\"keyword.control.flow.tsx\",\"keyword.control.ruby\",\"keyword.control.module.ruby\",\"keyword.control.class.ruby\",\"keyword.control.def.ruby\",\"keyword.control.loop.js\",\"keyword.control.loop.ts\",\"keyword.control.import.js\",\"keyword.control.import.ts\",\"keyword.control.import.tsx\",\"keyword.control.from.js\",\"keyword.control.from.ts\",\"keyword.control.from.tsx\",\"keyword.operator.instanceof.js\",\"keyword.operator.expression.instanceof.ts\",\"keyword.operator.expression.instanceof.tsx\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Keyword Control Conditional\",\"scope\":[\"keyword.control.conditional.js\",\"keyword.control.conditional.ts\",\"keyword.control.switch.js\",\"keyword.control.switch.ts\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"\"}},{\"name\":\"Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords\",\"scope\":[\"support.constant\",\"keyword.other.special-method\",\"keyword.other.new\",\"keyword.other.debugger\",\"keyword.control\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Function\",\"scope\":[\"support.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Invalid Broken\",\"scope\":[\"invalid.broken\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Invalid Unimplemented\",\"scope\":[\"invalid.unimplemented\"],\"settings\":{\"foreground\":\"#486e26\"}},{\"name\":\"Invalid Illegal\",\"scope\":[\"invalid.illegal\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Language Variable\",\"scope\":[\"variable.language\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Support Variable Property\",\"scope\":[\"support.variable.property\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Variable Function\",\"scope\":[\"variable.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variable Interpolation\",\"scope\":[\"variable.interpolation\"],\"settings\":{\"foreground\":\"#a64348\"}},{\"name\":\"Meta Function Call\",\"scope\":[\"meta.function-call\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Punctuation Section Embedded\",\"scope\":[\"punctuation.section.embedded\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Punctuation Tweaks\",\"scope\":[\"punctuation.terminator.expression\",\"punctuation.definition.arguments\",\"punctuation.definition.array\",\"punctuation.section.array\",\"meta.array\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"More Punctuation Tweaks\",\"scope\":[\"punctuation.definition.list.begin\",\"punctuation.definition.list.end\",\"punctuation.separator.arguments\",\"punctuation.definition.list\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Template Strings\",\"scope\":[\"string.template meta.template.expression\"],\"settings\":{\"foreground\":\"#b23834\"}},{\"name\":\"Backticks(``) in Template Strings\",\"scope\":[\"string.template punctuation.definition.string\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Italics\",\"scope\":[\"italic\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Bold\",\"scope\":[\"bold\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Quote\",\"scope\":[\"quote\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Raw Code\",\"scope\":[\"raw\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CoffeeScript Variable Assignment\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#186e73\"}},{\"name\":\"CoffeeScript Parameter Function\",\"scope\":[\"variable.parameter.function.coffee\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"CoffeeScript Assignments\",\"scope\":[\"variable.assignment.coffee\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C# Readwrite Variables\",\"scope\":[\"variable.other.readwrite.cs\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C# Classes & Storage types\",\"scope\":[\"entity.name.type.class.cs\",\"storage.type.cs\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"C# Namespaces\",\"scope\":[\"entity.name.type.namespace.cs\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Tag names in Stylesheets\",\"scope\":[\"entity.name.tag.css\",\"entity.name.tag.less\",\"entity.name.tag.custom.css\",\"support.constant.property-value.css\"],\"settings\":{\"foreground\":\"#984e4d\",\"fontStyle\":\"\"}},{\"name\":\"Wildcard(*) selector in Stylesheets\",\"scope\":[\"entity.name.tag.wildcard.css\",\"entity.name.tag.wildcard.less\",\"entity.name.tag.wildcard.scss\",\"entity.name.tag.wildcard.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"CSS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Attribute Name for CSS\",\"scope\":[\"meta.attribute-selector.css entity.other.attribute-name.attribute\",\"variable.other.readwrite.js\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Elixir Classes\",\"scope\":[\"source.elixir support.type.elixir\",\"source.elixir meta.module.elixir entity.name.class.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Functions\",\"scope\":[\"source.elixir entity.name.function\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Constants\",\"scope\":[\"source.elixir constant.other.symbol.elixir\",\"source.elixir constant.other.keywords.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir String Punctuations\",\"scope\":[\"source.elixir punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir\",\"scope\":[\"source.elixir variable.other.readwrite.module.elixir\",\"source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Elixir Binary Punctuations\",\"scope\":[\"source.elixir .punctuation.binary.elixir\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Closure Constant Keyword\",\"scope\":[\"constant.keyword.clojure\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Function Calls\",\"scope\":[\"source.go meta.function-call.go\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Go Keywords\",\"scope\":[\"source.go keyword.package.go\",\"source.go keyword.import.go\",\"source.go keyword.function.go\",\"source.go keyword.type.go\",\"source.go keyword.struct.go\",\"source.go keyword.interface.go\",\"source.go keyword.const.go\",\"source.go keyword.var.go\",\"source.go keyword.map.go\",\"source.go keyword.channel.go\",\"source.go keyword.control.go\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"Go Constants e.g. nil, string format (%s, %d, etc.)\",\"scope\":[\"source.go constant.language.go\",\"source.go constant.other.placeholder.go\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Functions\",\"scope\":[\"entity.name.function.preprocessor.cpp\",\"entity.scope.name.cpp\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"C++ Meta Namespace\",\"scope\":[\"meta.namespace-block.cpp\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"C++ Language Primitive Storage\",\"scope\":[\"storage.type.language.primitive.cpp\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"C++ Preprocessor Macro\",\"scope\":[\"meta.preprocessor.macro.cpp\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"C++ Variable Parameter\",\"scope\":[\"variable.parameter\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Powershell Variables\",\"scope\":[\"variable.other.readwrite.powershell\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Powershell Function\",\"scope\":[\"support.function.powershell\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"ID Attribute Name in HTML\",\"scope\":[\"entity.other.attribute-name.id.html\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"HTML Punctuation Definition Tag\",\"scope\":[\"punctuation.definition.tag.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"HTML Doctype\",\"scope\":[\"meta.tag.sgml.doctype.html\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"JavaScript Classes\",\"scope\":[\"meta.class entity.name.type.class.js\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.js\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JavaScript Terminator\",\"scope\":[\"terminator.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Meta Punctuation Definition\",\"scope\":[\"meta.js punctuation.definition.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Entity Names in Code Documentations\",\"scope\":[\"entity.name.type.instance.jsdoc\",\"entity.name.type.instance.phpdoc\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"Other Variables in Code Documentations\",\"scope\":[\"variable.other.jsdoc\",\"variable.other.phpdoc\"],\"settings\":{\"foreground\":\"#3e697c\"}},{\"name\":\"JavaScript module imports and exports\",\"scope\":[\"variable.other.meta.import.js\",\"meta.import.js variable.other\",\"variable.other.meta.export.js\",\"meta.export.js variable.other\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variable Parameter Function\",\"scope\":[\"variable.parameter.function.js\"],\"settings\":{\"foreground\":\"#555ea2\"}},{\"name\":\"JavaScript[React] Variable Other Object\",\"scope\":[\"variable.other.object.js\",\"variable.other.object.jsx\",\"variable.object.property.js\",\"variable.object.property.jsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Variables\",\"scope\":[\"variable.js\",\"variable.other.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JavaScript Entity Name Type\",\"scope\":[\"entity.name.type.js\",\"entity.name.type.module.js\"],\"settings\":{\"foreground\":\"#111111\",\"fontStyle\":\"\"}},{\"name\":\"JavaScript Support Classes\",\"scope\":[\"support.class.js\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"JSON Property Names\",\"scope\":[\"support.type.property-name.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"JSON Support Constants\",\"scope\":[\"support.constant.json\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"JSON Property values (string)\",\"scope\":[\"meta.structure.dictionary.value.json string.quoted.double\"],\"settings\":{\"foreground\":\"#7c5686\"}},{\"name\":\"Strings in JSON values\",\"scope\":[\"string.quoted.double.json punctuation.definition.string.json\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Specific JSON Property values like null\",\"scope\":[\"meta.structure.dictionary.json meta.structure.dictionary.value constant.language\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"JavaScript Other Variable\",\"scope\":[\"variable.other.object.js\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Variables\",\"scope\":[\"variable.other.ruby\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Ruby Class\",\"scope\":[\"entity.name.type.class.ruby\"],\"settings\":{\"foreground\":\"#984e4d\"}},{\"name\":\"Ruby Hashkeys\",\"scope\":[\"constant.language.symbol.hashkey.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Ruby Symbols\",\"scope\":[\"constant.language.symbol.ruby\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"LESS Tag names\",\"scope\":[\"entity.name.tag.less\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"LESS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.css\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Attribute Name for LESS\",\"scope\":[\"meta.attribute-selector.less entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Markdown Headings\",\"scope\":[\"markup.heading.markdown\",\"markup.heading.setext.1.markdown\",\"markup.heading.setext.2.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Italics\",\"scope\":[\"markup.italic.markdown\"],\"settings\":{\"foreground\":\"#8844ae\",\"fontStyle\":\"italic\"}},{\"name\":\"Markdown Bold\",\"scope\":[\"markup.bold.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\",\"fontStyle\":\"bold\"}},{\"name\":\"Markdown Quote + others\",\"scope\":[\"markup.quote.markdown\"],\"settings\":{\"foreground\":\"#5c6285\"}},{\"name\":\"Markdown Raw Code + others\",\"scope\":[\"markup.inline.raw.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown Links\",\"scope\":[\"markup.underline.link.markdown\",\"markup.underline.link.image.markdown\"],\"settings\":{\"foreground\":\"#954f5a\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Link Title and Description\",\"scope\":[\"string.other.link.title.markdown\",\"string.other.link.description.markdown\"],\"settings\":{\"foreground\":\"#403f53\",\"fontStyle\":\"underline\"}},{\"name\":\"Markdown Punctuation\",\"scope\":[\"punctuation.definition.string.markdown\",\"punctuation.definition.string.begin.markdown\",\"punctuation.definition.string.end.markdown\",\"meta.link.inline.markdown punctuation.definition.string\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown MetaData Punctuation\",\"scope\":[\"punctuation.definition.metadata.markdown\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Markdown List Punctuation\",\"scope\":[\"beginning.punctuation.definition.list.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Markdown Inline Raw String\",\"scope\":[\"markup.inline.raw.string.markdown\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"PHP Variables\",\"scope\":[\"variable.other.php\",\"variable.other.property.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Support Classes in PHP\",\"scope\":[\"support.class.php\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Punctuations in PHP function calls\",\"scope\":[\"meta.function-call.php punctuation\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"PHP Global Variables\",\"scope\":[\"variable.other.global.php\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Declaration Punctuation in PHP Global Variables\",\"scope\":[\"variable.other.global.php punctuation.definition.variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Language Constants in Python\",\"scope\":[\"constant.language.python\"],\"settings\":{\"foreground\":\"#a24848\"}},{\"name\":\"Python Function Parameter and Arguments\",\"scope\":[\"variable.parameter.function.python\",\"meta.function-call.arguments.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Function Call\",\"scope\":[\"meta.function-call.python\",\"meta.function-call.generic.python\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"Punctuations in Python\",\"scope\":[\"punctuation.python\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Decorator Functions in Python\",\"scope\":[\"entity.name.function.decorator.python\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Python Language Variable\",\"scope\":[\"source.python variable.language.special\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Python import control keyword\",\"scope\":[\"keyword.control\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"SCSS Variable\",\"scope\":[\"variable.scss\",\"variable.sass\",\"variable.parameter.url.scss\",\"variable.parameter.url.sass\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"Variables in SASS At-Rules\",\"scope\":[\"source.css.scss meta.at-rule variable\",\"source.css.sass meta.at-rule variable\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"Attribute Name for SASS\",\"scope\":[\"meta.attribute-selector.scss entity.other.attribute-name.attribute\",\"meta.attribute-selector.sass entity.other.attribute-name.attribute\"],\"settings\":{\"foreground\":\"#aa0982\"}},{\"name\":\"Tag names in SASS\",\"scope\":[\"entity.name.tag.scss\",\"entity.name.tag.sass\"],\"settings\":{\"foreground\":\"#096e72\"}},{\"name\":\"SASS Keyword Other Unit\",\"scope\":[\"keyword.other.unit.scss\",\"keyword.other.unit.sass\"],\"settings\":{\"foreground\":\"#8844ae\"}},{\"name\":\"TypeScript[React] Variables and Object Properties\",\"scope\":[\"variable.other.readwrite.alias.ts\",\"variable.other.readwrite.alias.tsx\",\"variable.other.readwrite.ts\",\"variable.other.readwrite.tsx\",\"variable.other.object.ts\",\"variable.other.object.tsx\",\"variable.object.property.ts\",\"variable.object.property.tsx\",\"variable.other.ts\",\"variable.other.tsx\",\"variable.tsx\",\"variable.ts\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Entity Name Types\",\"scope\":[\"entity.name.type.ts\",\"entity.name.type.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript[React] Node Classes\",\"scope\":[\"support.class.node.ts\",\"support.class.node.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Entity Name Types as Parameters\",\"scope\":[\"meta.type.parameters.ts entity.name.type\",\"meta.type.parameters.tsx entity.name.type\"],\"settings\":{\"foreground\":\"#4d667b\"}},{\"name\":\"TypeScript[React] Import/Export Punctuations\",\"scope\":[\"meta.import.ts punctuation.definition.block\",\"meta.import.tsx punctuation.definition.block\",\"meta.export.ts punctuation.definition.block\",\"meta.export.tsx punctuation.definition.block\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.decorator punctuation.decorator.ts\",\"meta.decorator punctuation.decorator.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"TypeScript[React] Punctuation Decorators\",\"scope\":[\"meta.tag.js meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"YAML Entity Name Tags\",\"scope\":[\"entity.name.tag.yaml\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"JavaScript Variable Other ReadWrite\",\"scope\":[\"variable.other.readwrite.js\",\"variable.parameter\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"Support Class Component\",\"scope\":[\"support.class.component.js\",\"support.class.component.tsx\"],\"settings\":{\"foreground\":\"#aa0982\",\"fontStyle\":\"\"}},{\"name\":\"Text nested in React tags\",\"scope\":[\"meta.jsx.children\",\"meta.jsx.children.js\",\"meta.jsx.children.tsx\"],\"settings\":{\"foreground\":\"#403f53\"}},{\"name\":\"TypeScript Classes\",\"scope\":[\"meta.class entity.name.type.class.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Entity Name Type\",\"scope\":[\"entity.name.type.tsx\",\"entity.name.type.module.tsx\"],\"settings\":{\"foreground\":\"#111111\"}},{\"name\":\"TypeScript Class Variable Keyword\",\"scope\":[\"meta.class.ts meta.var.expr.ts storage.type.ts\",\"meta.class.tsx meta.var.expr.tsx storage.type.tsx\"],\"settings\":{\"foreground\":\"#76578b\"}},{\"name\":\"TypeScript Method Declaration e.g. `constructor`\",\"scope\":[\"meta.method.declaration storage.type.ts\",\"meta.method.declaration storage.type.tsx\"],\"settings\":{\"foreground\":\"#3b61b0\"}},{\"name\":\"normalize font style of certain components\",\"scope\":[\"meta.property-list.css meta.property-value.css variable.other.less\",\"meta.property-list.scss variable.scss\",\"meta.property-list.sass variable.sass\",\"meta.brace\",\"keyword.operator.operator\",\"keyword.operator.or.regexp\",\"keyword.operator.expression.in\",\"keyword.operator.relational\",\"keyword.operator.assignment\",\"keyword.operator.comparison\",\"keyword.operator.type\",\"keyword.operator\",\"keyword\",\"punctuation.definition.string\",\"punctuation\",\"variable.other.readwrite.js\",\"storage.type\",\"source.css\",\"string.quoted\"],\"settings\":{\"fontStyle\":\"\"}}],\"styleOverrides\":{\"frames\":{\"editorBackground\":\"var(--sl-color-gray-7)\",\"terminalBackground\":\"var(--sl-color-gray-7)\",\"editorActiveTabBackground\":\"var(--sl-color-gray-7)\",\"terminalTitlebarDotsForeground\":\"color-mix(in srgb, var(--sl-color-gray-5), transparent 25%)\",\"terminalTitlebarDotsOpacity\":\"0.75\",\"inlineButtonForeground\":\"var(--sl-color-text)\",\"frameBoxShadowCssValue\":\"none\"},\"textMarkers\":{\"markBackground\":\"#0000001a\",\"markBorderColor\":\"#00000055\"}}}],\"defaultLocale\":\"en\",\"cascadeLayer\":\"starlight.components\",\"styleOverrides\":{\"borderRadius\":\"0px\",\"borderWidth\":\"1px\",\"codePaddingBlock\":\"0.75rem\",\"codePaddingInline\":\"1rem\",\"codeFontFamily\":\"var(--__sl-font-mono)\",\"codeFontSize\":\"var(--sl-text-code)\",\"codeLineHeight\":\"var(--sl-line-height)\",\"uiFontFamily\":\"var(--__sl-font)\",\"textMarkers\":{\"lineDiffIndicatorMarginLeft\":\"0.25rem\",\"defaultChroma\":\"45\",\"backgroundOpacity\":\"60%\"}},\"plugins\":[{\"name\":\"Starlight Plugin\",\"hooks\":{}},{\"name\":\"astro-expressive-code\",\"hooks\":{}}]}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[{\"codes\":[\"en\"],\"path\":\"en\"},{\"codes\":[\"ru\"],\"path\":\"ru\"}],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","docs",["Map",11,12,25,26,37,38],"index",{"id":11,"data":13,"body":22,"filePath":23,"digest":24,"deferredRender":16},{"title":14,"description":15,"editUrl":16,"head":17,"template":18,"sidebar":19,"pagefind":16,"draft":20},"Welcome","Get started building your docs site with Starlight.",true,[],"splash",{"hidden":20,"attrs":21},false,{},"import { Card, CardGrid } from '@astrojs/starlight/components';\n\n## Next steps\n\n\u003CCardGrid stagger>\n\t\u003CCard title=\"Update content\" icon=\"pencil\">\n\t\tEdit `src/content/docs/index.mdx` to see this page change.\n\t\u003C/Card>\n\t\u003CCard title=\"Change page layout\" icon=\"document\">\n\t\tDelete `template: splash` in `src/content/docs/index.mdx` to display a\n\t\tsidebar on this page.\n\t\u003C/Card>\n\t\u003CCard title=\"Add new content\" icon=\"add-document\">\n\t\tAdd Markdown or MDX files to `src/content/docs` to create new pages.\n\t\u003C/Card>\n\t\u003CCard title=\"Configure your site\" icon=\"setting\">\n\t\tEdit your `sidebar` and other config in `astro.config.mjs`.\n\t\u003C/Card>\n\t\u003CCard title=\"Read the docs\" icon=\"open-book\">\n\t\tLearn more in [the Starlight Docs](https://starlight.astro.build/).\n\t\u003C/Card>\n\u003C/CardGrid>","src/content/docs/index.mdx","576d77b3cc95d789","get-started/overview",{"id":25,"data":27,"body":34,"filePath":35,"digest":36,"deferredRender":16},{"title":28,"editUrl":16,"head":29,"template":30,"sidebar":31,"pagefind":16,"draft":20},"Overview",[],"doc",{"order":32,"hidden":20,"attrs":33},1,{},"import { FileTree, LinkCard } from '@astrojs/starlight/components';\n\n**Feature-Sliced Design** (FSD) is an architectural methodology for scaffolding front-end applications. Simply put, it's a compilation of rules and conventions on organizing code. The main purpose of this methodology is to make the project more understandable and stable in the face of ever-changing business requirements.\n\nApart from a set of conventions, FSD is also a toolchain. We have a [linter][ext-steiger] to check your project's architecture, [folder generators][ext-tools] through a CLI or IDEs, as well as a rich library of [examples][examples].\n\n## Is it right for me? \\{#is-it-right-for-me\\}\n\nFSD can be implemented in projects and teams of any size. It is right for your project if:\n\n- You're doing **frontend** (UI on web, mobile, desktop, etc.)\n- You're building an **application**, not a library\n\nAnd that's it! There are no restrictions on what programming language, UI framework, or state manager you use. You can also adopt FSD incrementally, use it in monorepos, and scale to great lengths by breaking your app into packages and implementing FSD individually within them.\n\nIf you already have an architecture and you're considering a switch to FSD, make sure that the current architecture is **causing trouble** in your team. For example, if your project has grown too large and inter-connected to efficiently implement new features, or if you're expecting a lot of new members to join the team. If the current architecture works, maybe it's not worth changing. But if you do decide to migrate, see the [Migration][migration] section for guidance.\n\n## Basic example \\{#basic-example\\}\n\nHere is a simple project that implements FSD:\n\n\u003CFileTree>\n\n- app/\n- pages/\n- shared/\n\n\u003C/FileTree>\n\nThese top-level folders are called _layers_. Let's look deeper:\n\n\u003CFileTree>\n\n- app/\n - routes/\n - analytics/\n- pages/\n - home/\n - article-reader/\n - ui/\n - api/\n - setings/\n- shared/\n - ui/\n - api/\n\n\u003C/FileTree>\n\nFolders inside `📂 pages` are called _slices_. They divide the layer by domain (in this case, by pages). \n\nFolders inside `📂 app`, `📂 shared`, and `📂 pages/article-reader` are called _segments_, and they divide slices (or layers) by technical purpose, i.e. what the code is for.\n\n## Concepts \\{#concepts\\}\n\nLayers, slices, and segments form a hierarchy like this:\n\n\u003Cfigure>\n ![Hierarchy of FSD concepts, described below](../../../../static/img/visual_schema.jpg)\n\n \u003Cfigcaption style={{ fontStyle: \"italic\", fontSize: \"0.9em\" }}>\n \u003Cp>Pictured above: three pillars, labeled left to right as \"Layers\", \"Slices\", and \"Segments\" respectively.\u003C/p>\n \u003Cp>The \"Layers\" pillar contains seven divisions arranged top to bottom and labeled \"app\", \"processes\", \"pages\", \"widgets\", \"features\", \"entities\", and \"shared\". The \"processes\" division is crossed out. The \"entities\" division is connected to the second pillar \"Slices\" in a way that conveys that the second pillar is the content of \"entities\".\u003C/p>\n \u003Cp>The \"Slices\" pillar contains three divisions arranged top to bottom and labeled \"user\", \"post\", and \"comment\". The \"post\" division is connected to the third pillar \"Segments\" in the same way such that it's the content of \"post\".\u003C/p>\n \u003Cp>The \"Segments\" pillar contains three divisions, arranged top to bottom and labeled \"ui\", \"model\", and \"api\".\u003C/p>\n \u003C/figcaption>\n\u003C/figure>\n\n### Layers \\{#layers\\}\n\nLayers are standardized across all FSD projects. You don't have to use all of the layers, but their names are important. There are currently seven of them (from top to bottom):\n\n1. **App** — everything that makes the app run — routing, entrypoints, global styles, providers.\n2. **Processes** (deprecated) — complex inter-page scenarios.\n3. **Pages** — full pages or large parts of a page in nested routing.\n4. **Widgets** — large self-contained chunks of functionality or UI, usually delivering an entire use case.\n5. **Features** — _reused_ implementations of entire product features, i.e. actions that bring business value to the user.\n6. **Entities** — business entities that the project works with, like `user` or `product`.\n7. **Shared** — reusable functionality, especially when it's detached from the specifics of the project/business, though not necessarily.\n\n:::warning\n\nLayers **App** and **Shared**, unlike other layers, do not have slices and are divided into segments directly.\n\nHowever, all other layers — **Entities**, **Features**, **Widgets**, and **Pages**, retain the structure in which you must first create slices, inside which you create the segments.\n\n:::\nThe trick with layers is that modules on one layer can only know about and import from modules from the layers strictly below.\n\n### Slices \\{#slices\\}\n\nNext up are slices, which partition the code by business domain. You're free to choose any names for them, and create as many as you wish. Slices make your codebase easier to navigate by keeping logically related modules close together.\n\nSlices cannot use other slices on the same layer, and that helps with high cohesion and low coupling.\n\n### Segments \\{#segments\\}\n\nSlices, as well as layers App and Shared, consist of segments, and segments group your code by its purpose. Segment names are not constrained by the standard, but there are several conventional names for the most common purposes:\n\n- `ui` — everything related to UI display: UI components, date formatters, styles, etc.\n- `api` — backend interactions: request functions, data types, mappers, etc.\n- `model` — the data model: schemas, interfaces, stores, and business logic.\n- `lib` — library code that other modules on this slice need.\n- `config` — configuration files and feature flags.\n\nUsually these segments are enough for most layers, you would only create your own segments in Shared or App, but this is not a rule.\n\n## Advantages \\{#advantages\\}\n\n- **Uniformity** \n Since the structure is standardized, projects become more uniform, which makes onboarding new members easier for the team.\n\n- **Stability in face of changes and refactoring** \n A module on one layer cannot use other modules on the same layer, or the layers above. \n This allows you to make isolated modifications without unforeseen consequences to the rest of the app.\n\n- **Controlled reuse of logic** \n Depending on the layer, you can make code very reusable or very local. \n This keeps a balance between following the **DRY** principle and practicality. \n\n- **Orientation to business and users needs** \n The app is split into business domains and usage of the business language is encouraged in naming, so that you can do useful product work without fully understanding all other unrelated parts of the project.\n\n## Incremental adoption \\{#incremental-adoption\\}\n\nIf you have an existing codebase that you want to migrate to FSD, we suggest the following strategy. We found it useful in our own migration experience.\n\n1. Start by slowly shaping up the App and Shared layers module-by-module to create a foundation.\n\n2. Distribute all of the existing UI across Widgets and Pages using broad strokes, even if they have dependencies that violate the rules of FSD.\n\n3. Start gradually resolving import violations and also extracting Entities and possibly even Features.\n\nIt's advised to refrain from adding new large entities while refactoring or refactoring only certain parts of the project.\n\n## Next steps \\{#next-steps\\}\n\n\u003CLinkCard\n title=\"Want to get a good grasp of how to think in FSD?\"\n href=\"/get-started/tutorial\"\n description=\"Check out the Tutorial\"\n/>\n\n\u003CLinkCard\n title=\"Prefer to learn from examples?\"\n href=\"/examples\"\n description=\"We have a lot in the Examples section\"\n/>\n\n\u003CLinkCard\n title=\"Have questions?\"\n href=\"https://t.me/feature_sliced\"\n description=\"Drop by our Telegram chat and get help from the community\"\n/>\n\n[tutorial]: /docs/get-started/tutorial\n[examples]: /examples\n[migration]: /docs/guides/migration/from-custom\n[ext-steiger]: https://github.com/feature-sliced/steiger\n[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools\n[ext-telegram]: https://t.me/feature_sliced","src/content/docs/get-started/overview.mdx","8c2d79b4ffec811f","get-started/faq",{"id":37,"data":39,"body":45,"filePath":46,"digest":47,"deferredRender":16},{"title":40,"editUrl":16,"head":41,"template":30,"sidebar":42,"pagefind":16,"draft":20},"FAQ",[],{"order":43,"hidden":20,"attrs":44},20,{},"import { Aside } from '@astrojs/starlight/components';\n\n\u003CAside>\nYou can ask your question in our [Telegram chat][telegram], [Discord community][discord], and [GitHub Discussions][github-discussions].\n\u003C/Aside>\n\n### Is there a toolkit or a linter?\n\nYes! We have a linter called [Steiger][ext-steiger] to check your project's architecture and [folder generators][ext-tools] through a CLI or IDEs.\n\n### Where to store the layout/template of pages?\n\nIf you need plain markup layouts, you can keep them in `shared/ui`. If you need to use higher layers inside, there are a few options:\n\n- Perhaps you don't need layouts at all? If the layout is only a few lines, it might be reasonable to duplicate the code in each page rather than try to abstract it.\n- If you do need layouts, you can have them as separate widgets or pages, and compose them in your router configuration in App. Nested routing is another option.\n\n### What is the difference between a feature and an entity?\n\nAn _entity_ is a real-life concept that your app is working with. A _feature_ is an interaction that provides real-life value to your app’s users, the thing people want to do with your entities.\n\nFor more information, along with examples, see the Reference page on [slices][reference-entities].\n\n### Can I embed pages/features/entities into each other?\n\nYes, but this embedding should happen in higher layers. For example, inside a widget, you can import both features and then insert one feature into another as props/children.\n\nYou cannot import one feature from another feature, this is prohibited by the [**import rule on layers**][import-rule-layers].\n\n### What about Atomic Design?\n\nThe current version of the methodology does not require nor prohibit the use of Atomic Design together with Feature-Sliced Design.\n\nFor example, Atomic Design [can be applied well](https://t.me/feature_sliced/1653) for the `ui` segment of modules.\n\n### Are there any useful resources/articles/etc. about FSD?\n\nYes! https://github.com/feature-sliced/awesome\n\n### Why do I need Feature-Sliced Design?\n\nIt helps you and your team to quickly overview the project in terms of its main value-bringing components. A standardized architecture helps to speed up onboarding and resolves debates about code structure. See the [motivation][motivation] page to learn more about why FSD was created.\n\n### Does a novice developer need an architecture/methodology?\n\nRather yes than no\n\n*Usually, when you design and develop a project in one person, everything goes smoothly. But if there are pauses in development, new developers are added to the team - then problems come*\n\n### How do I work with the authorization context?\n\nAnswered [here](/docs/guides/examples/auth)\n\n[ext-steiger]: https://github.com/feature-sliced/steiger\n[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools\n[import-rule-layers]: /docs/reference/layers#import-rule-on-layers\n[reference-entities]: /docs/reference/layers#entities\n[motivation]: /docs/about/motivation\n[telegram]: https://t.me/feature_sliced\n[discord]: https://discord.gg/S8MzWTUsmp\n[github-discussions]: https://github.com/feature-sliced/documentation/discussions","src/content/docs/get-started/faq.mdx","75c7081b5c5388dc"] \ No newline at end of file diff --git a/.astro/settings.json b/.astro/settings.json deleted file mode 100644 index c085660bc8..0000000000 --- a/.astro/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "_variables": { - "lastUpdateCheck": 1769728146579 - } -} \ No newline at end of file diff --git a/.astro/types.d.ts b/.astro/types.d.ts deleted file mode 100644 index 03d7cc43f1..0000000000 --- a/.astro/types.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0efb134844..996ece3364 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # IDE .idea +# Astro +.astro + # Dependencies node_modules From 708e3b96d4cb6e40f1427d29b84a22b0b09636d3 Mon Sep 17 00:00:00 2001 From: choisuhyeok1255 <72919631+choisuhyeok1255@users.noreply.github.com> Date: Fri, 30 Jan 2026 04:05:15 +0900 Subject: [PATCH 08/45] docs(kr): fix title mismatch in Next.js guide (#896) - Change title from "Usage with Electron" to "Usage with Next.js" - Ensures title matches file name, URL path, and content --- .../current/guides/tech/with-electron.mdx | 2 +- .../current/guides/tech/with-nextjs.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-electron.mdx b/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-electron.mdx index 622c977d21..a02d5797c9 100644 --- a/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-electron.mdx +++ b/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-electron.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 10 --- -# Usage with Next.js +# Usage with Electron Electron 애플리케이션은 역할이 다른 여러 **프로세스**(Main, Renderer, Preload)로 구성됩니다. 따라서 FSD를 적용하려면 Electron 특성에 맞게 구조를 조정해야 합니다. diff --git a/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-nextjs.mdx b/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-nextjs.mdx index 7daf5d54e3..5ae2f831ea 100644 --- a/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-nextjs.mdx +++ b/i18n/kr/docusaurus-plugin-content-docs/current/guides/tech/with-nextjs.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 10 --- -# Usage with Electron +# Usage with Next.js NextJS 프로젝트에도 FSD 아키텍처를 적용할 수 있지만, 구조적 차이로 두 가지 충돌이 발생합니다. From 91626ecdfc1c47ad2bbd5e2de1d6e157050a8585 Mon Sep 17 00:00:00 2001 From: Minsu Date: Tue, 3 Feb 2026 21:47:54 +0900 Subject: [PATCH 09/45] docs: code smells/cross-imports add (#888) * docs: code smells/cross-imports add * fix: Title typo correction * docs: reflecting feedback * docs: reflecting feedback * Update i18n/en/docusaurus-plugin-content-docs/current/guides/issues/cross-imports.mdx Co-authored-by: Solant --------- Co-authored-by: Solant --- .../current/guides/issues/cross-imports.mdx | 257 +++++++++++++++++- 1 file changed, 251 insertions(+), 6 deletions(-) diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/cross-imports.mdx b/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/cross-imports.mdx index 1e29ae65fd..b6dfa6eaec 100644 --- a/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/cross-imports.mdx +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/cross-imports.mdx @@ -1,18 +1,263 @@ --- sidebar_position: 4 -sidebar_class_name: sidebar-item--wip pagination_next: reference/layers --- -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' -# Cross-imports +# Cross-import - +A **cross-import** is an import **between different slices within the same layer**. -> Cross-imports appear when the layer or abstraction begins to take too much responsibility than it should. That is why the methodology identifies new layers that allow you to uncouple these cross-imports +For example: +- importing `features/product` from `features/cart` +- importing `widgets/sidebar` from `widgets/header` -## See also +Cross-imports are a code smell: a warning sign that slices are becoming coupled. In some situations they may be hard to avoid, but they should always be deliberate and either documented or shared within the team/project. + +:::note +The `shared` and `app` layers do not have the concept of a slice, so imports *within* those layers are **not** considered cross-imports. +::: + +## Why is this a code smell? + +Cross-imports are not just a matter of style—they are generally considered a **code smell** because they blur the boundaries between domains and introduce implicit dependencies. + +Consider a case where the `cart` slice directly depends on `product` business logic. At first glance, this might seem convenient. However, this creates several problems: + +1. **Unclear ownership and responsibility.** When `cart` imports from `product`, it becomes unclear which slice "owns" the shared logic. If the `product` team changes their internal implementation, they might unknowingly break `cart`. This ambiguity makes it harder to reason about the codebase and assign responsibility for bugs or features. + +2. **Reduced isolation and testability.** One of the main benefits of sliced architecture is that each slice can be developed, tested, and deployed independently. Cross-imports break this isolation—testing `cart` now requires setting up `product` as well, and changes in one slice can cause unexpected test failures in another. + +3. **Increased cognitive load.** Working on `cart` also requires accounting for how `product` is structured and how it behaves. As cross-imports accumulate, tracing the impact of a change requires following more code across slice boundaries, and even small edits demand more context to be held in mind. + +4. **Path to circular dependencies.** Cross-imports often start as one-way dependencies but can evolve into bidirectional ones (A imports B, B imports A). This tends to lock slices together, making dependencies harder to untangle and increasing refactoring cost over time. + +The purpose of clear domain boundaries is to keep each slice focused and changeable within its own responsibility. When dependencies are loose, it becomes easier to predict the impact of a change and to keep review and testing scope contained. Cross-imports weaken this separation, expanding the impact of changes and increasing refactoring cost over time—this is why they are treated as a code smell worth addressing. + +In the sections below, we outline how these issues typically appear in real projects and what strategies you can use to address them. + +## Entities layer cross-imports + +Cross-imports in `entities` are often caused by splitting entities too granularly. Before reaching for `@x`, consider whether the boundaries should be merged instead. +Some teams use `@x` as a dedicated cross-import surface for `entities`, but it should be treated as a **last resort** — a **necessary compromise**, not a recommended approach. + +Think of `@x` as an explicit gateway for unavoidable domain references—not a general-purpose reuse mechanism. Overuse tends to lock entity boundaries together and makes refactoring more costly over time. + +For details about `@x`, see the [Public API documentation](/docs/reference/public-api). + +For concrete examples of cross-references between business entities, see: +- [Types guide — Business entities and their cross-references](/docs/guides/examples/types#business-entities-and-their-cross-references) +- [Layers reference — Entities](/docs/reference/layers#entities) + +## Features and widgets: Multiple strategies + +In the `features` and `widgets` layers, it's usually more realistic to say there are **multiple strategies** for handling cross-imports, rather than declaring them **always forbidden**. This section focuses less on code and more on the **patterns** you can choose from depending on your team and product context. + +### Strategy A: Slice merge + +If two slices are not truly independent and they are always changed together, merge them into a single larger slice. + +Example (before): +- `features/profile` +- `features/profileSettings` + +If these keep cross-importing each other and effectively move as one unit, they are likely one feature in practice. In that case, merging into `features/profile` is often the simpler and cleaner choice. + +### Strategy B: Push shared domain flows down into `entities` (domain-only) + +If multiple features share a domain-level flow, move that flow into a domain slice inside `entities` (for example, `entities/session`). + +Key principles: +- `entities` contains **domain types and domain logic only** +- UI remains in `features` / `widgets` +- features import and use the domain logic from `entities` + +For example, if both `features/auth` and `features/profile` need session validation, place session-related domain functions in `entities/session` and reuse them from both features. + +For more guidance, see [Layers reference — Entities](/docs/reference/layers#entities). + +### Strategy C: Compose from an upper layer (pages / app) + +Instead of connecting slices within the same layer via cross-imports, compose them at a higher level (`pages` / `app`). This approach uses **Inversion of Control (IoC)** patterns—rather than slices knowing about each other, an upper layer assembles and connects them. + +Common IoC techniques include: +- **Render props (React)**: Pass components or render functions as props +- **Slots (Vue)**: Use named slots to inject content from parent components +- **Dependency injection**: Pass dependencies through props or context + +#### Basic composition example (React): + +```tsx title="features/userProfile/index.ts" +export { UserProfilePanel } from './ui/UserProfilePanel'; +``` + +```tsx title="features/activityFeed/index.ts" +export { ActivityFeed } from './ui/ActivityFeed'; +``` + +```tsx title="pages/UserDashboardPage.tsx" +import React from 'react'; +import { UserProfilePanel } from '@/features/userProfile'; +import { ActivityFeed } from '@/features/activityFeed'; + +export function UserDashboardPage() { + return ( +
+ + +
+ ); +} +``` + +With this structure, `features/userProfile` and `features/activityFeed` do not know about each other. `pages/UserDashboardPage` composes them to build the full screen. + +#### Render props example (React): + +When one feature needs to render content from another, use render props to invert the dependency: + +```tsx title="features/commentList/ui/CommentList.tsx" +interface CommentListProps { + comments: Comment[]; + renderUserAvatar?: (userId: string) => React.ReactNode; +} + +export function CommentList({ comments, renderUserAvatar }: CommentListProps) { + return ( +
    + {comments.map(comment => ( +
  • + {renderUserAvatar?.(comment.userId)} + {comment.text} +
  • + ))} +
+ ); +} +``` + +```tsx title="pages/PostPage.tsx" +import { CommentList } from '@/features/commentList'; +import { UserAvatar } from '@/features/userProfile'; + +export function PostPage() { + return ( + } + /> + ); +} +``` + +Now `CommentList` doesn't import from `userProfile`—the page injects the avatar component. + +#### Slots example (Vue): + +Vue's slot system provides a natural way to compose features without cross-imports: + +```vue title="features/commentList/ui/CommentList.vue" + + + +``` + +```vue title="pages/PostPage.vue" + + + +``` + +The `CommentList` feature remains independent of `userProfile`. The page uses slots to compose them together. + +### Strategy D: Cross-feature reuse only via Public API + +If the above strategies don't fit your case and cross-feature reuse is truly unavoidable, allow it only through an explicit **Public API** (for example: exported hooks or UI components). Avoid directly accessing another slice's `store`/`model` or internal implementation details. + +Unlike strategies A-C which aim to eliminate cross-imports, this strategy accepts them while minimizing the risks through strict boundaries. + +#### Example code: + +```tsx title="features/auth/index.ts" + +export { useAuth } from './model/useAuth'; +export { AuthButton } from './ui/AuthButton'; +``` + +```tsx title="features/profile/ui/ProfileMenu.tsx" + +import React from 'react'; +import { useAuth, AuthButton } from '@/features/auth'; + +export function ProfileMenu() { + const { user } = useAuth(); + + if (!user) { + return ; + } + + return
{user.name}
; +} +``` + +For example, prevent `features/profile` from importing from paths like `features/auth/model/internal/*`. Restrict usage to only what `features/auth` explicitly exposes as its Public API. + +## When should cross-imports be treated as a problem? + +After reviewing these strategies, a natural question is: + +> When is a cross-import acceptable to keep, and when should it be treated as a code smell and refactored? + +Common warning signs: +- directly depending on another slice's store/model/business logic +- deep imports into another slice's internal files +- **bidirectional dependencies** (A imports B, and B imports A) +- changes in one slice frequently breaking another slice +- flows that should be composed in `pages` / `app`, but are forced into cross-imports within the same layer + +When you see these signals, treat the cross-import as a **code smell** and consider applying at least one of the strategies above. + +## How strict you are is a team/project decision + +How strictly to enforce these rules depends on the team and project. + +For example: +- In **early-stage products** with heavy experimentation, allowing some cross-imports may be a pragmatic speed trade-off. +- In **long-lived or regulated systems** (for example, fintech or large-scale services), stricter boundaries often pay off in maintainability and stability. + +Cross-imports are not an absolute prohibition here. They are dependencies that are **generally best avoided**, but sometimes used intentionally. + +If you do introduce a cross-import: +- treat it as a deliberate architectural choice +- document the reasoning +- revisit it periodically as the system evolves + +Teams should align on: +- what strictness level they want +- how to reflect it in lint rules, code review, and documentation +- when and how to reevaluate existing cross-imports over time + +## References - [(Thread) About the supposed inevitability of cross-ports](https://t.me/feature_sliced/4515) - [(Thread) About resolving cross-ports in entities](https://t.me/feature_sliced/3678) From 0d0362fb1b3f8f70bbd1cb5d4a8dfff8a91799fb Mon Sep 17 00:00:00 2001 From: Solant Date: Tue, 3 Feb 2026 14:06:10 +0100 Subject: [PATCH 10/45] docs: refactor desegmentation page (#895) * docs: refactor desegmentation page * docs: remove wip class * docs: remove outdated translations --- .../current/guides/issues/desegmented.mdx | 205 +++++++++++------- .../current/guides/issues/desegmented.mdx | 96 -------- .../current/guides/issues/desegmented.mdx | 97 --------- .../current/guides/issues/desegmented.mdx | 96 -------- .../current/guides/issues/desegmented.mdx | 97 --------- .../current/guides/issues/desegmented.mdx | 97 --------- 6 files changed, 124 insertions(+), 564 deletions(-) delete mode 100644 i18n/ja/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx delete mode 100644 i18n/kr/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx delete mode 100644 i18n/ru/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx delete mode 100644 i18n/vi/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx delete mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx b/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx index 1ba2b6e2e9..ed1de2d012 100644 --- a/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx @@ -1,97 +1,140 @@ --- sidebar_position: 2 -sidebar_class_name: sidebar-item--wip --- -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' - -# Desegemented - - - -## Situation - -Very often, there is a situation on projects when modules related to a specific domain from the subject area are unnecessarily desegmented and scattered around the project - -```sh -├── components/ -| ├── DeliveryCard -| ├── DeliveryChoice -| ├── RegionSelect -| ├── UserAvatar -├── actions/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── epics/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── constants/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── helpers/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── entities/ -| ├── delivery/ -| | ├── getters.js -| | ├── selectors.js -| ├── region/ -| ├── user/ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Desegmentation + +Desegmentation (also known as horizontal slicing or packaging by layer) is a code organization pattern where files are grouped by their technical roles rather than by the business domains they serve. This means code with similar technical functions is stored in the same place, regardless of the business logic it handles. + +This approach is popular in meta-frameworks like Next and Nuxt due to its simplicity, as it's easy to get started and enables features like auto-imports and file-based routing: + +- 📂 app + - 📂 components + - 📄 DeliveryCard.jsx + - 📄 DeliveryChoice.jsx + - 📄 RegionSelect.jsx + - 📄 UserAvatar.jsx + - 📂 actions + - 📄 delivery.js + - 📄 region.js + - 📄 user.js + - 📂 composables + - 📄 delivery.js + - 📄 region.js + - 📄 user.js + - 📂 constants + - 📄 delivery.js + - 📄 region.js + - 📄 user.js + - 📂 utils + - 📄 delivery.js + - 📄 region.js + - 📄 user.js + - 📂 stores + - 📂 delivery + - 📄 getters.js + - 📄 actions.js + +This pattern also occurs in FSD codebases, in the form of generic folders: + +- 📂 features + - 📂 delivery + - 📂 ui + - 📂 components ⚠️ +- 📂 entities + - 📂 recommendations + - 📂 utils ⚠️ + +Files can also be a source of desegmentation. Files like `types.ts` can aggregate multiple domains, complicating navigation and future refactoring, especially in layers like `pages` or `widgets`: + +- 📂 pages + - 📂 delivery + - 📄 index.ts + - 📂 ui + - 📄 DeliveryCard.tsx + - 📄 DeliveryChoice.tsx + - 📄 UserAvatar.tsx + - 📂 model + - 📄 types.ts ⚠️ + - 📄 utils.ts ⚠️ + - 📂 api + - 📄 endpoints.ts ⚠️ + + + + +```ts title="pages/delivery/model/types.ts" +// ❌ Bad: Mixed business domains in generic file +export interface DeliveryOption { + id: string; + name: string; + price: number; +} + +export interface UserInfo { + id: string; + name: string; + avatar: string; +} ``` + + + +```ts title="pages/delivery/model/utils.ts" +// ❌ Bad: Mixed business domains in generic file +export function formatDeliveryPrice(price: number) { + return `$${price.toFixed(2)}`; +} + +export function getUserInitials(name: string) { + return name.split(' ').map(n => n[0]).join(''); +} +``` + + + +```ts title="pages/delivery/api/endpoints.ts" +// ❌ Bad: Mixed business domains in generic file +export async function fetchDeliveryOptions() { /* ... */ } +export async function fetchUserInfo() { /* ... */ } +``` + + + +## The Problem -## Problem +While this structure is easy to start with, it can lead to scalability issues in larger projects: -The problem manifests itself at least in violation of the principle of * * High Cohesion** and excessive stretching * * of the axis of changes** +- Low Cohesion: Modifying a single feature often requires editing files in multiple large folders, such as `pages`, `components`, and `stores`. -## If you ignore it +- Tight Coupling: Components can have unexpected dependencies, leading to complex and tangled dependency chains. -- If necessary, touch on the logic, for example, delivery - we will have to keep in mind that it lies in several places and touch on several places in the code-which unnecessarily stretches our * * Axis of changes** -- If we need to study the logic of the user, we will have to go through the whole project to study in detail * * actions, epics, constants, entities, components** - instead of it lying in one place -- Implicit connections and the uncontrollability of a growing subject area -- With this approach, the eye is very often blurred and you may not notice how we "create constants for the sake of constants", creating a dump in the corresponding project directory +- Difficult Refactoring: It requires additional effort to manually extract code related to a specific domain. ## Solution -Place all modules related to a specific domain/user case - directly next to each other - -So that when studying a particular module, all its components lie side by side, and are not scattered around the project - -> It also increases the discoverability and clarity of the code base and the relationships between modules - -```diff -- ├── components/ -- | ├── DeliveryCard -- | ├── DeliveryChoice -- | ├── RegionSelect -- | ├── UserAvatar -- ├── actions/ -- | ├── delivery.js -- | ├── region.js -- | ├── user.js -- ├── epics/{...} -- ├── constants/{...} -- ├── helpers/{...} - ├── entities/ - | ├── delivery/ -+ | | ├── ui/ # ~ components/ -+ | | | ├── card.js -+ | | | ├── choice.js -+ | | ├── model/ -+ | | | ├── actions.js -+ | | | ├── constants.js -+ | | | ├── epics.js -+ | | | ├── getters.js -+ | | | ├── selectors.js -+ | | ├── lib/ # ~ helpers - | ├── region/ - | ├── user/ -``` +Group all code that relates to a specific domain in one place. + +Avoid generic folder names such as `types`, `components`, `utils`, as well as generic file names like `types.ts`, `utils.ts`, or `helpers.ts`. Instead, use names that directly reflect the domain they represent. + +Avoid generic file names like `types.ts` if possible, especially in slices with multiple domains: + +- 📂 pages + - 📂 delivery + - 📄 index.tsx + - 📂 ui + - 📄 DeliveryPage.tsx + - 📄 DeliveryCard.tsx + - 📄 DeliveryChoice.tsx + - 📄 UserInfo.tsx + - 📂 model + - 📄 delivery.ts + - 📄 user.ts -## See also +## See Also * [(Article) About Low Coupling and High Cohesion clearly](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) * [(Article) Low Coupling and High Cohesion. The Law of Demeter](https://medium.com/german-gorelkin/low-coupling-high-cohesion-d36369fb1be9) diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx b/i18n/ja/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx deleted file mode 100644 index 28f3bddc42..0000000000 --- a/i18n/ja/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -sidebar_position: 2 -sidebar_class_name: sidebar-item--wip ---- - -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' - -# デセグメンテーション - - - -## 状況 {#situation} - -プロジェクトでは、特定のドメインに関連するモジュールが過度にデセグメント化され、プロジェクト全体に散らばっていることがよくあります。 - -```sh -├── components/ -| ├── DeliveryCard -| ├── DeliveryChoice -| ├── RegionSelect -| ├── UserAvatar -├── actions/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── epics/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── constants/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── helpers/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── entities/ -| ├── delivery/ -| | ├── getters.js -| | ├── selectors.js -| ├── region/ -| ├── user/ -``` - - -## 問題 {#problem} - -問題は、**高い凝集性**の原則の違反と、**変更の軸**の過度な拡張として現れます。 - -## 無視する場合 {#if-you-ignore-it} - -- 例えば、配達に関するロジックに触れる必要がある場合、このロジックが複数の箇所に分散していることを考慮しなければならず、コード内で複数の箇所に触れる必要がある。これにより、**変更の軸**が過度に引き伸ばされる -- ユーザーに関するロジックを調べる必要がある場合、**actions、epics、constants、entities、components**の詳細を調べるためにプロジェクト全体を巡回しなければならない -- 暗黙関係と拡大するドメインの制御不能 - - このアプローチでは、視野が狭くなり、「定数のための定数」を作成し、プロジェクトの該当ディレクトリをごちゃごちゃさせてしまうことに気づかないことがよくある - -## 解決策 {#solution} - -特定のドメイン/ユースケースに関連するすべてのモジュールを近くに配置することです。 - -これは特定のモジュールを調べる際に、そのすべての構成要素がプロジェクト全体に散らばらず、近くに配置されるためです。 - -> これにより、コードベースとモジュール間の関係の発見しやすさと明確さが向上します。 - -```diff -- ├── components/ -- | ├── DeliveryCard -- | ├── DeliveryChoice -- | ├── RegionSelect -- | ├── UserAvatar -- ├── actions/ -- | ├── delivery.js -- | ├── region.js -- | ├── user.js -- ├── epics/{...} -- ├── constants/{...} -- ├── helpers/{...} - ├── entities/ - | ├── delivery/ -+ | | ├── ui/ # ~ components/ -+ | | | ├── card.js -+ | | | ├── choice.js -+ | | ├── model/ -+ | | | ├── actions.js -+ | | | ├── constants.js -+ | | | ├── epics.js -+ | | | ├── getters.js -+ | | | ├── selectors.js -+ | | ├── lib/ # ~ helpers - | ├── region/ - | ├── user/ -``` - -## 参照 {#see-also} -* [(記事) Cohesion and Coupling: the difference](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) diff --git a/i18n/kr/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx b/i18n/kr/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx deleted file mode 100644 index e0cc2da21f..0000000000 --- a/i18n/kr/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -sidebar_position: 2 -sidebar_class_name: sidebar-item--wip ---- - -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' - -# Desegmentation - - - -## 상황 - -프로젝트에서 동일한 도메인의 모듈들이 서로 연관되어 있음에도 불구하고, 프로젝트 전체에 불필요하게 분산되어 있는 경우가 많습니다. - -```sh -├── components/ -| ├── DeliveryCard -| ├── DeliveryChoice -| ├── RegionSelect -| ├── UserAvatar -├── actions/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── epics/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── constants/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── helpers/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── entities/ -| ├── delivery/ -| | ├── getters.js -| | ├── selectors.js -| ├── region/ -| ├── user/ -``` - -## 문제점 - -이는 높은 응집도 원칙을 위반하며, **Changes Axis의 과도한 확장**을 초래합니다. - -## 무시했을 때의 결과 - -- delivery 관련 로직 수정 시 여러 위치의 코드를 찾아 수정해야 하며, 이는 **Changes Axis를 불필요하게 확장**합니다 -- user 관련 로직을 이해하려면 프로젝트 전반의 **actions, epics, constants, entities, components**를 모두 찾아봐야 합니다 -- 암묵적 연결로 인해 도메인 영역이 비대해지고 관리가 어려워집니다 -- 불필요한 파일들이 쌓여 문제 인식이 어려워집니다 - -## 해결 방안 - -도메인이나 use case와 관련된 모듈들을 한 곳에 모아 배치합니다. - -이를 통해 모듈 학습이나 수정 시 필요한 모든 요소를 쉽게 찾을 수 있습니다. - -> 이 접근은 코드베이스의 탐색성과 가독성을 높이고, 모듈 간 관계를 더 명확하게 보여줍니다. - -```diff -- ├── components/ -- | ├── DeliveryCard -- | ├── DeliveryChoice -- | ├── RegionSelect -- | ├── UserAvatar -- ├── actions/ -- | ├── delivery.js -- | ├── region.js -- | ├── user.js -- ├── epics/{...} -- ├── constants/{...} -- ├── helpers/{...} - ├── entities/ - | ├── delivery/ -+ | | ├── ui/ # ~ components/ -+ | | | ├── card.js -+ | | | ├── choice.js -+ | | ├── model/ -+ | | | ├── actions.js -+ | | | ├── constants.js -+ | | | ├── epics.js -+ | | | ├── getters.js -+ | | | ├── selectors.js -+ | | ├── lib/ # ~ helpers - | ├── region/ - | ├── user/ -``` - -## 참고 자료 - -* [(아티클) Coupling과 Cohesion의 명확한 이해](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) -* [(아티클) Coupling, Cohesion과 Law of Demeter](https://medium.com/german-gorelkin/low-coupling-high-cohesion-d36369fb1be9) \ No newline at end of file diff --git a/i18n/ru/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx b/i18n/ru/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx deleted file mode 100644 index 3da4f36d17..0000000000 --- a/i18n/ru/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -sidebar_position: 2 -sidebar_class_name: sidebar-item--wip ---- - -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' - -# Десегментация - - - -## Ситуация {#situation} - -Очень часто на проектах встречается ситуация, когда модули, относящиеся к конкретному домену из предметной области, излишне десегментированы и раскиданы по проекту - -```sh -├── components/ -| ├── DeliveryCard -| ├── DeliveryChoice -| ├── RegionSelect -| ├── UserAvatar -├── actions/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── epics/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── constants/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── helpers/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── entities/ -| ├── delivery/ -| | ├── getters.js -| | ├── selectors.js -| ├── region/ -| ├── user/ -``` - -## Проблема {#problem} - -Проблема проявляется как минимум в нарушении принципа **High Cohesion** и излишнего растягивания **оси изменений** - -## Если проигнорировать {#if-you-ignore-it} - -- При необходимости затронуть логику, например, доставки - нам придется держать в голове, что она лежит в нескольких местах и затронуть в коде именно несколько мест - что излишне растягивает нашу **Ось изменений** -- Если нам надо изучить логику по пользователю, нам придется пройтись по всему-всему проекту, чтобы изучить в деталях **actions, epics, constants, entities, components** - вместо того, чтобы это лежало в одном месте -- Неявные связи и неконтролируемость растущей предметной области - - При таком подходе очень часто замыливается глаз и можно не заметить, как мы "создаем константы ради констант", создавая свалку в соответствующей директории проекта - -## Решение {#solution} - -Располагать все модули, относящиеся к конкретному домену/юзкейсу - непосредственно рядом - -Чтобы при изучении конкретного модуля - все его составляющие лежали рядом, а не были раскиданы по проекту - -> Это также повышает discoverability и явность кодовой базы и связей между модулями - -```diff -- ├── components/ -- | ├── DeliveryCard -- | ├── DeliveryChoice -- | ├── RegionSelect -- | ├── UserAvatar -- ├── actions/ -- | ├── delivery.js -- | ├── region.js -- | ├── user.js -- ├── epics/{...} -- ├── constants/{...} -- ├── helpers/{...} - ├── entities/ - | ├── delivery/ -+ | | ├── ui/ # ~ components/ -+ | | | ├── card.js -+ | | | ├── choice.js -+ | | ├── model/ -+ | | | ├── actions.js -+ | | | ├── constants.js -+ | | | ├── epics.js -+ | | | ├── getters.js -+ | | | ├── selectors.js -+ | | ├── lib/ # ~ helpers - | ├── region/ - | ├── user/ -``` - -## См. также {#see-also} -* [(Статья) Про Low Coupling и High Cohesion наглядно](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) -* [(Статья) Low Coupling и High Cohesion. Закон Деметры](https://medium.com/german-gorelkin/low-coupling-high-cohesion-d36369fb1be9) diff --git a/i18n/vi/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx b/i18n/vi/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx deleted file mode 100644 index 736bbb7f4a..0000000000 --- a/i18n/vi/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -sidebar_position: 2 -sidebar_class_name: sidebar-item--wip ---- - -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' - -# Desegmented - - - -## Tình huống - -Rất thường xuyên xảy ra tình huống trên các project khi các module liên quan đến một domain cụ thể từ lĩnh vực chủ đề bị phân tách không cần thiết và nằm rải rác khắp project - -```sh -├── components/ -| ├── DeliveryCard -| ├── DeliveryChoice -| ├── RegionSelect -| ├── UserAvatar -├── actions/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── epics/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── constants/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── helpers/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── entities/ -| ├── delivery/ -| | ├── getters.js -| | ├── selectors.js -| ├── region/ -| ├── user/ -``` - -## Vấn đề - -Vấn đề thể hiện ít nhất là vi phạm nguyên tắc **High Cohesion** và kéo dài quá mức **trục thay đổi** - -## Nếu bỏ qua - -- Nếu cần chạm vào logic, ví dụ delivery - chúng ta sẽ phải nhớ rằng nó nằm ở nhiều nơi và phải chạm vào nhiều chỗ trong code - điều này kéo dài không cần thiết **Trục thay đổi** của chúng ta -- Nếu cần nghiên cứu logic của user, chúng ta sẽ phải đi khắp project để tìm hiểu chi tiết **actions, epics, constants, entities, components** - thay vì để nó nằm ở một chỗ -- Các liên kết ngầm và sự mất kiểm soát của domain area đang phát triển -- Với cách tiếp cận này, mắt rất dễ bị mờ đi và bạn có thể không nhận ra khi chúng ta "tạo constants vì constants", tạo ra một đống rác trong thư mục tương ứng của project - -## Giải pháp - -Đặt tất cả các module liên quan đến một domain/use case cụ thể - ngay cạnh nhau - -Để khi nghiên cứu một module cụ thể, tất cả các thành phần của nó nằm cạnh nhau, không bị rải rác khắp project - -> Điều này cũng tăng khả năng khám phá và sự rõ ràng của code base và mối quan hệ giữa các module - -```diff -- ├── components/ -- | ├── DeliveryCard -- | ├── DeliveryChoice -- | ├── RegionSelect -- | ├── UserAvatar -- ├── actions/ -- | ├── delivery.js -- | ├── region.js -- | ├── user.js -- ├── epics/{...} -- ├── constants/{...} -- ├── helpers/{...} - ├── entities/ - | ├── delivery/ -+ | | ├── ui/ # ~ components/ -+ | | | ├── card.js -+ | | | ├── choice.js -+ | | ├── model/ -+ | | | ├── actions.js -+ | | | ├── constants.js -+ | | | ├── epics.js -+ | | | ├── getters.js -+ | | | ├── selectors.js -+ | | ├── lib/ # ~ helpers - | ├── region/ - | ├── user/ -``` - -## Xem thêm - -* [(Article) Về Low Coupling và High Cohesion một cách rõ ràng](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) -* [(Article) Low Coupling và High Cohesion. Law of Demeter](https://medium.com/german-gorelkin/low-coupling-high-cohesion-d36369fb1be9) \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx deleted file mode 100644 index 9b011b03a3..0000000000 --- a/i18n/zh/docusaurus-plugin-content-docs/current/guides/issues/desegmented.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -sidebar_position: 2 -sidebar_class_name: sidebar-item--wip ---- - -import WIP from '@site/src/shared/ui/wip/tmpl.mdx' - -# 去分段化 - - - -## 情况 - -在项目中经常出现这样的情况:与主题领域中特定域相关的模块被不必要地去分段化并分散在项目周围 - -```sh -├── components/ -| ├── DeliveryCard -| ├── DeliveryChoice -| ├── RegionSelect -| ├── UserAvatar -├── actions/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── epics/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── constants/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── helpers/ -| ├── delivery.js -| ├── region.js -| ├── user.js -├── entities/ -| ├── delivery/ -| | ├── getters.js -| | ├── selectors.js -| ├── region/ -| ├── user/ -``` - -## 问题 - -该问题至少表现为违反了**高内聚**原则和过度拉伸**变更轴** - -## 如果您忽略它 - -- 如果需要涉及逻辑,例如交付 - 我们必须记住它位于多个地方,并涉及代码中的多个地方 - 这不必要地拉伸了我们的**变更轴** -- 如果我们需要研究用户的逻辑,我们将不得不遍历整个项目来详细研究**actions、epics、constants、entities、components** - 而不是将其放在一个地方 -- 隐式连接和不断增长的主题领域的不可控性 -- 使用这种方法,眼睛经常会模糊,您可能不会注意到我们如何"为了常量而创建常量",在相应的项目目录中创建垃圾场 - -## 解决方案 - -将与特定域/用户案例相关的所有模块 - 直接彼此相邻放置 - -这样,在研究特定模块时,其所有组件都并排放置,而不是分散在项目周围 - -> 它还增加了代码库的可发现性和清晰度以及模块之间的关系 - -```diff -- ├── components/ -- | ├── DeliveryCard -- | ├── DeliveryChoice -- | ├── RegionSelect -- | ├── UserAvatar -- ├── actions/ -- | ├── delivery.js -- | ├── region.js -- | ├── user.js -- ├── epics/{...} -- ├── constants/{...} -- ├── helpers/{...} - ├── entities/ - | ├── delivery/ -+ | | ├── ui/ # ~ components/ -+ | | | ├── card.js -+ | | | ├── choice.js -+ | | ├── model/ -+ | | | ├── actions.js -+ | | | ├── constants.js -+ | | | ├── epics.js -+ | | | ├── getters.js -+ | | | ├── selectors.js -+ | | ├── lib/ # ~ helpers - | ├── region/ - | ├── user/ -``` - -## See also - -* [(Article) About Low Coupling and High Cohesion clearly](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) -* [(Article) Low Coupling and High Cohesion. The Law of Demeter](https://medium.com/german-gorelkin/low-coupling-high-cohesion-d36369fb1be9) From 1ae476f49541d381fc629f679accb7df3a7983b5 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 01:10:29 +0100 Subject: [PATCH 11/45] docs: migrate examples/guides/types --- src/content/docs/guides/examples/types.mdx | 436 +++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 src/content/docs/guides/examples/types.mdx diff --git a/src/content/docs/guides/examples/types.mdx b/src/content/docs/guides/examples/types.mdx new file mode 100644 index 0000000000..7a2defc8a8 --- /dev/null +++ b/src/content/docs/guides/examples/types.mdx @@ -0,0 +1,436 @@ +--- +title: Types +sidebar: + order: 2 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +This guide concerns data types from typed languages like TypeScript and describes where they fit within FSD. + + + +## Utility types + +Utility types are types that don't have much meaning on their own and are usually used with other types. For example: + +```ts +type ArrayValues = T[number]; +``` + +To make utility types available across your project, either install a library like [`type-fest`][ext-type-fest], or create your own library in `shared/lib`. Make sure to clearly indicate what new types _should_ be added to this library, and what types _don't belong_ there. For example, call it `shared/lib/utility-types` and add a README inside that describes what is a utility type in your team. + +Don't overestimate the potential reusability of a utility type. Just because it can be reused, doesn't mean it will be, and as such, not every utility type needs to be in Shared. Some utility types are fine right next to where they are needed: + + +- pages/ + - home/ + - api/ + - ArrayValues.ts utility type + - getMemoryUsageMetrics.ts uses the utility type + + + + +## Business entities and their cross-references + +Among the most important types in an app are the types of business entities, i.e. the real-world things that your app works with. For example, in a music streaming app, you might have business entities _Song_, _Album_, etc. + +Business entities often come from the backend, so the first step is to type the backend responses. It's convenient to have a function to make a request to every endpoint, and to type the response of this function. For extra type safety, you may want to run the response through a schema validation library like [Zod][ext-zod]. + +For example, if you keep all your requests in Shared, you could do it like this: + +```ts title="shared/api/songs.ts" +import type { Artist } from "./artists"; + +interface Song { + id: number; + title: string; + artists: Array; +} + +export function listSongs() { + return fetch('/api/songs').then((res) => res.json() as Promise>); +} +``` + +You might notice that the `Song` type references a different entity, `Artist`. This is a benefit of storing your requests in Shared — real-world types are often intertwined. If we kept this function in `entities/song/api`, we wouldn't be able to simply import `Artist` from `entities/artist`, because FSD restricts cross-imports between slices with [the import rule on layers][import-rule-on-layers]: + +> A module in a slice can only import other slices when they are located on layers strictly below. + +There are two ways to deal with this issue: + +1. **Parametrize your types** + You can make your types accept type arguments as slots for connections with other entities, and even impose constraints on those slots. For example: + + ```ts title="entities/song/model/song.ts" + interface Song { + id: number; + title: string; + artists: Array; + } + ``` + + This works better for some types than others. A simple type like `Cart = { items: Array }` can easily be made to work with any type of product. More connected types, like `Country` and `City`, may not be as easy to separate. + +2. **Cross-import (but do it right)** + To make cross-imports between entities in FSD, you can use a special public API specifically for each slice that will be cross-importing. For example, if we have entities `song`, `artist`, and `playlist`, and the latter two need to reference `song`, we can make two special public APIs for both of them in the `song` entity with the `@x` notation: + + + - entities/ + - song/ + - @x/ + - artist.ts a public API for the `artist` entity to import from + - playlist.ts a public API for the `playlist` entity to import from + - index.ts regular public API + + + The contents of a file `📄 entities/song/@x/artist.ts` are similar to `📄 entities/song/index.ts`: + + ```ts title="entities/song/@x/artist.ts" + export type { Song } from "../model/song.ts"; + ``` + + Then the `📄 entities/artist/model/artist.ts` can import `Song` like this: + + ```ts title="entities/artist/model/artist.ts" + import type { Song } from "entities/song/@x/artist"; + + export interface Artist { + name: string; + songs: Array; + } + ``` + + By making explicit connections between entities, we stay on top of inter-dependencies and maintain a decent level of domain separation. + +## Data transfer objects and mappers \{#data-transfer-objects-and-mappers\} + +Data transfer objects, or DTOs, is a term that describes the shape of data that comes from the backend. Sometimes, the DTO is fine to use as is, but sometimes it's inconvenient for the frontend. That's where mappers come in — they transform a DTO into a more convenient shape. + +### Where to put DTOs + +If you have backend types in a separate package (for example, if you share code between the frontend and the backend), then just import your DTOs from there and you're done! If you don't share code between the backend and frontend, then you need to keep DTOs somewhere in your frontend codebase, and we will explore this case below. + +If you have your request functions in `shared/api`, that's where the DTOs should be, right next to the function that uses them: + +```ts title="shared/api/songs.ts" +import type { ArtistDTO } from "./artists"; + +interface SongDTO { + id: number; + title: string; + artist_ids: Array; +} + +export function listSongs() { + return fetch('/api/songs').then((res) => res.json() as Promise>); +} +``` + +As mentioned in the previous section, storing your requests and DTOs in Shared comes with the benefit of being able to reference other DTOs. + +### Where to put mappers + +Mappers are functions that accept a DTO for transformation, and as such, they should be located near the definition of the DTO. In practice this means that if your requests and DTOs are defined in `shared/api`, then the mappers should go there as well: + +```ts title="shared/api/songs.ts" +import type { ArtistDTO } from "./artists"; + +interface SongDTO { + id: number; + title: string; + disc_no: number; + artist_ids: Array; +} + +interface Song { + id: string; + title: string; + /** The full title of the song, including the disc number. */ + fullTitle: string; + artistIds: Array; +} + +function adaptSongDTO(dto: SongDTO): Song { + return { + id: String(dto.id), + title: dto.title, + fullTitle: `${dto.disc_no} / ${dto.title}`, + artistIds: dto.artist_ids.map(String), + }; +} + +export function listSongs() { + return fetch('/api/songs').then(async (res) => (await res.json()).map(adaptSongDTO)); +} +``` + +If your requests and stores are defined in entity slices, then all this code would go there, keeping in mind the limitations of cross-imports between slices: + +```ts title="entities/song/api/dto.ts" +import type { ArtistDTO } from "entities/artist/@x/song"; + +export interface SongDTO { + id: number; + title: string; + disc_no: number; + artist_ids: Array; +} +``` + +```ts title="entities/song/api/mapper.ts" +import type { SongDTO } from "./dto"; + +export interface Song { + id: string; + title: string; + /** The full title of the song, including the disc number. */ + fullTitle: string; + artistIds: Array; +} + +export function adaptSongDTO(dto: SongDTO): Song { + return { + id: String(dto.id), + title: dto.title, + fullTitle: `${dto.disc_no} / ${dto.title}`, + artistIds: dto.artist_ids.map(String), + }; +} +``` + +```ts title="entities/song/api/listSongs.ts" +import { adaptSongDTO } from "./mapper"; + +export function listSongs() { + return fetch('/api/songs').then(async (res) => (await res.json()).map(adaptSongDTO)); +} +``` + +```ts title="entities/song/model/songs.ts" +import { createSlice, createEntityAdapter } from "@reduxjs/toolkit"; + +import { listSongs } from "../api/listSongs"; + +export const fetchSongs = createAsyncThunk('songs/fetchSongs', listSongs); + +const songAdapter = createEntityAdapter(); +const songsSlice = createSlice({ + name: "songs", + initialState: songAdapter.getInitialState(), + reducers: {}, + extraReducers: (builder) => { + builder.addCase(fetchSongs.fulfilled, (state, action) => { + songAdapter.upsertMany(state, action.payload); + }) + }, +}); +``` + +### How to deal with nested DTOs + +The most problematic part is when a response from the backend contains several entities. For example, if the song included not just the authors' IDs, but the entire author objects. In this case, it is impossible for entities not to know about each other (unless we want to discard the data or have a firm conversation with the backend team). Instead of coming up with solutions for indirect connections between slices (such as a common middleware that would dispatch actions to other slices), prefer explicit cross-imports with the `@x` notation. Here is how we can implement it with Redux Toolkit: + +```ts title="entities/song/model/songs.ts" +import { + createSlice, + createEntityAdapter, + createAsyncThunk, + createSelector, +} from '@reduxjs/toolkit' +import { normalize, schema } from 'normalizr' + +import { getSong } from "../api/getSong"; + +// Define normalizr entity schemas +export const artistEntity = new schema.Entity('artists') +export const songEntity = new schema.Entity('songs', { + artists: [artistEntity], +}) + +const songAdapter = createEntityAdapter() + +export const fetchSong = createAsyncThunk( + 'songs/fetchSong', + async (id: string) => { + const data = await getSong(id) + // Normalize the data so reducers can load a predictable payload, like: + // `action.payload = { songs: {}, artists: {} }` + const normalized = normalize(data, songEntity) + return normalized.entities + } +) + +export const slice = createSlice({ + name: 'songs', + initialState: songAdapter.getInitialState(), + reducers: {}, + extraReducers: (builder) => { + builder.addCase(fetchSong.fulfilled, (state, action) => { + songAdapter.upsertMany(state, action.payload.songs) + }) + }, +}) + +const reducer = slice.reducer +export default reducer +``` + +```ts title="entities/song/@x/artist.ts" +export { fetchSong } from "../model/songs"; +``` + +```ts title="entities/artist/model/artists.ts" +import { createSlice, createEntityAdapter } from '@reduxjs/toolkit' + +import { fetchSong } from 'entities/song/@x/artist' + +const artistAdapter = createEntityAdapter() + +export const slice = createSlice({ + name: 'users', + initialState: artistAdapter.getInitialState(), + reducers: {}, + extraReducers: (builder) => { + builder.addCase(fetchSong.fulfilled, (state, action) => { + // And handle the same fetch result by inserting the artists here + artistAdapter.upsertMany(state, action.payload.artists) + }) + }, +}) + +const reducer = slice.reducer +export default reducer +``` + +This slightly limits the benefits of slice isolation, but it accurately represents a connection between these two entities that we have no control over. If these entities are to ever be refactored, they have to be refactored together. + +## Global types and Redux + +Global types are types that will be used across the whole application. There are two kinds of global types, based on what they need to know about: +1. Generic types that don't have any application specifics +2. Types that need to know about the whole application + +The first case is simple to resolve — place your types in Shared, in an appropriate segment. For example, if you have an interface for a global variable for analytics, you can put it in `shared/analytics`. + +:::warning + +Avoid creating the `shared/types` folder. It groups unrelated things based only on the property of "being a type", and that property is usually not useful when searching for code in a project. + +::: + +The second case is commonly encountered in projects with Redux without RTK. Your final store type is only available once you add all the reducers together, but this store type needs to be available to selectors that you use across the app. For example, here's your typical store definition: + +```ts title="app/store/index.ts" +import { combineReducers, rootReducer } from "redux"; + +import { songReducer } from "entities/song"; +import { artistReducer } from "entities/artist"; + +const rootReducer = combineReducers(songReducer, artistReducer); + +const store = createStore(rootReducer); + +type RootState = ReturnType; +type AppDispatch = typeof store.dispatch; +``` + +It would be nice to have typed Redux hooks `useAppDispatch` and `useAppSelector` in `shared/store`, but they cannot import `RootState` and `AppDispatch` from the App layer due to the [import rule on layers][import-rule-on-layers]: + +> A module in a slice can only import other slices when they are located on layers strictly below. + +The recommended solution in this case is to create an implicit dependency between layers Shared and App. These two types, `RootState` and `AppDispatch` are unlikely to change, and they will be familiar to Redux developers, so we don't have to worry about them as much. + +In TypeScript, you can do it by declaring the types as global like this: + +```ts title="app/store/index.ts" +/* same content as in the code block before… */ + +declare type RootState = ReturnType; +declare type AppDispatch = typeof store.dispatch; +``` + +```ts title="shared/store/index.ts" +import { useDispatch, useSelector, type TypedUseSelectorHook } from "react-redux"; + +export const useAppDispatch = useDispatch.withTypes() +export const useAppSelector: TypedUseSelectorHook = useSelector; +``` + +## Enums + +The general rule with enums is that they should be defined **as close to the usage locations as possible**. When an enum represents values specific to a single feature, it should be defined in that same feature. + +The choice of segment should be dictated by usage locations as well. If your enum contains, for example, positions of a toast on the screen, it should be placed in the `ui` segment. If it represents the loading state of a backend operation, it should be placed in the `api` segment. + +Some enums are truly common across the whole project, like general backend response statuses or design system tokens. In this case, you can place them in Shared, and choose the segment based on what the enum represents (`api` for response statuses, `ui` for design tokens, etc.). + +## Type validation schemas and Zod + +If you want to validate that your data conforms to a certain shape or constraints, you can define a validation schema. In TypeScript, a popular library for this job is [Zod][ext-zod]. Validation schemas should also be colocated with the code that uses them, as much as possible. + +Validation schemas are similar to mappers (as discussed in the [Data transfer objects and mappers](#data-transfer-objects-and-mappers) section) in the sense that they take a data transfer object and parse it, producing an error if the parsing fails. + +One of the most common cases of validation is for the data that comes from the backend. Typically, you want to fail the request when the data doesn't match the schema, so it makes sense to put the schema in the same place as the request function, which is usually the `api` segment. + +If your data comes through user input, like a form, the validation should happen as the data is being entered. You can place your schema in the `ui` segment, next to the form component, or in the `model` segment, if the `ui` segment is too crowded. + +## Typings of component props and context + +In general, it's best to keep the props or context interface in the same file as the component or context that uses them. If you have a framework with single-file components, like Vue or Svelte, and you can't define the props interface in the same file, or you want to share that interface between several components, create a separate file in the same folder, typically, the `ui` segment. + +Here's an example with JSX (React or Solid): + +```ts title="pages/home/ui/RecentActions.tsx" +interface RecentActionsProps { + actions: Array<{ id: string; text: string }>; +} + +export function RecentActions({ actions }: RecentActionsProps) { + /* … */ +} +``` + +And here's an example with the interface stored in a separate file for Vue: + +```ts title="pages/home/ui/RecentActionsProps.ts" +export interface RecentActionsProps { + actions: Array<{ id: string; text: string }>; +} +``` + +```html title="pages/home/ui/RecentActions.vue" + +``` + +## Ambient declaration files (`*.d.ts`) + +Some packages, for example, [Vite][ext-vite] or [ts-reset][ext-ts-reset], require ambient declaration files to work across your app. Usually, they aren't large or complicated, so they often don't require any architecting, it's fine to just throw them in the `src/` folder. To keep the `src` more organized, you can keep them on the App layer, in `app/ambient/`. + +Other packages simply don't have typings, and you might want to declare them as untyped or even write your own typings for them. A good place for those typings would be `shared/lib`, in a folder like `shared/lib/untyped-packages`. Create a `%LIBRARY_NAME%.d.ts` file there and declare the types you need: + +```ts title="shared/lib/untyped-packages/use-react-screenshot.d.ts" +// This library doesn't have typings, and we didn't want to bother writing our own. +declare module "use-react-screenshot"; +``` + +## Auto-generation of types + +It's common to generate types from external sources, for example, generating backend types from an OpenAPI schema. In this case, create a dedicated place in your codebase for these types, like `shared/api/openapi`. Ideally, you should also include a README in that folder that describes what these files are, how to regenerate them, etc. + +[import-rule-on-layers]: /docs/reference/layers#import-rule-on-layers +[ext-type-fest]: https://github.com/sindresorhus/type-fest +[ext-zod]: https://zod.dev +[ext-vite]: https://vitejs.dev +[ext-ts-reset]: https://www.totaltypescript.com/ts-reset From d87edb940ec279f7b3494918ee942d4b46b33216 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 01:13:24 +0100 Subject: [PATCH 12/45] docs: migrate guides/examples/page-layout --- .../docs/guides/examples/page-layout.mdx | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/content/docs/guides/examples/page-layout.mdx diff --git a/src/content/docs/guides/examples/page-layout.mdx b/src/content/docs/guides/examples/page-layout.mdx new file mode 100644 index 0000000000..d2c901005d --- /dev/null +++ b/src/content/docs/guides/examples/page-layout.mdx @@ -0,0 +1,104 @@ +--- +title: Page Layouts +sidebar: + position: 3 +--- + +import { Aside } from '@astrojs/starlight/components'; + +This guide examines the abstraction of a _page layout_ — when several pages share the same overall structure, and differ only in the main content. + + + +## Simple layout + +The simplest layout can be seen on this page. It has a header with site navigation, two sidebars, and a footer with external links. There is no complicated business logic, and the only dynamic parts are sidebars and the switchers on the right side of the header. Such a layout can be placed entirely in `shared/ui` or in `app/layouts`, with props filling in the content for the sidebars: + +```tsx title="shared/ui/layout/Layout.tsx" +import { Link, Outlet } from "react-router-dom"; +import { useThemeSwitcher } from "./useThemeSwitcher"; + +export function Layout({ siblingPages, headings }) { + const [theme, toggleTheme] = useThemeSwitcher(); + + return ( +
+
+ + +
+
+ + {/* This is where the main content goes */} + +
+
+
    +
  • GitHub
  • +
  • Twitter
  • +
+
+
+ ); +} +``` + +```ts title="shared/ui/layout/useThemeSwitcher.ts" +export function useThemeSwitcher() { + const [theme, setTheme] = useState("light"); + + function toggleTheme() { + setTheme(theme === "light" ? "dark" : "light"); + } + + useEffect(() => { + document.body.classList.remove("light", "dark"); + document.body.classList.add(theme); + }, [theme]); + + return [theme, toggleTheme] as const; +} +``` + +The code of sidebars is left as an exercise for the reader 😉. + +## Using widgets in the layout + +Sometimes you want to include certain business logic in the layout, especially if you're using deeply nested routes with a router like [React Router][ext-react-router]. Then you can't store the layout in Shared or in Widgets due to [the import rule on layers][import-rule-on-layers]: + +> A module in a slice can only import other slices when they are located on layers strictly below. + +Before we discuss solutions, we need to discuss if it's even a problem in the first place. Do you _really need_ that layout, and if so, does it _really need_ to be a Widget? If the block of business logic in question is reused on 2-3 pages, and the layout is simply a small wrapper for that widget, consider one of these two options: + +1. **Write the layout inline on the App layer, where you configure the routing** + This is great for routers that support nesting, because you can group certain routes and apply the layout only to them. + +2. **Just copy-paste it** + The urge to abstract code is often very overrated. It is especially the case for layouts, which rarely change. At some point, if one of these pages will need to change, you can simply do the change without needlessly affecting other pages. If you're worried that someone might forget to update the other pages, you can always leave a comment that describes the relationship between the pages. + +If none of the above are applicable, there are two solutions to include a widget in the layout: + +1. **Use render props or slots** + Most frameworks allow you to pass a piece of UI externally. In React, it's called [render props][ext-render-props], in Vue it's called [slots][ext-vue-slots]. +2. **Move the layout to the App layer** + You can also store your layout on the App layer, for example, in `app/layouts`, and compose any widgets you want. + +## Further reading + +- There's an example of how to build a layout with authentication with React and Remix (equivalent to React Router) in the [tutorial][tutorial]. + +[tutorial]: /docs/get-started/tutorial +[import-rule-on-layers]: /docs/reference/layers#import-rule-on-layers +[ext-react-router]: https://reactrouter.com/ +[ext-render-props]: https://www.patterns.dev/react/render-props-pattern/ +[ext-vue-slots]: https://vuejs.org/guide/components/slots From 64d00ba9377dfddf31542b3456783da7d7fe3bea Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 01:18:52 +0100 Subject: [PATCH 13/45] docs: migrate guides/examples/api-requests --- .../docs/guides/examples/api-requests.mdx | 144 ++++++++++++++++++ .../docs/guides/examples/page-layout.mdx | 2 +- 2 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/guides/examples/api-requests.mdx diff --git a/src/content/docs/guides/examples/api-requests.mdx b/src/content/docs/guides/examples/api-requests.mdx new file mode 100644 index 0000000000..17c34d441f --- /dev/null +++ b/src/content/docs/guides/examples/api-requests.mdx @@ -0,0 +1,144 @@ +--- +title: Handling API Requests +sidebar: + order: 4 +--- + +import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components'; + +## Shared API Requests \{#shared-api-requests\} + +Start by placing common API request logic in the `shared/api` directory. This makes it easy to reuse requests across your application and helps with faster prototyping. For many projects, this is all you'll need for API calls. + +A typical file structure would be: + + +- shared/ + - client.ts + - index.ts + - endpoints/ + - login.ts + + +The `client.ts` file centralizes your HTTP request setup. It wraps your chosen method (like `fetch()` or an `axios` instance) and handles common configurations, such as: + +- Backend base URL. +- Default headers (e.g., for authentication). +- Data serialization. + +Here are examples for `axios` and `fetch`: + + + + +```ts title="shared/api/client.ts" +// Example using axios +import axios from 'axios'; + +export const client = axios.create({ + baseURL: 'https://your-api-domain.com/api/', + timeout: 5000, + headers: { 'X-Custom-Header': 'my-custom-value' } +}); +``` + + + +```ts title="shared/api/client.ts" +export const client = { + async post(endpoint: string, body: any, options?: RequestInit) { + const response = await fetch(`https://your-api-domain.com/api${endpoint}`, { + method: 'POST', + body: JSON.stringify(body), + ...options, + headers: { + 'Content-Type': 'application/json', + 'X-Custom-Header': 'my-custom-value', + ...options?.headers, + }, + }); + return response.json(); + } + // ... other methods like put, delete, etc. +}; +``` + + + + +Organize your individual API request functions in `shared/api/endpoints`, grouping them by the API endpoint. + +:::note + +To keep examples focused, we omit form interaction and validation. For details on libraries like Zod or Valibot, refer to the [Type Validation and Schemas](/docs/guides/examples/types#type-validation-schemas-and-zod) article. + +::: + +```ts title="shared/api/endpoints/login.ts" +import { client } from '../client'; + +export interface LoginCredentials { + email: string; + password: string; +} + +export function login(credentials: LoginCredentials) { + return client.post('/login', credentials); +} +``` +Use an `index.ts` file in `shared/api` to export your request functions. + +```ts title="shared/api/index.ts" +export { client } from './client'; // If you want to export the client itself +export { login } from './endpoints/login'; +export type { LoginCredentials } from './endpoints/login'; +``` + +## Slice-Specific API Requests \{#slice-specific-api-requests\} + +If an API request is only used by a specific slice (like a single page or feature) and won't be reused, place it in the api segment of that slice. This keeps slice-specific logic neatly contained. + + +- pages/ + - login/ + - index.ts + - api/ + - login.ts + - ui/ + - LoginPage.tsx + + +```ts title="pages/login/api/login.ts" +import { client } from 'shared/api'; + +interface LoginCredentials { + email: string; + password: string; +} + +export function login(credentials: LoginCredentials) { + return client.post('/login', credentials); +} +``` + +You don't need to export `login()` function in the page's public API, because it's unlikely that any other place in the app will need this request. + +:::note + +Avoid placing API calls and response types in the `entities` layer prematurely. Backend responses may differ from what your frontend entities need. API logic in `shared/api` or a slice's `api` segment allows you to transform data appropriately, keeping entities focused on frontend concerns. + +::: + +## Using Client Generators \{#client-generators\} + +If your backend has an OpenAPI specification, tools like [orval](https://orval.dev/) or [openapi-typescript](https://openapi-ts.dev/) can generate API types and request functions for you. Place the generated code in, for example, `shared/api/openapi`. Make sure to include `README.md` to document what those types are, and how to generate them. + +## Integrating with Server State Libraries \{#server-state-libraries\} + +When using server state libraries like [TanStack Query (React Query)](https://tanstack.com/query/latest) or [Pinia Colada](https://pinia-colada.esm.dev/) you might need to share types or cache keys between slices. Use the `shared` layer for things like: + +- API data types +- Cache keys +- Common query/mutation options + +For more details on how to work with server state libraries, refer to [React Query article](/docs/guides/tech/with-react-query) diff --git a/src/content/docs/guides/examples/page-layout.mdx b/src/content/docs/guides/examples/page-layout.mdx index d2c901005d..1e005abe2f 100644 --- a/src/content/docs/guides/examples/page-layout.mdx +++ b/src/content/docs/guides/examples/page-layout.mdx @@ -1,7 +1,7 @@ --- title: Page Layouts sidebar: - position: 3 + order: 3 --- import { Aside } from '@astrojs/starlight/components'; From acfcf1eccaeec785e0157e4dd91c67a1e345e31d Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 01:42:11 +0100 Subject: [PATCH 14/45] docs: migrate guides/migration articles --- astro.config.mjs | 4 + .../docs/guides/migration/from-custom.mdx | 234 ++++++++++++++++++ src/content/docs/guides/migration/from-v1.mdx | 175 +++++++++++++ .../docs/guides/migration/from-v2-0.mdx | 45 ++++ 4 files changed, 458 insertions(+) create mode 100644 src/content/docs/guides/migration/from-custom.mdx create mode 100644 src/content/docs/guides/migration/from-v1.mdx create mode 100644 src/content/docs/guides/migration/from-v2-0.mdx diff --git a/astro.config.mjs b/astro.config.mjs index 4b38fa722c..ede7cd175f 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -35,6 +35,10 @@ export default defineConfig({ autogenerate: { directory: 'guides/examples' } }, ], + }, + { + label: 'Migration', + autogenerate: { directory: 'guides/migration' } } ], }), diff --git a/src/content/docs/guides/migration/from-custom.mdx b/src/content/docs/guides/migration/from-custom.mdx new file mode 100644 index 0000000000..c2127ebac0 --- /dev/null +++ b/src/content/docs/guides/migration/from-custom.mdx @@ -0,0 +1,234 @@ +--- +title: From a custom architecture +sidebar: + order: 1 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +This guide describes an approach that might be helpful when migrating from a custom self-made architecture to Feature-Sliced Design. + +Here is the folder structure of a typical custom architecture. We will be using it as an example in this guide. +Click on the blue arrow to open the folder. + + +- src/ + - actions/ + - product/ + - order/ + - api/ + - components/ + - containers/ + - constants/ + - i18n/ + - modules/ + - helpers/ + - routes/ + - products.jsx + - products.[id].jsx + - utils/ + - reducers/ + - selectors/ + - styles/ + - App.jsx + - index.jsx + + +## Before you start \{#before-you-start\} + +The most important question to ask your team when considering to switch to Feature-Sliced Design is — _do you really need it?_ We love Feature-Sliced Design, but even we recognize that some projects are perfectly fine without it. + +Here are some reasons to consider making the switch: + +1. New team members are complaining that it's hard to get to a productive level +2. Making modifications to one part of the code **often** causes another unrelated part to break +3. Adding new functionality is difficult due to the sheer amount of things you need to think about + +**Avoid switching to FSD against the will of your teammates**, even if you are the lead. +First, convince your teammates that the benefits outweigh the cost of migration and the cost of learning a new architecture instead of the established one. + +Also keep in mind that any kind of architectural changes are not immediately observable to the management. Make sure they are on board with the switch before starting and explain to them why it might benefit the project. + + + +--- + +If you made the decision to start migrating, then the first thing you want to do is to set up an alias for `📁 src`. It will be helpful later to refer to top-level folders. We will consider `@` as an alias for `./src` for the rest of this guide. + +## Step 1. Divide the code by pages \{#divide-code-by-pages\} + +Most custom architectures already have a division by pages, however small or large in logic. If you already have `📁 pages`, you may skip this step. + +If you only have `📁 routes`, create `📁 pages` and try to move as much component code from `📁 routes` as possible. Ideally, you would have a tiny route and a larger page. As you're moving code, create a folder for each page and add an index file: + + + +Route file: + +```js title="src/routes/products.[id].js" +export { ProductPage as default } from "@/pages/product" +``` + +Page index file: + +```js title="src/pages/product/index.js" +export { ProductPage } from "./ProductPage.jsx" +``` + +Page component file: + +```jsx title="src/pages/product/ProductPage.jsx" +export function ProductPage(props) { + return
; +} +``` + +## Step 2. Separate everything else from the pages \{#separate-everything-else-from-pages\} + +Create a folder `📁 src/shared` and move everything that doesn't import from `📁 pages` or `📁 routes` there. Create a folder `📁 src/app` and move everything that does import the pages or routes there, including the routes themselves. + +Remember that the Shared layer doesn't have slices, so it's fine if segments import from each other. + +You should end up with a file structure like this: + + +- src/ + - app/ + - routes/ + - products.jsx + - products.[id].jsx + - App.jsx + - index.js + - pages/ + - product/ + - index.js + - ui/ + - ProductPage.jsx + - catalog/ + - shared/ + - actions/ + - api/ + - components/ + - containers/ + - constants/ + - i18n/ + - modules/ + - helpers/ + - utils/ + - reducers/ + - selectors/ + - styles/ + + +## Step 3. Tackle cross-imports between pages \{#tackle-cross-imports-between-pages\} + +Find all instances where one page is importing from the other and do one of the two things: + +1. Copy-paste the imported code into the depending page to remove the dependency +2. Move the code to a proper segment in Shared: + - if it's a part of the UI kit, move it to `📁 shared/ui`; + - if it's a configuration constant, move it to `📁 shared/config`; + - if it's a backend interaction, move it to `📁 shared/api`. + + + +## Step 4. Unpack the Shared layer \{#unpack-shared-layer\} + +You might have a lot of stuff in the Shared layer on this step, and you generally want to avoid that. The reason is that the Shared layer may be a dependency for any other layer in your codebase, so making changes to that code is automatically more prone to unintended consequences. + +Find all the objects that are only used on one page and move it to the slice of that page. And yes, _that applies to actions, reducers, and selectors, too_. There is no benefit in grouping all actions together, but there is benefit in colocating relevant actions close to their usage. + +You should end up with a file structure like this: + + +- src/ + - app/ + - ... + - pages/ + - product/ + - actions/ + - reducers/ + - selectors/ + - ui/ + - Component.jsx + - Container.jsx + - ProductPage.jsx + - index.js + - catalog/ + - shared/ only objects that are reused + - actions/ + - api/ + - components/ + - containers/ + - constants/ + - i18n/ + - modules/ + - helpers/ + - utils/ + - reducers/ + - selectors/ + - styles/ + + +## Step 5. Organize code by technical purpose \{#organize-by-technical-purpose\} + +In FSD, division by technical purpose is done with _segments_. There are a few common ones: + +- `ui` — everything related to UI display: UI components, date formatters, styles, etc. +- `api` — backend interactions: request functions, data types, mappers, etc. +- `model` — the data model: schemas, interfaces, stores, and business logic. +- `lib` — library code that other modules on this slice need. +- `config` — configuration files and feature flags. + +You can create your own segments, too, if you need. Make sure not to create segments that group code by what it is, like `components`, `actions`, `types`, `utils`. Instead, group the code by what it's for. + +Reorganize your pages to separate code by segments. You should already have a `ui` segment, now it's time to create other segments, like `model` for your actions, reducers, and selectors, or `api` for your thunks and mutations. + +Also reorganize the Shared layer to remove these folders: +- `📁 components`, `📁 containers` — most of it should become `📁 shared/ui`; +- `📁 helpers`, `📁 utils` — if there are some reused helpers left, group them together by function, like dates or type conversions, and move theses groups to `📁 shared/lib`; +- `📁 constants` — again, group by function and move to `📁 shared/config`. + +## Optional steps \{#optional-steps\} + +### Step 6. Form entities/features from Redux slices that are used on several pages \{#form-entities-features-from-redux\} + +Usually, these reused Redux slices will describe something relevant to the business, for example, products or users, so these can be moved to the Entities layer, one entity per one folder. If the Redux slice is related to an action that your users want to do in your app, like comments, then you can move it to the Features layer. + +Entities and features are meant to be independent from each other. If your business domain contains inherent connections between entities, refer to the [guide on business entities][business-entities-cross-relations] for advice on how to organize these connections. + +The API functions related to these slices can stay in `📁 shared/api`. + +### Step 7. Refactor your modules \{#refactor-your-modules\} + +The `📁 modules` folder is commonly used for business logic, so it's already pretty similar in nature to the Features layer from FSD. Some modules might also be describe large chunks of the UI, like an app header. In that case, you should migrate them to the Widgets layer. + +### Step 8. Form a clean UI foundation in `shared/ui` \{#form-clean-ui-foundation\} + +`📁 shared/ui` should ideally contain a set of UI elements that don't have any business logic encoded in them. They should also be highly reusable. + +Refactor the UI components that used to be in `📁 components` and `📁 containers` to separate out the business logic. Move that business logic to the higher layers. If it's not used in too many places, you could even consider copy-pasting. + +## See also \{#see-also\} + +- [(Talk in Russian) Ilya Klimov — Крысиные бега бесконечного рефакторинга: как не дать техническому долгу убить мотивацию и продукт](https://youtu.be/aOiJ3k2UvO4) + +[ext-steiger]: https://github.com/feature-sliced/steiger +[business-entities-cross-relations]: /docs/guides/examples/types#business-entities-and-their-cross-references diff --git a/src/content/docs/guides/migration/from-v1.mdx b/src/content/docs/guides/migration/from-v1.mdx new file mode 100644 index 0000000000..1e5dc9e6f2 --- /dev/null +++ b/src/content/docs/guides/migration/from-v1.mdx @@ -0,0 +1,175 @@ +--- +title: Migration from v1 to v2 +sidebar: + order: 2 +--- + +import { Aside } from '@astrojs/starlight/components'; + +## Why v2? + +The original concept of **feature-slices** [was announced][ext-tg-spb] in 2018. + +Since then, many transformations of the methodology have taken place, but at the same time **[the basic principles were preserved][ext-v1]**: + +- Using a *standardized* frontend project structure +- Splitting the application in the first place-according to *business logic* +- Use of *isolated features* to prevent implicit side effects and cyclic dependencies +- Using the *Public API* with a ban on climbing "into the insides" of the module + +At the same time, in the previous version of the methodology, there were still **weak points** that + +- Sometimes it leads to boilerplate code +- Sometimes it leads to excessive complication of the code base and non-obvious rules between abstractions +- Sometimes it leads to implicit architectural solutions, which prevented the project from being pulled up and new people from onboarding + +The new version of the methodology ([v2][ext-v2]) is designed **to eliminate these shortcomings, while preserving the existing advantages** of the approach. + +Since 2018, [has also developed][ext-fdd-issues] another similar methodology - [**feature-driven**][ext-fdd], which was first announced by [Oleg Isonen][ext-kof]. + +After merging of the two approaches, we have **improved and refined existing practices** - towards greater flexibility, clarity and efficiency in application. + +> As a result, this has even affected the name of the methodology - *"feature-slice**d**"* + +## Why does it make sense to migrate the project to v2? + + + +#### 🔍 More transparent and simple architecture + +The methodology (v2) offers **more intuitive and more common abstractions and ways of separating logic among developers.** + +All this has an extremely positive effect on attracting new people, as well as studying the current state of the project, and distributing the business logic of the application. + +#### 📦 More flexible and honest modularity + +The methodology (v2) allows **to distribute logic in a more flexible way:** + +- With the ability to refactor isolated parts from scratch +- With the ability to rely on the same abstractions, but without unnecessary interweaving of dependencies +- With simpler requirements for the location of the new module *(layer => slice => segment)* + +#### 🚀 More specifications, plans, community + +At the moment, the `core-team` is actively working on the latest (v2) version of the methodology + +So it is for her: + +- there will be more described cases / problems +- there will be more guides on the application +- there will be more real examples +- in general, there will be more documentation for onboarding new people and studying the concepts of the methodology +- the toolkit will be developed in the future to comply with the concepts and conventions on architecture + +> Of course, there will be user support for the first version as well - but the latest version is still a priority for us +> +> In the future, with the next major updates, you will still have access to the current version (v2) of the methodology, **without risks for your teams and projects** + +## Changelog + +### `BREAKING` Layers + +Now the methodology assumes explicit allocation of layers at the top level + +- `/app` > `/processes` > **`/pages`** > **`/features`** > `/entities` > `/shared` +- *That is, not everything is now treated as features/pages* +- This approach allows you to [explicitly set rules for layers][ext-tg-v2-draft]: +- The **higher the layer** of the module is located , the more **context** it has + + *(in other words-each module of the layer - can import only the modules of the underlying layers, but not higher)* + +- The **lower the layer of the** module is located , the more **danger and responsibility** to make changes to it + + *(because it is usually the underlying layers that are more overused)* + +### `BREAKING` Shared + +The infrastructure abstractions `/ui`, `/lib`, `/api`, which used to lie in the src root of the project, are now separated by a separate directory `/src/shared` + +- `shared/ui` - Still the same general uikit of the application (optional) + - *At the same time, no one forbids using `Atomic Design` here as before* +- `shared/lib` - A set of auxiliary libraries for implementing logic + - *Still - without a dump of helpers* +- `shared/api` - A common entry point for accessing the API + - *Can also be registered locally in each feature / page - but it is not recommended* +- As before - there should be no explicit binding to business logic in `shared` + - *If necessary, you need to take this relationship to the `entities` level or even higher* + +### `NEW` Entities, Processes + +In v2 **, other new abstractions** have been added to eliminate the problems of logic complexity and high coupling. + +- `/entities` - layer **business entities** containing slices that are related directly to the business models or synthetic entities required only on frontend + - *Examples: `user`, `i18n`, `order`, `blog`* +- `/processes` - layer **business processes**, penetrating app + - **The layer is optional**, it is usually recommended to use it when *the logic grows and begins to blur in several pages* + - *Examples: `payment`, `auth`, `quick-tour`* + +### `BREAKING` Abstractions & Naming + +Now specific abstractions and [clear recommendations for naming them][refs-adaptability]are defined + +[disc-process]: https://github.com/feature-sliced/documentation/discussions/20 +[disc-features]: https://github.com/feature-sliced/documentation/discussions/23 +[disc-entities]: https://github.com/feature-sliced/documentation/discussions/18#discussioncomment-422649 +[disc-shared]: https://github.com/feature-sliced/documentation/discussions/31#discussioncomment-453020 + +[disc-ui]: https://github.com/feature-sliced/documentation/discussions/31#discussioncomment-453132 +[disc-model]: https://github.com/feature-sliced/documentation/discussions/31#discussioncomment-472645 +[disc-api]: https://github.com/feature-sliced/documentation/discussions/66 + +#### Layers + +- `/app` — **application initialization layer** + - *Previous versions: `app`, `core`,`init`, `src/index` (and this happens)* +- `/processes` — [**business process layer**][disc-process] + - *Previous versions: `processes`, `flows`, `workflows`* +- `/pages` — **application page layer** + - *Previous versions: `pages`, `screens`, `views`, `layouts`, `components`, `containers`* +- `/features` — [**functionality parts layer**][disc-features] + - *Previous versions: `features`, `components`, `containers`* +- `/entities` — [**business entity layer**][disc-entities] + - *Previous versions: `entities`, `models`, `shared`* +- `/shared` — [**layer of reused infrastructure code**][disc-shared] 🔥 + - *Previous versions: `shared`, `common`, `lib`* + +#### Segments + +- `/ui` — [**UI segment**][disc-ui] 🔥 + - *Previous versions: `ui`, `components`, `view`* +- `/model` — [**BL-segment**][disc-model] 🔥 + - *Previous versions: `model`, `store`, `state`, `services`, `controller`* +- `/lib` — segment **of auxiliary code** + - *Previous versions: `lib`, `libs`, `utils`, `helpers`* +- `/api` — [**API segment**][disc-api] + - *Previous versions: `api`, `service`, `requests`, `queries`* +- `/config` — **application configuration segment** + - *Previous versions: `config`, `env`, `get-env`* + +### `REFINED` Low coupling + +Now it is much easier to [observe the principle of low coupling][refs-low-coupling] between modules, thanks to the new layers. + +*At the same time, it is still recommended to avoid as much as possible cases where it is extremely difficult to "uncouple" modules* + +## See also + +- [Notes from the report "React SPB Meetup #1"][ext-tg-spb] +- [React Berlin Talk - Oleg Isonen "Feature Driven Architecture"][ext-kof-fdd] +- [Comparison with v1 (community-chat)](https://t.me/feature_sliced/493) +- [New ideas v2 with explanations (atomicdesign-chat)][ext-tg-v2-draft] +- [Discussion of abstractions and naming for the new version of the methodology (v2)](https://github.com/feature-sliced/documentation/discussions/31) + +[refs-low-coupling]: /docs/reference/slices-segments#zero-coupling-high-cohesion +[refs-adaptability]: /docs/about/understanding/naming + +[ext-v1]: https://feature-sliced.github.io/featureslices.dev/v1.0.html +[ext-tg-spb]: https://t.me/feature_slices +[ext-fdd]: https://github.com/feature-sliced/documentation/tree/rc/feature-driven +[ext-fdd-issues]: https://github.com/kof/feature-driven-architecture/issues +[ext-v2]: https://github.com/feature-sliced/documentation +[ext-kof]: https://github.com/kof +[ext-kof-fdd]: https://www.youtube.com/watch?v=BWAeYuWFHhs +[ext-tg-v2-draft]: https://t.me/atomicdesign/18708 diff --git a/src/content/docs/guides/migration/from-v2-0.mdx b/src/content/docs/guides/migration/from-v2-0.mdx new file mode 100644 index 0000000000..aeef2f5046 --- /dev/null +++ b/src/content/docs/guides/migration/from-v2-0.mdx @@ -0,0 +1,45 @@ +--- +title: Migration from v2.0 to v2.1 +sidebar: + order: 3 +--- + +The main change in v2.1 is the new mental model for decomposing an interface — pages first. + +In v2.0, FSD would recommend identifying entities and features in your interface, considering even the smallest bits of entity representation and interactivity for decomposition. Then you would build widgets and pages from entities and features. In this model of decomposition, most of the logic was in entities and features, and pages were just compositional layers that didn't have much significance on their own. + +In v2.1, we recommend starting with pages, and possibly even stopping there. Most people already know how to separate the app into individual pages, and pages are also a common starting point when trying to locate a component in the codebase. In this new model of decomposition, you keep most of the UI and logic in each individual page, maintaining a reusable foundation in Shared. If a need arises to reuse business logic across several pages, you can move it to a layer below. + +Another addition to Feature-Sliced Design is the standardization of cross-imports between entities with the `@x`-notation. + +## How to migrate \{#how-to-migrate\} + +There are no breaking changes in v2.1, which means that a project written with FSD v2.0 is also a valid project in FSD v2.1. However, we believe that the new mental model is more beneficial for teams and especially onboarding new developers, so we recommend making minor adjustments to your decomposition. + +### Merge slices + +A simple way to start is by running our linter, [Steiger][steiger], on the project. Steiger is built with the new mental model, and the most helpful rules will be: + +- [`insignificant-slice`][insignificant-slice] — if an entity or feature is only used in one page, this rule will suggest merging that entity or feature into the page entirely. +- [`excessive-slicing`][excessive-slicing] — if a layer has too many slices, it's usually a sign that the decomposition is too fine-grained. This rule will suggest merging or grouping some slices to help project navigation. + +```bash +npx steiger src +``` + +This will help you identify which slices are only used once, so that you could reconsider if they are really necessary. In such considerations, keep in mind that a layer forms some kind of global namespace for all the slices inside of it. Just as you wouldn't pollute the global namespace with variables that are only used once, you should treat a place in the namespace of a layer as valuable, to be used sparingly. + +### Standardize cross-imports + +If you had cross-imports between in your project before (we don't judge!), you may now take advantage of a new notation for cross-importing in Feature-Sliced Design — the `@x`-notation. It looks like this: + +```ts title="entities/B/some/file.ts" +import type { EntityA } from "entities/A/@x/B"; +``` + +For more details, check out the [Public API for cross-imports][public-api-for-cross-imports] section in the reference. + +[insignificant-slice]: https://github.com/feature-sliced/steiger/tree/master/packages/steiger-plugin-fsd/src/insignificant-slice +[steiger]: https://github.com/feature-sliced/steiger +[excessive-slicing]: https://github.com/feature-sliced/steiger/tree/master/packages/steiger-plugin-fsd/src/excessive-slicing +[public-api-for-cross-imports]: /docs/reference/public-api#public-api-for-cross-imports From 9d2a585d6a2c49f63fcffa486d95ab7e5864fd93 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 22:44:16 +0100 Subject: [PATCH 15/45] docs: migrate tech category --- astro.config.mjs | 12 +- .../docs/guides/tech/with-electron.mdx | 137 ++++++ src/content/docs/guides/tech/with-nextjs.mdx | 196 ++++++++ src/content/docs/guides/tech/with-nuxtjs.mdx | 181 +++++++ .../docs/guides/tech/with-react-query.mdx | 440 ++++++++++++++++++ .../docs/guides/tech/with-sveltekit.mdx | 100 ++++ 6 files changed, 1062 insertions(+), 4 deletions(-) create mode 100644 src/content/docs/guides/tech/with-electron.mdx create mode 100644 src/content/docs/guides/tech/with-nextjs.mdx create mode 100644 src/content/docs/guides/tech/with-nuxtjs.mdx create mode 100644 src/content/docs/guides/tech/with-react-query.mdx create mode 100644 src/content/docs/guides/tech/with-sveltekit.mdx diff --git a/astro.config.mjs b/astro.config.mjs index ede7cd175f..56c031a266 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -34,12 +34,16 @@ export default defineConfig({ label: 'Examples', autogenerate: { directory: 'guides/examples' } }, + { + label: 'Migration', + autogenerate: { directory: 'guides/migration' } + }, + { + label: 'Tech', + autogenerate: { directory: 'guides/tech' } + } ], }, - { - label: 'Migration', - autogenerate: { directory: 'guides/migration' } - } ], }), ], diff --git a/src/content/docs/guides/tech/with-electron.mdx b/src/content/docs/guides/tech/with-electron.mdx new file mode 100644 index 0000000000..1b1dff7daf --- /dev/null +++ b/src/content/docs/guides/tech/with-electron.mdx @@ -0,0 +1,137 @@ +--- +title: Usage with Electron +sidebar: + order: 10 +--- + +import { FileTree } from '@astrojs/starlight/components'; + +Electron applications have a special architecture consisting of multiple processes with different responsibilities. Applying FSD in such a context requires adapting the structure to the Electron specifics. + + +- src + - app Common app layer + - main Main process + - index.ts Main process entry point + - preload Preload script and Context Bridge + - index.ts Preload entry point + - renderer Renderer process + - index.html Renderer process entry point + - main + - features + - user + - ipc + - get-user.ts + - send-user.ts + - entities + - shared + - renderer + - pages + - settings + - ipc + - get-user.ts + - save-user.ts + - ui + - user.tsx + - index.ts + - home + - ui + - home.tsx + - index.ts + - widgets + - features + - entities + - shared + - shared Common code between main and renderer + - ipc IPC description (event names, contracts) + + +## Public API rules +Each process must have its own public API. For example, you can't import modules from `main` to `renderer`. +Only the `src/shared` folder is public for both processes. +It's also necessary for describing contracts for process interaction. + +## Additional changes to the standard structure +It's suggested to use a new `ipc` segment, where interaction between processes takes place. +The `pages` and `widgets` layers, based on their names, should not be present in `src/main`. You can use `features`, `entities` and `shared`. +The `app` layer in `src` contains entry points for `main` and `renderer`, as well as the IPC. +It's not desirable for segments in the `app` layer to have intersection points + +## Interaction example + +```typescript title="src/shared/ipc/channels.ts" +export const CHANNELS = { + GET_USER_DATA: 'GET_USER_DATA', + SAVE_USER: 'SAVE_USER', +} as const; + +export type TChannelKeys = keyof typeof CHANNELS; +``` + +```typescript title="src/shared/ipc/events.ts" +import { CHANNELS } from './channels'; + +export interface IEvents { + [CHANNELS.GET_USER_DATA]: { + args: void, + response?: { name: string; email: string; }; + }; + [CHANNELS.SAVE_USER]: { + args: { name: string; }; + response: void; + }; +} +``` + +```typescript title="src/shared/ipc/preload.ts" +import { CHANNELS } from './channels'; +import type { IEvents } from './events'; + +type TOptionalArgs = T extends void ? [] : [args: T]; + +export type TElectronAPI = { + [K in keyof typeof CHANNELS]: (...args: TOptionalArgs) => IEvents[typeof CHANNELS[K]]['response']; +}; +``` + +```typescript title="src/app/preload/index.ts" +import { contextBridge, ipcRenderer } from 'electron'; +import { CHANNELS, type TElectronAPI } from 'shared/ipc'; + +const API: TElectronAPI = { + [CHANNELS.GET_USER_DATA]: () => ipcRenderer.sendSync(CHANNELS.GET_USER_DATA), + [CHANNELS.SAVE_USER]: args => ipcRenderer.invoke(CHANNELS.SAVE_USER, args), +} as const; + +contextBridge.exposeInMainWorld('electron', API); +``` + +```typescript title="src/main/features/user/ipc/send-user.ts" +import { ipcMain } from 'electron'; +import { CHANNELS } from 'shared/ipc'; + +export const sendUser = () => { + ipcMain.on(CHANNELS.GET_USER_DATA, ev => { + ev.returnValue = { + name: 'John Doe', + email: 'john.doe@example.com', + }; + }); +}; +``` + +```typescript title="src/renderer/pages/user-settings/ipc/get-user.ts" +import { CHANNELS } from 'shared/ipc'; + +export const getUser = () => { + const user = window.electron[CHANNELS.GET_USER_DATA](); + + return user ?? { name: 'John Donte', email: 'john.donte@example.com' }; +}; +``` + +## See also +- [Process Model Documentation](https://www.electronjs.org/docs/latest/tutorial/process-model) +- [Context Isolation Documentation](https://www.electronjs.org/docs/latest/tutorial/context-isolation) +- [Inter-Process Communication Documentation](https://www.electronjs.org/docs/latest/tutorial/ipc) +- [Example](https://github.com/feature-sliced/examples/tree/master/examples/electron) diff --git a/src/content/docs/guides/tech/with-nextjs.mdx b/src/content/docs/guides/tech/with-nextjs.mdx new file mode 100644 index 0000000000..c2e6f088d1 --- /dev/null +++ b/src/content/docs/guides/tech/with-nextjs.mdx @@ -0,0 +1,196 @@ +--- +title: Usage with Next.js +sidebar: + order: 1 +--- + +import { Tabs, TabItem, FileTree } from '@astrojs/starlight/components'; + +FSD is compatible with Next.js in both the App Router version and the Pages Router version if you solve the main conflict — the `app` and `pages` folders. + +## App Router \{#app-router\} + +### Conflict between FSD and Next.js in the `app` layer \{#conflict-between-fsd-and-nextjs-in-the-app-layer\} + +Next.js suggests using the `app` folder to define application routes. It expects files in the `app` folder to correspond to pathnames. This routing mechanism **does not align** with the FSD concept, as it's not possible to maintain a flat slice structure. + +The solution is to move the Next.js `app` folder to the project root and import FSD pages from `src`, where the FSD layers are, into the Next.js `app` folder. + +You will also need to add a `pages` folder to the project root, otherwise Next.js will try to use `src/pages` as the Pages Router even if you use the App Router, which will break the build. It's also a good idea to put a `README.md` file inside this root `pages` folder describing why it is necessary, even though it's empty. + + +- app App folder (Next.js) + - api + - get-example + - route.ts + - example + - page.tsx +- pages Empty pages folder (Next.js) + - README.md +- src + - app + - api-routes API routes + - pages + - example + - index.ts + - ui + - example.tsx + - widgets/ + - features/ + - entities/ + - shared/ + + +Example of re-exporting a page from `src/pages` in the Next.js `app`: + +```tsx title="app/example/page.tsx" +export { ExamplePage as default, metadata } from '@/pages/example'; +``` + +### Middleware \{#middleware\} + +If you use middleware in your project, it must be located in the project root alongside the Next.js `app` and `pages` folders. + +### Instrumentation \{#instrumentation\} + +The `instrumentation.js` file allows you to monitor the performance and behavior of your application. If you use it, it must be located in the project root, similar to `middleware.js`. + +## Pages Router \{#pages-router\} + +### Conflict between FSD and Next.js in the `pages` layer \{#conflict-between-fsd-and-nextjs-in-the-pages-layer\} + +Routes should be placed in the `pages` folder in the root of the project, similar to `app` folder for the App Router. The structure inside `src` where the layer folders are located remains unchanged. + + +- pages Pages folder (Next.js) + - _app.tsx + - api + - example.ts API route re-export + - example + - index.tsx +- src + - app + - custom-app + - custom-app.tsx Custom App component + - api-routes + - get-example-data.ts API route + - pages + - example + - index.ts + - ui + - example.tsx + - widgets/ + - features/ + - entities/ + - shared/ + + +Example of re-exporting a page from `src/pages` in the Next.js `pages`: + +```tsx title="pages/example/index.tsx" +export { Example as default } from '@/pages/example'; +``` + +### Custom `_app` component \{#custom-_app-component\} + +You can place your Custom App component in `src/app/_app` or `src/app/custom-app`: + +```tsx title="src/app/custom-app/custom-app.tsx" +import type { AppProps } from 'next/app'; + +export const MyApp = ({ Component, pageProps }: AppProps) => { + return ( + <> +

My Custom App component

+ + + ); +}; +``` + +```tsx title="pages/_app.tsx" +export { App as default } from '@/app/custom-app'; +``` + +## Route Handlers (API routes) \{#route-handlers-api-routes\} + +Use the `api-routes` segment in the `app` layer to work with Route Handlers. + +Be mindful when writing backend code in the FSD structure — FSD is primarily intended for frontends, meaning that's what people will expect to find. +If you need a lot of endpoints, consider separating them into a different package in a monorepo. + + + + + +```tsx title="src/app/api-routes/get-example-data.ts" +import { getExamplesList } from '@/shared/db'; + +export const getExampleData = () => { + try { + const examplesList = getExamplesList(); + + return Response.json({ examplesList }); + } catch { + return Response.json(null, { + status: 500, + statusText: 'Ouch, something went wrong', + }); + } +}; +``` + +```tsx title="app/api/example/route.ts" +export { getExampleData as GET } from '@/app/api-routes'; +``` + + + + + +```tsx title="src/app/api-routes/get-example-data.ts" +import type { NextApiRequest, NextApiResponse } from 'next'; + +const config = { + api: { + bodyParser: { + sizeLimit: '1mb', + }, + }, + maxDuration: 5, +}; + +const handler = (req: NextApiRequest, res: NextApiResponse) => { + res.status(200).json({ message: 'Hello from FSD' }); +}; + +export const getExampleData = { config, handler } as const; +``` + +```tsx title="src/app/api-routes/index.ts" +export { getExampleData } from './get-example-data'; +``` + +```tsx title="app/api/example.ts" +import { getExampleData } from '@/app/api-routes'; + +export const config = getExampleData.config; +export default getExampleData.handler; +``` + + + + + +## Additional recommendations \{#additional-recommendations\} + +- Use the `db` segment in the `shared` layer to describe database queries and their further use in higher layers. +- Caching and revalidating queries logic is better kept in the same place as the queries themselves. + +## See also \{#see-also\} + +- [Next.js Project Structure](https://nextjs.org/docs/app/getting-started/project-structure) +- [Next.js Page Layouts](https://nextjs.org/docs/app/getting-started/layouts-and-pages) + +[project-knowledge]: /docs/about/understanding/knowledge-types +[ext-app-router-stackblitz]: https://stackblitz.com/edit/stackblitz-starters-aiez55?file=README.md diff --git a/src/content/docs/guides/tech/with-nuxtjs.mdx b/src/content/docs/guides/tech/with-nuxtjs.mdx new file mode 100644 index 0000000000..0a8c0113a0 --- /dev/null +++ b/src/content/docs/guides/tech/with-nuxtjs.mdx @@ -0,0 +1,181 @@ +--- +title: Usage with NuxtJS +sidebar: + order: 10 +--- + +import { FileTree } from '@astrojs/starlight/components'; + +It is possible to implement FSD in a NuxtJS project, but conflicts arise due to the differences between NuxtJS project structure requirements and FSD principles: + +- Initially, NuxtJS offers a project file structure without a `src` folder, i.e. in the root of the project. +- The file routing is in the `pages` folder, while in FSD this folder is reserved for the flat slice structure. + + +## Adding an alias for the `src` directory + +Add an `alias` object to your config: +```ts title="nuxt.config.ts" +export default defineNuxtConfig({ + devtools: { enabled: true }, // Not FSD related, enabled at project startup + alias: { + "@": '../src' + }, +}) +``` +## Choose how to configure the router + +In NuxtJS, there are two ways to customize the routing - using a config and using a file structure. +In the case of file-based routing, you will create index.vue files in folders inside the app/routes directory, and in the case of configure, you will configure the routers in the `router.options.ts` file. + + +### Routing using config + +In the `app` layer, create a `router.options.ts` file, and export a config object from it: +```ts title="app/router.options.ts" +import type { RouterConfig } from '@nuxt/schema'; + +export default { + routes: (_routes) => [], +}; + +``` + +To add a `Home` page to your project, you need to do the following steps: +- Add a page slice inside the `pages` layer +- Add the appropriate route to the `app/router.config.ts` config + + +To create a page slice, let's use the [CLI](https://github.com/feature-sliced/cli): + +```shell +fsd pages home +``` + +Create a ``home-page.vue`` file inside the ui segment, access it using the Public API + +```ts title="src/pages/home/index.ts" +export { default as HomePage } from './ui/home-page'; +``` + +Thus, the file structure will look like this: + + +- src + - app + - router.config.ts + - pages + - home + - ui + - home-page.vue + - index.ts + +Finally, let's add a route to the config: + +```ts title="app/router.config.ts" +import type { RouterConfig } from '@nuxt/schema' + +export default { + routes: (_routes) => [ + { + name: 'home', + path: '/', + component: () => import('@/pages/home.vue').then(r => r.default || r) + } + ], +} +``` + +### File Routing + +First of all, create a `src` directory in the root of your project, and create app and pages layers inside this directory and a routes folder inside the app layer. +Thus, your file structure should look like this: + + +- src + - app + - routes + - pages Pages folder, related to FSD + + +In order for NuxtJS to use the routes folder inside the `app` layer for file routing, you need to modify `nuxt.config.ts` as follows: +```ts title="nuxt.config.ts" +export default defineNuxtConfig({ + devtools: { enabled: true }, // Not FSD related, enabled at project startup + alias: { + "@": '../src' + }, + dir: { + pages: './src/app/routes' + } +}) +``` + +Now, you can create routes for pages within `app` and connect pages from `pages` to them. + +For example, to add a `Home` page to your project, you need to do the following steps: +- Add a page slice inside the `pages` layer +- Add the corresponding route inside the `app` layer +- Connect the page from the slice with the route + +To create a page slice, let's use the [CLI](https://github.com/feature-sliced/cli): + +```shell +fsd pages home +``` + +Create a ``home-page.vue`` file inside the ui segment, access it using the Public API + +```ts title="src/pages/home/index.ts" +export { default as HomePage } from './ui/home-page'; +``` + +Create a route for this page inside the `app` layer: + + +- src + - app + - routes + - index.vue + - pages + - home + - ui + - home-page.vue + - index.ts + + +Add your page component inside the `index.vue` file: + +```html title="src/app/routes/index.vue" + + + +``` + +## What to do with `layouts`? + +You can place layouts inside the `app` layer, to do this you need to modify the config as follows: + +```ts title="nuxt.config.ts" +export default defineNuxtConfig({ + devtools: { enabled: true }, // Not related to FSD, enabled at project startup + alias: { + "@": '../src' + }, + dir: { + pages: './src/app/routes', + layouts: './src/app/layouts' + } +}) +``` + + +## See also + +- [Documentation on changing directory config in NuxtJS](https://nuxt.com/docs/api/nuxt-config#dir) +- [Documentation on changing router config in NuxtJS](https://nuxt.com/docs/guide/recipes/custom-routing#router-config) +- [Documentation on changing aliases in NuxtJS](https://nuxt.com/docs/api/nuxt-config#alias) diff --git a/src/content/docs/guides/tech/with-react-query.mdx b/src/content/docs/guides/tech/with-react-query.mdx new file mode 100644 index 0000000000..e1d0d24318 --- /dev/null +++ b/src/content/docs/guides/tech/with-react-query.mdx @@ -0,0 +1,440 @@ +--- +title: Usage with React Query +sidebar: + order: 10 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +## The problem of “where to put the keys” + +### Solution — break down by entities + +If the project already has a division into entities, and each request corresponds to a single entity, +the purest division will be by entity. In this case, we suggest using the following structure: + +- src/ + - app/ + - ... + - pages/ + - ... + - entities/ + - \{entity\}/ + - ... + - api/ + - `{entity}.query` Query-factory where are the keys and functions + - `get-{entity}` Entity getter function + - `create-{entity}` Entity creation function + - `update-{entity}` Entity update function + - `delete-{entity}` Entity delete function + - ... + - features/ + - ... + - widgets/ + - ... + - shared/ + - ... + + +If there are connections between the entities (for example, the Country entity has a field-list of City entities), +then you can use the [public API for cross-imports][public-api-for-cross-imports] or consider the alternative solution below. + +### Alternative solution — keep it in shared + +In cases where entity separation is not appropriate, the following structure can be considered: + + +- src/ + - ... + - shared/ + - api/ + - ... + - queries Query-factories + - document.ts + - background-jobs.ts + - ... + - index.ts + + +Then in `@/shared/api/index.ts`: + +```ts title="@/shared/api/index.ts" +export { documentQueries } from "./queries/document"; +``` + +## The problem of “Where to insert mutations?” + +It is not recommended to mix mutations with queries. There are two options: + +### 1. Define a custom hook in the `api` segment near the place of use + +```tsx title="@/features/update-post/api/use-update-title.ts" +export const useUpdateTitle = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, newTitle }) => + apiClient + .patch(`/posts/${id}`, { title: newTitle }) + .then((data) => console.log(data)), + + onSuccess: (newPost) => { + queryClient.setQueryData(postsQueries.ids(id), newPost); + }, + }); +}; +``` + +### 2. Define a mutation function somewhere else (Shared or Entities) and use `useMutation` directly in the component + +```tsx +const { mutateAsync, isPending } = useMutation({ + mutationFn: postApi.createPost, +}); +``` + +```tsx title="@/pages/post-create/ui/post-create-page.tsx" +export const CreatePost = () => { + const { classes } = useStyles(); + const [title, setTitle] = useState(""); + + const { mutate, isPending } = useMutation({ + mutationFn: postApi.createPost, + }); + + const handleChange = (e: ChangeEvent) => + setTitle(e.target.value); + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + mutate({ title, userId: DEFAULT_USER_ID }); + }; + + return ( +
+ + + Create + + + ); +}; +``` + +## Organization of requests + +### Query factory + +A query factory is an object where the key values are functions that return a list of query keys. Here's how to use it: + +```tsx +const keyFactory = { + all: () => ["entity"], + lists: () => [...postQueries.all(), "list"], +}; +``` + + + +### 1. Creating a Query Factory + +```tsx title="@/entities/post/api/post.queries.ts" +import { keepPreviousData, queryOptions } from "@tanstack/react-query"; +import { getPosts } from "./get-posts"; +import { getDetailPost } from "./get-detail-post"; +import { PostDetailQuery } from "./query/post.query"; + +export const postQueries = { + all: () => ["posts"], + + lists: () => [...postQueries.all(), "list"], + list: (page: number, limit: number) => + queryOptions({ + queryKey: [...postQueries.lists(), page, limit], + queryFn: () => getPosts(page, limit), + placeholderData: keepPreviousData, + }), + + details: () => [...postQueries.all(), "detail"], + detail: (query?: PostDetailQuery) => + queryOptions({ + queryKey: [...postQueries.details(), query?.id], + queryFn: () => getDetailPost({ id: query?.id }), + staleTime: 5000, + }), +}; +``` + +### 2. Using Query Factory in application code +```tsx +import { useParams } from "react-router-dom"; +import { postApi } from "@/entities/post"; +import { useQuery } from "@tanstack/react-query"; + +type Params = { + postId: string; +}; + +export const PostPage = () => { + const { postId } = useParams(); + const id = parseInt(postId || ""); + const { + data: post, + error, + isLoading, + isError, + } = useQuery(postApi.postQueries.detail({ id })); + + if (isLoading) { + return
Loading...
; + } + + if (isError || !post) { + return <>{error?.message}; + } + + return ( +
+

Post id: {post.id}

+
+

{post.title}

+
+

{post.body}

+
+
+
Owner: {post.userId}
+
+ ); +}; +``` + +### Benefits of using a Query Factory +- **Request structuring:** A factory allows you to organize all API requests in one place, making your code more readable and maintainable. +- **Convenient access to queries and keys:** The factory provides convenient methods for accessing different types of queries and their keys. +- **Query Refetching Ability:** The factory allows easy refetching without the need to change query keys in different parts of the application. + +## Pagination + +In this section, we'll look at an example of the `getPosts` function, which makes an API request to retrieve post entities using pagination. + +### 1. Creating a function `getPosts` +The getPosts function is located in the `get-posts.ts` file, which is located in the `api` segment + +```tsx title="@/pages/post-feed/api/get-posts.ts" +import { apiClient } from "@/shared/api/base"; + +import { PostWithPaginationDto } from "./dto/post-with-pagination.dto"; +import { PostQuery } from "./query/post.query"; +import { mapPost } from "./mapper/map-post"; +import { PostWithPagination } from "../model/post-with-pagination"; + +const calculatePostPage = (totalCount: number, limit: number) => + Math.floor(totalCount / limit); + +export const getPosts = async ( + page: number, + limit: number, +): Promise => { + const skip = page * limit; + const query: PostQuery = { skip, limit }; + const result = await apiClient.get("/posts", query); + + return { + posts: result.posts.map((post) => mapPost(post)), + limit: result.limit, + skip: result.skip, + total: result.total, + totalPages: calculatePostPage(result.total, limit), + }; +}; +``` + +### 2. Query factory for pagination +The `postQueries` query factory defines various query options for working with posts, +including requesting a list of posts with a specific page and limit. + +```tsx +import { keepPreviousData, queryOptions } from "@tanstack/react-query"; +import { getPosts } from "./get-posts"; + +export const postQueries = { + all: () => ["posts"], + lists: () => [...postQueries.all(), "list"], + list: (page: number, limit: number) => + queryOptions({ + queryKey: [...postQueries.lists(), page, limit], + queryFn: () => getPosts(page, limit), + placeholderData: keepPreviousData, + }), +}; +``` + + +### 3. Use in application code + +```tsx title="@/pages/home/ui/index.tsx" +export const HomePage = () => { + const itemsOnScreen = DEFAULT_ITEMS_ON_SCREEN; + const [page, setPage] = usePageParam(DEFAULT_PAGE); + const { data, isFetching, isLoading } = useQuery( + postApi.postQueries.list(page, itemsOnScreen), + ); + return ( + <> + setPage(page)} + page={page} + count={data?.totalPages} + variant="outlined" + color="primary" + /> + + + ); +}; +``` + + +## `QueryProvider` for managing queries +In this guide, we will look at how to organize a `QueryProvider`. + +### 1. Creating a `QueryProvider` +The file `query-provider.tsx` is located at the path `@/app/providers/query-provider.tsx`. + +```tsx title="@/app/providers/query-provider.tsx" +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { ReactNode } from "react"; + +type Props = { + children: ReactNode; + client: QueryClient; +}; + +export const QueryProvider = ({ client, children }: Props) => { + return ( + + {children} + + + ); +}; +``` + +### 2. Creating a `QueryClient` +`QueryClient` is an instance used to manage API requests. +The `query-client.ts` file is located at `@/shared/api/query-client.ts`. +`QueryClient` is created with certain settings for query caching. + +```tsx title="@/shared/api/query-client.ts" +import { QueryClient } from "@tanstack/react-query"; + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, + gcTime: 5 * 60 * 1000, + }, + }, +}); +``` + +## Code generation + +There are tools that can generate API code for you, but they are less flexible than the manual approach described above. +If your Swagger file is well-structured, +and you're using one of these tools, it might make sense to generate all the code in the `@/shared/api` directory. + + +## Additional advice for organizing RQ +### API Client + +Using a custom API client class in the shared layer, +you can standardize the configuration and work with the API in the project. +This allows you to manage logging, +headers and data exchange format (such as JSON or XML) from one place. +This approach makes it easier to maintain and develop the project because it simplifies changes and updates to interactions with the API. + +```tsx title="@/shared/api/api-client.ts" +import { API_URL } from "@/shared/config"; + +export class ApiClient { + private baseUrl: string; + + constructor(url: string) { + this.baseUrl = url; + } + + async handleResponse(response: Response): Promise { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + try { + return await response.json(); + } catch (error) { + throw new Error("Error parsing JSON response"); + } + } + + public async get( + endpoint: string, + queryParams?: Record, + ): Promise { + const url = new URL(endpoint, this.baseUrl); + + if (queryParams) { + Object.entries(queryParams).forEach(([key, value]) => { + url.searchParams.append(key, value.toString()); + }); + } + const response = await fetch(url.toString(), { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + return this.handleResponse(response); + } + + public async post>( + endpoint: string, + body: TData, + ): Promise { + const response = await fetch(`${this.baseUrl}${endpoint}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + return this.handleResponse(response); + } +} + +export const apiClient = new ApiClient(API_URL); +``` + +## See also \{#see-also\} + +- [(GitHub) Sample Project](https://github.com/ruslan4432013/fsd-react-query-example) +- [(CodeSandbox) Sample Project](https://codesandbox.io/p/github/ruslan4432013/fsd-react-query-example/main) +- [About the query factory](https://tkdodo.eu/blog/the-query-options-api) + +[public-api-for-cross-imports]: /docs/reference/public-api#public-api-for-cross-imports diff --git a/src/content/docs/guides/tech/with-sveltekit.mdx b/src/content/docs/guides/tech/with-sveltekit.mdx new file mode 100644 index 0000000000..231825dab0 --- /dev/null +++ b/src/content/docs/guides/tech/with-sveltekit.mdx @@ -0,0 +1,100 @@ +--- +title: Usage with SvelteKit +sidebar: + order: 10 +--- + +import { FileTree } from '@astrojs/starlight/components'; + +It is possible to implement FSD in a SvelteKit project, but conflicts arise due to the differences between the structure requirements of a SvelteKit project and the principles of FSD: + +- Initially, SvelteKit offers a file structure inside the `src/routes` folder, while in FSD the routing must be part of the `app` layer. +- SvelteKit suggests putting everything not related to routing in the `src/lib` folder. + + +## Let's set up the config + +```ts title="svelte.config.ts" +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config}*/ +const config = { + preprocess: [vitePreprocess()], + kit: { + adapter: adapter(), + files: { + routes: 'src/app/routes', // move routing inside the app layer + lib: 'src', + appTemplate: 'src/app/index.html', // Move the application entry point inside the app layer + assets: 'public' + }, + alias: { + '@/*': 'src/*' // Create an alias for the src directory + } + } +}; +export default config; +``` + +## Move file routing to `src/app`. + +Let's create an app layer, move the app's entry point `index.html` into it, and create a routes folder. +Thus, your file structure should look like this: + + +- src + - app + - index.html + - routes + - pages FSD Pages folder + + +Now, you can create routes for pages within `app` and connect pages from `pages` to them. + +For example, to add a home page to your project, you need to do the following steps: +- Add a page slice inside the `pages` layer +- Add the corresponding rooute to the `routes` folder from the `app` layer +- Align the page from the slice with the rooute + +To create a page slice, let's use the [CLI](https://github.com/feature-sliced/cli): + +```shell +fsd pages home +``` + +Create a ``home-page.svelte`` file inside the ui segment, access it using the Public API + +```ts title="src/pages/home/index.ts" +export { default as HomePage } from './ui/home-page.svelte'; +``` + +Create a route for this page inside the `app` layer: + + +- src + - app + - routes + - +page.svelte + - index.html + - pages + - home + - ui + - home-page.svelte + - index.ts + + +Add your page component inside the `+page.svelte` file: + +```html title="src/app/routes/+page.svelte" + + + + +``` + +## See also. + +- [Documentation on changing directory config in SvelteKit](https://kit.svelte.dev/docs/configuration#files) From df2c27ac679ee97c81f6e88a433dad68b9fa6e46 Mon Sep 17 00:00:00 2001 From: Solant Date: Thu, 5 Feb 2026 22:51:58 +0100 Subject: [PATCH 16/45] docs: migrate issues --- .../docs/guides/issues/cross-imports.mdx | 266 ++++++++++++++++++ .../docs/guides/issues/desegmented.mdx | 147 ++++++++++ .../docs/guides/issues/excessive-entities.mdx | 120 ++++++++ 3 files changed, 533 insertions(+) create mode 100644 src/content/docs/guides/issues/cross-imports.mdx create mode 100644 src/content/docs/guides/issues/desegmented.mdx create mode 100644 src/content/docs/guides/issues/excessive-entities.mdx diff --git a/src/content/docs/guides/issues/cross-imports.mdx b/src/content/docs/guides/issues/cross-imports.mdx new file mode 100644 index 0000000000..f5e41c18b3 --- /dev/null +++ b/src/content/docs/guides/issues/cross-imports.mdx @@ -0,0 +1,266 @@ +--- +title: Cross-imports +sidebar: + order: 4 +--- + +import { Aside } from '@astrojs/starlight/components'; + +A **cross-import** is an import **between different slices within the same layer**. + +For example: +- importing `features/product` from `features/cart` +- importing `widgets/sidebar` from `widgets/header` + +Cross-imports are a code smell: a warning sign that slices are becoming coupled. In some situations they may be hard to avoid, but they should always be deliberate and either documented or shared within the team/project. + + + +## Why is this a code smell? + +Cross-imports are not just a matter of style—they are generally considered a **code smell** because they blur the boundaries between domains and introduce implicit dependencies. + +Consider a case where the `cart` slice directly depends on `product` business logic. At first glance, this might seem convenient. However, this creates several problems: + +1. **Unclear ownership and responsibility.** When `cart` imports from `product`, it becomes unclear which slice "owns" the shared logic. If the `product` team changes their internal implementation, they might unknowingly break `cart`. This ambiguity makes it harder to reason about the codebase and assign responsibility for bugs or features. + +2. **Reduced isolation and testability.** One of the main benefits of sliced architecture is that each slice can be developed, tested, and deployed independently. Cross-imports break this isolation—testing `cart` now requires setting up `product` as well, and changes in one slice can cause unexpected test failures in another. + +3. **Increased cognitive load.** Working on `cart` also requires accounting for how `product` is structured and how it behaves. As cross-imports accumulate, tracing the impact of a change requires following more code across slice boundaries, and even small edits demand more context to be held in mind. + +4. **Path to circular dependencies.** Cross-imports often start as one-way dependencies but can evolve into bidirectional ones (A imports B, B imports A). This tends to lock slices together, making dependencies harder to untangle and increasing refactoring cost over time. + +The purpose of clear domain boundaries is to keep each slice focused and changeable within its own responsibility. When dependencies are loose, it becomes easier to predict the impact of a change and to keep review and testing scope contained. Cross-imports weaken this separation, expanding the impact of changes and increasing refactoring cost over time—this is why they are treated as a code smell worth addressing. + +In the sections below, we outline how these issues typically appear in real projects and what strategies you can use to address them. + +## Entities layer cross-imports + +Cross-imports in `entities` are often caused by splitting entities too granularly. Before reaching for `@x`, consider whether the boundaries should be merged instead. +Some teams use `@x` as a dedicated cross-import surface for `entities`, but it should be treated as a **last resort** — a **necessary compromise**, not a recommended approach. + +Think of `@x` as an explicit gateway for unavoidable domain references—not a general-purpose reuse mechanism. Overuse tends to lock entity boundaries together and makes refactoring more costly over time. + +For details about `@x`, see the [Public API documentation](/docs/reference/public-api). + +For concrete examples of cross-references between business entities, see: +- [Types guide — Business entities and their cross-references](/docs/guides/examples/types#business-entities-and-their-cross-references) +- [Layers reference — Entities](/docs/reference/layers#entities) + +## Features and widgets: Multiple strategies + +In the `features` and `widgets` layers, it's usually more realistic to say there are **multiple strategies** for handling cross-imports, rather than declaring them **always forbidden**. This section focuses less on code and more on the **patterns** you can choose from depending on your team and product context. + +### Strategy A: Slice merge + +If two slices are not truly independent and they are always changed together, merge them into a single larger slice. + +Example (before): +- `features/profile` +- `features/profileSettings` + +If these keep cross-importing each other and effectively move as one unit, they are likely one feature in practice. In that case, merging into `features/profile` is often the simpler and cleaner choice. + +### Strategy B: Push shared domain flows down into `entities` (domain-only) + +If multiple features share a domain-level flow, move that flow into a domain slice inside `entities` (for example, `entities/session`). + +Key principles: +- `entities` contains **domain types and domain logic only** +- UI remains in `features` / `widgets` +- features import and use the domain logic from `entities` + +For example, if both `features/auth` and `features/profile` need session validation, place session-related domain functions in `entities/session` and reuse them from both features. + +For more guidance, see [Layers reference — Entities](/docs/reference/layers#entities). + +### Strategy C: Compose from an upper layer (pages / app) + +Instead of connecting slices within the same layer via cross-imports, compose them at a higher level (`pages` / `app`). This approach uses **Inversion of Control (IoC)** patterns—rather than slices knowing about each other, an upper layer assembles and connects them. + +Common IoC techniques include: +- **Render props (React)**: Pass components or render functions as props +- **Slots (Vue)**: Use named slots to inject content from parent components +- **Dependency injection**: Pass dependencies through props or context + +#### Basic composition example (React): + +```tsx title="features/userProfile/index.ts" +export { UserProfilePanel } from './ui/UserProfilePanel'; +``` + +```tsx title="features/activityFeed/index.ts" +export { ActivityFeed } from './ui/ActivityFeed'; +``` + +```tsx title="pages/UserDashboardPage.tsx" +import React from 'react'; +import { UserProfilePanel } from '@/features/userProfile'; +import { ActivityFeed } from '@/features/activityFeed'; + +export function UserDashboardPage() { + return ( +
+ + +
+ ); +} +``` + +With this structure, `features/userProfile` and `features/activityFeed` do not know about each other. `pages/UserDashboardPage` composes them to build the full screen. + +#### Render props example (React): + +When one feature needs to render content from another, use render props to invert the dependency: + +```tsx title="features/commentList/ui/CommentList.tsx" +interface CommentListProps { + comments: Comment[]; + renderUserAvatar?: (userId: string) => React.ReactNode; +} + +export function CommentList({ comments, renderUserAvatar }: CommentListProps) { + return ( +
    + {comments.map(comment => ( +
  • + {renderUserAvatar?.(comment.userId)} + {comment.text} +
  • + ))} +
+ ); +} +``` + +```tsx title="pages/PostPage.tsx" +import { CommentList } from '@/features/commentList'; +import { UserAvatar } from '@/features/userProfile'; + +export function PostPage() { + return ( + } + /> + ); +} +``` + +Now `CommentList` doesn't import from `userProfile`—the page injects the avatar component. + +#### Slots example (Vue): + +Vue's slot system provides a natural way to compose features without cross-imports: + +```vue title="features/commentList/ui/CommentList.vue" + + + +``` + +```vue title="pages/PostPage.vue" + + + +``` + +The `CommentList` feature remains independent of `userProfile`. The page uses slots to compose them together. + +### Strategy D: Cross-feature reuse only via Public API + +If the above strategies don't fit your case and cross-feature reuse is truly unavoidable, allow it only through an explicit **Public API** (for example: exported hooks or UI components). Avoid directly accessing another slice's `store`/`model` or internal implementation details. + +Unlike strategies A-C which aim to eliminate cross-imports, this strategy accepts them while minimizing the risks through strict boundaries. + +#### Example code: + +```tsx title="features/auth/index.ts" + +export { useAuth } from './model/useAuth'; +export { AuthButton } from './ui/AuthButton'; +``` + +```tsx title="features/profile/ui/ProfileMenu.tsx" + +import React from 'react'; +import { useAuth, AuthButton } from '@/features/auth'; + +export function ProfileMenu() { + const { user } = useAuth(); + + if (!user) { + return ; + } + + return
{user.name}
; +} +``` + +For example, prevent `features/profile` from importing from paths like `features/auth/model/internal/*`. Restrict usage to only what `features/auth` explicitly exposes as its Public API. + +## When should cross-imports be treated as a problem? + +After reviewing these strategies, a natural question is: + +> When is a cross-import acceptable to keep, and when should it be treated as a code smell and refactored? + +Common warning signs: +- directly depending on another slice's store/model/business logic +- deep imports into another slice's internal files +- **bidirectional dependencies** (A imports B, and B imports A) +- changes in one slice frequently breaking another slice +- flows that should be composed in `pages` / `app`, but are forced into cross-imports within the same layer + +When you see these signals, treat the cross-import as a **code smell** and consider applying at least one of the strategies above. + +## How strict you are is a team/project decision + +How strictly to enforce these rules depends on the team and project. + +For example: +- In **early-stage products** with heavy experimentation, allowing some cross-imports may be a pragmatic speed trade-off. +- In **long-lived or regulated systems** (for example, fintech or large-scale services), stricter boundaries often pay off in maintainability and stability. + +Cross-imports are not an absolute prohibition here. They are dependencies that are **generally best avoided**, but sometimes used intentionally. + +If you do introduce a cross-import: +- treat it as a deliberate architectural choice +- document the reasoning +- revisit it periodically as the system evolves + +Teams should align on: +- what strictness level they want +- how to reflect it in lint rules, code review, and documentation +- when and how to reevaluate existing cross-imports over time + +## References + +- [(Thread) About the supposed inevitability of cross-ports](https://t.me/feature_sliced/4515) +- [(Thread) About resolving cross-ports in entities](https://t.me/feature_sliced/3678) +- [(Thread) About cross-imports and responsibility](https://t.me/feature_sliced/3287) +- [(Thread) About imports between segments](https://t.me/feature_sliced/4021) +- [(Thread) About cross-imports inside shared](https://t.me/feature_sliced/3618) diff --git a/src/content/docs/guides/issues/desegmented.mdx b/src/content/docs/guides/issues/desegmented.mdx new file mode 100644 index 0000000000..c9eed3c6e5 --- /dev/null +++ b/src/content/docs/guides/issues/desegmented.mdx @@ -0,0 +1,147 @@ +--- +title: Desegmentation +sidebar: + order: 2 +--- + +import { Tabs, TabItem, FileTree } from '@astrojs/starlight/components'; + +Desegmentation (also known as horizontal slicing or packaging by layer) is a code organization pattern where files are grouped by their technical roles rather than by the business domains they serve. This means code with similar technical functions is stored in the same place, regardless of the business logic it handles. + +This approach is popular in meta-frameworks like Next and Nuxt due to its simplicity, as it's easy to get started and enables features like auto-imports and file-based routing: + + +- app + - components + - DeliveryCard.jsx + - DeliveryChoice.jsx + - RegionSelect.jsx + - UserAvatar.jsx + - actions + - delivery.js + - region.js + - user.js + - composables + - delivery.js + - region.js + - user.js + - constants + - delivery.js + - region.js + - user.js + - utils + - delivery.js + - region.js + - user.js + - stores + - delivery + - getters.js + - actions.js + + +This pattern also occurs in FSD codebases, in the form of generic folders: + + +- features + - delivery + - ui + - components ⚠️ +- entities + - recommendations + - utils ⚠️ + + +Files can also be a source of desegmentation. Files like `types.ts` can aggregate multiple domains, complicating navigation and future refactoring, especially in layers like `pages` or `widgets`: + + +- pages + - delivery + - index.ts + - ui + - DeliveryCard.tsx + - DeliveryChoice.tsx + - UserAvatar.tsx + - model + - types.ts ⚠️ + - utils.ts ⚠️ + - api + - endpoints.ts ⚠️ + + + + + +```ts title="pages/delivery/model/types.ts" +// ❌ Bad: Mixed business domains in generic file +export interface DeliveryOption { + id: string; + name: string; + price: number; +} + +export interface UserInfo { + id: string; + name: string; + avatar: string; +} +``` + + + +```ts title="pages/delivery/model/utils.ts" +// ❌ Bad: Mixed business domains in generic file +export function formatDeliveryPrice(price: number) { + return `$${price.toFixed(2)}`; +} + +export function getUserInitials(name: string) { + return name.split(' ').map(n => n[0]).join(''); +} +``` + + + +```ts title="pages/delivery/api/endpoints.ts" +// ❌ Bad: Mixed business domains in generic file +export async function fetchDeliveryOptions() { /* ... */ } +export async function fetchUserInfo() { /* ... */ } +``` + + + +## The Problem + +While this structure is easy to start with, it can lead to scalability issues in larger projects: + +- Low Cohesion: Modifying a single feature often requires editing files in multiple large folders, such as `pages`, `components`, and `stores`. + +- Tight Coupling: Components can have unexpected dependencies, leading to complex and tangled dependency chains. + +- Difficult Refactoring: It requires additional effort to manually extract code related to a specific domain. + +## Solution + +Group all code that relates to a specific domain in one place. + +Avoid generic folder names such as `types`, `components`, `utils`, as well as generic file names like `types.ts`, `utils.ts`, or `helpers.ts`. Instead, use names that directly reflect the domain they represent. + +Avoid generic file names like `types.ts` if possible, especially in slices with multiple domains: + + +- pages + - delivery + - index.tsx + - ui + - DeliveryPage.tsx + - DeliveryCard.tsx + - DeliveryChoice.tsx + - UserInfo.tsx + - model + - delivery.ts + - user.ts + + +## See Also + +* [(Article) About Low Coupling and High Cohesion clearly](https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/) +* [(Article) Low Coupling and High Cohesion. The Law of Demeter](https://medium.com/german-gorelkin/low-coupling-high-cohesion-d36369fb1be9) diff --git a/src/content/docs/guides/issues/excessive-entities.mdx b/src/content/docs/guides/issues/excessive-entities.mdx new file mode 100644 index 0000000000..dbab5b9b8e --- /dev/null +++ b/src/content/docs/guides/issues/excessive-entities.mdx @@ -0,0 +1,120 @@ +--- +title: Excessive Entities +--- + +import { Aside, FileTree } from '@astrojs/starlight/components'; + +The `entities` layer in Feature-Sliced Design is one of the lower layers that's primarily for business logic. That makes it widely accessible — all layers except for `shared` can access it. However, its global nature means that changes to `entities` can have a widespread impact, requiring careful design to avoid costly refactors. + +Excessive entities can lead to ambiguity (what code belongs to this layer), coupling, and constant import dilemmas (code scattered across sibling entities). + +## How to keep `entities` layer clean + +### 0. Consider having no `entities` layer + +You might think that your application won't be Feature-Sliced if you don't include this layer, but it is completely fine for the application to have no `entities` layer. It doesn't break FSD in any way, on the contrary, it simplifies the architecture and keeps the `entities` layer available for future scaling. For example, if your application acts as a thin client, most likely it doesn't need `entities` layer. + + + +### 1. Avoid preemptive slicing + +In contrast to previous versions, FSD 2.1 encourages deferred decomposition of slices instead of preemptive, and this approach also extends to `entities` layer. At first, you can place all your code in the `model` segment of your page (widget, feature), and then consider refactoring it later, when business requirements are stable. + +Remember: the later you move code to the `entities` layer, the less dangerous your potential refactors will be — code in Entities may affect functionality of any slice on higher layers. + +### 2. Avoid Unnecessary Entities + +Do not create an entity for every piece of business logic. Instead, leverage types from `shared/api` and place logic in the `model` segment of a current slice. For reusable business logic, use the `model` segment within an entity slice while keeping data definitions in `shared/api`: + + +- entities + - order + - index.ts + - model + - apply-discount.ts Business logic using OrderDto from shared/api +- shared + - api + - index.ts + - endpoints + - order.ts + + +### 3. Exclude CRUD Operations from Entities + +CRUD operations, while essential, often involve boilerplate code without significant business logic. Including them in the `entities` layer can clutter it and obscure meaningful code. Instead, place CRUD operations in `shared/api`: + + +- shared + - api + - client.ts + - index.ts + - endpoints + - order.ts Contains all order-related CRUD operations + - products.ts + - cart.ts + + +For complex CRUD operations (e.g., atomic updates, rollbacks, or transactions), evaluate whether the `entities` layer is appropriate, but use it with caution. + +### 4. Store Authentication Data in `shared` + +Prefer `shared` layer to creating a `user` entity for authentication data, such as tokens or user DTOs returned from the backend. These are context-specific and unlikely to be reused outside authentication scope: + +- Authentication responses (e.g., tokens or DTOs) often lack fields needed for broader reuse or vary by context (e.g., private vs. public user profiles). +- Using entities for auth data can lead to cross-layer imports (e.g., `entities` into `shared`) or usage of `@x` notation, complicating the architecture. + +Instead, store authentication-related data in `shared/auth` or `shared/api`: + + +- shared + - auth + - use-auth.ts authenticated user info or token + - index.ts + - api + - client.ts + - index.ts + - endpoints + - order.ts + + +For more details on implementing authentication, see [the Authentication guide](/docs/guides/examples/auth). + +### 5. Minimize Cross-Imports + +FSD permits cross-imports via `@x` notation, but they can introduce technical issues like circular dependencies. To avoid this, design entities within isolated business contexts to eliminate the need for cross-imports: + +Non-Isolated Business Context (Avoid): + + +- entities + - order + - @x + - model + - order-item + - @x + - model + - order-customer-info + - @x + - model + + +Isolated Business Context (Preferred): + + +- entities + - order-info + - index.ts + - model + - order-info.ts + + +An isolated context encapsulates all related logic (e.g., order items and customer info) within a single module, reducing complexity and preventing external modifications to tightly coupled logic. From ede6c0bae5dc8622a89117e863508ee2ea94ebbb Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 00:30:31 +0100 Subject: [PATCH 17/45] docs: migrate reference --- astro.config.mjs | 7 + src/content/docs/reference/layers.mdx | 153 +++++++++++++++++ src/content/docs/reference/public-api.mdx | 157 ++++++++++++++++++ .../docs/reference/slices-segments.mdx | 69 ++++++++ src/styles/custom.css | 4 + 5 files changed, 390 insertions(+) create mode 100644 src/content/docs/reference/layers.mdx create mode 100644 src/content/docs/reference/public-api.mdx create mode 100644 src/content/docs/reference/slices-segments.mdx create mode 100644 src/styles/custom.css diff --git a/astro.config.mjs b/astro.config.mjs index 56c031a266..e629851666 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -12,6 +12,9 @@ export default defineConfig({ starlight({ title: 'Feature-Sliced Design', defaultLocale: 'root', + customCss: [ + './src/styles/custom.css', + ], locales: { root: { label: 'English', @@ -44,6 +47,10 @@ export default defineConfig({ } ], }, + { + label: '📚 Reference', + autogenerate: { directory: 'reference' } + } ], }), ], diff --git a/src/content/docs/reference/layers.mdx b/src/content/docs/reference/layers.mdx new file mode 100644 index 0000000000..2488d9bb02 --- /dev/null +++ b/src/content/docs/reference/layers.mdx @@ -0,0 +1,153 @@ +--- +title: Layers +sidebar: + order: 1 +--- + +import { Aside } from '@astrojs/starlight/components'; + +Layers are the first level of organisational hierarchy in Feature-Sliced Design. Their purpose is to separate code based on how much responsibility it needs and how many other modules in the app it depends on. Every layer carries special semantic meaning to help you determine how much responsibility you should allocate to your code. + +There are **7 layers** in total, arranged from most responsibility and dependency to least: + +A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out. +A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out. + +1. App +2. Processes (deprecated) +3. Pages +4. Widgets +5. Features +6. Entities +7. Shared + +You don't have to use every layer in your project — only add them if you think it brings value to your project. Typically, most frontend projects will have at least the Shared, Pages, and App layers. + +In practice, layers are folders with lowercase names (for example, `📁 shared`, `📁 pages`, `📁 app`). Adding new layers is _not recommended_ because their semantics are standardized. + +## Import rule on layers + +Layers are made up of _slices_ — highly cohesive groups of modules. Dependencies between slices are regulated by **the import rule on layers**: + +> _A module (file) in a slice can only import other slices when they are located on layers strictly below._ + +For example, the folder `📁 ~/features/aaa` is a slice with the name "aaa". A file inside of it, `~/features/aaa/api/request.ts`, cannot import code from any file in `📁 ~/features/bbb`, but can import code from `📁 ~/entities` and `📁 ~/shared`, as well as any sibling code from `📁 ~/features/aaa`, for example, `~/features/aaa/lib/cache.ts`. + +Layers App and Shared are **exceptions** to this rule — they are both a layer and a slice at the same time. Slices partition code by business domain, and these two layers are exceptions because Shared does not have business domains, and App combines all business domains. + +In practice, this means that layers App and Shared are made up of segments, and segments can import each other freely. + +## Layer definitions + +This section describes the semantic meaning of each layer to create an intuition for what kind of code belongs there. + +### Shared + +This layer forms a foundation for the rest of the app. It's a place to create connections with the external world, for example, backends, third-party libraries, the environment. It is also a place to define your own highly contained libraries. + +This layer, like the App layer, _does not contain slices_. Slices are intended to divide the layer into business domains, but business domains do not exist in Shared. This means that all files in Shared can reference and import from each other. + +Here are the segments that you can typically find in this layer: + +- `📁 api` — the API client and potentially also functions to make requests to specific backend endpoints. +- `📁 ui` — the application's UI kit. + Components on this layer should not contain business logic, but it's okay for them to be business-themed. For example, you can put the company logo and page layout here. Components with UI logic are also allowed (for example, autocomplete or a search bar). +- `📁 lib` — a collection of internal libraries. + This folder should not be treated as helpers or utilities ([read here why these folders often turn into a dump][ext-sova-utility-dump]). Instead, every library in this folder should have one area of focus, for example, dates, colors, text manipulation, etc. That area of focus should be documented in a README file. The developers in your team should know what can and cannot be added to these libraries. +- `📁 config` — environment variables, global feature flags and other global configuration for your app. +- `📁 routes` — route constants or patterns for matching routes. +- `📁 i18n` — setup code for translations, global translation strings. + +You are free to add more segments, but make sure that the name of these segments describes the purpose of the content, not its essence. For example, `components`, `hooks`, and `types` are bad segment names because they aren't that helpful when you're looking for code. + +### Entities + +Slices on this layer represent concepts from the real world that the project is working with. Commonly, they are the terms that the business uses to describe the product. For example, a social network might work with business entities like User, Post, and Group. + +An entity slice might contain the data storage (`📁 model`), data validation schemas (`📁 model`), entity-related API request functions (`📁 api`), as well as the visual representation of this entity in the interface (`📁 ui`). The visual representation doesn't have to produce a complete UI block — it is primarily meant to reuse the same appearance across several pages in the app, and different business logic may be attached to it through props or slots. + +#### Entity relationships + +Entities in FSD are slices, and by default, slices cannot know about each other. In real life, however, entities often interact with each other, and sometimes one entity owns or contains other entities. Because of that, the business logic of these interactions is preferably kept in higher layers, like Features or Pages. + +When one entity's data object contains other data objects, usually it's a good idea to make the connection between the entities explicit and side-step the slice isolation by making a cross-reference API with the `@x` notation. The reason is that connected entities need to be refactored together, so it's best to make the connection impossible to miss. + +For example: + +```ts title="entities/artist/model/artist.ts" +import type { Song } from "entities/song/@x/artist"; + +export interface Artist { + name: string; + songs: Array; +} +``` + +```ts title="entities/song/@x/artist.ts" +export type { Song } from "../model/song.ts"; +``` + +Learn more about the `@x` notation in the [Public API for cross-imports][public-api-for-cross-imports] section. + +### Features + +This layer is for the main interactions in your app, things that your users care to do. These interactions often involve business entities, because that's what the app is about. + +A crucial principle for using the Features layer effectively is: **not everything needs to be a feature**. A good indicator that something needs to be a feature is the fact that it is reused on several pages. + +For example, if the app has several editors, and all of them have comments, then comments are a reused feature. Remember that slices are a mechanism for finding code quickly, and if there are too many features, the important ones are drowned out. + +Ideally, when you arrive in a new project, you would discover its functionality by looking through the pages and features. When deciding on what should be a feature, optimize for the experience of a newcomer to the project to quickly discover large important areas of code. + +A feature slice might contain the UI to perform the interaction like a form (`📁 ui`), the API calls needed to make the action (`📁 api`), validation and internal state (`📁 model`), feature flags (`📁 config`). + +### Widgets + +The Widgets layer is intended for large self-sufficient blocks of UI. Widgets are most useful when they are reused across multiple pages, or when the page that they belong to has multiple large independent blocks, and this is one of them. + +If a block of UI makes up most of the interesting content on a page, and is never reused, it **should not be a widget**, and instead it should be placed directly inside that page. + + + +### Pages + +Pages are what makes up websites and applications (also known as screens or activities). One page usually corresponds to one slice, however, if there are several very similar pages, they can be grouped into one slice, for example, registration and login forms. + +There's no limit to how much code you can place in a page slice as long as your team still finds it easy to navigate. If a UI block on a page is not reused, it's perfectly fine to keep it inside the page slice. + +In a page slice you can typically find the page's UI as well as loading states and error boundaries (`📁 ui`) and the data fetching and mutating requests (`📁 api`). It's not common for a page to have a dedicated data model, and tiny bits of state can be kept in the components themselves. + +### Processes + + + +Processes are escape hatches for multi-page interactions. + +This layer is deliberately left undefined. Most applications should not use this layer, and keep router-level and server-level logic on the App layer. Consider using this layer only when the App layer grows large enough to become unmaintainable and needs unloading. + +### App + +All kinds of app-wide matters, both in the technical sense (e.g., context providers) and in the business sense (e.g., analytics). + +This layer usually doesn't contain slices, as well as Shared, instead having segments directly. + +Here are the segments that you can typically find in this layer: + +- `📁 routes` — the router configuration +- `📁 store` — global store configuration +- `📁 styles` — global styles +- `📁 entrypoint` — the entrypoint to the application code, framework-specific + +[public-api-for-cross-imports]: /docs/reference/public-api#public-api-for-cross-imports +[ext-remix]: https://remix.run +[ext-sova-utility-dump]: https://dev.to/sergeysova/why-utils-helpers-is-a-dump-45fo diff --git a/src/content/docs/reference/public-api.mdx b/src/content/docs/reference/public-api.mdx new file mode 100644 index 0000000000..029e9f9ca4 --- /dev/null +++ b/src/content/docs/reference/public-api.mdx @@ -0,0 +1,157 @@ +--- +title: Public API +sidebar: + order: 3 +--- + +import { FileTree } from '@astrojs/starlight/components'; + +A public API is a _contract_ between a group of modules, like a slice, and the code that uses it. It also acts as a gate, only allowing access to certain objects, and only through that public API. + +In practice, it's usually implemented as an index file with re-exports: + +```js title="pages/auth/index.js" +export { LoginPage } from "./ui/LoginPage"; +export { RegisterPage } from "./ui/RegisterPage"; +``` + +## What makes a good public API? + +A good public API makes using and integrating into other code a slice convenient and reliable. It can be achieved by setting these three goals: + +1. The rest of the application must be protected from structural changes to the slice, like a refactoring +1. Significant changes in the behavior of the slice that break the previous expectations should cause changes in the public API +1. Only the necessary parts of the slice should be exposed + +The last goal has some important practical implications. It may be tempting to create wildcard re-exports of everything, especially in early development of the slice, because any new objects you export from your files are also automatically exported from the slice: + +```js title="Bad practice, features/comments/index.js" +// ❌ BAD CODE BELOW, DON'T DO THIS +export * from "./ui/Comment"; // 👎 don't try this at home +export * from "./model/comments"; // 💩 this is bad practice +``` + +This hurts the discoverability of a slice because you can't easily tell what the interface of this slice is. Not knowing the interface means that you have to dig deep into the code of a slice to understand how to integrate it. Another problem is that you might accidentally expose the module internals, which will make refactoring difficult if someone starts depending on them. + +## Public API for cross-imports \{#public-api-for-cross-imports\} + +Cross-imports are a situation when one slice imports from another slice on the same layer. Usually that is prohibited by the [import rule on layers][import-rule-on-layers], but often there are legitimate reasons to cross-import. For example, business entities often reference each other in the real world, and it's best to reflect these relationships in the code instead of working around them. + +For this purpose, there's a special kind of public API, also known as the `@x`-notation. If you have entities A and B, and entity B needs to import from entity A, then entity A can declare a separate public API just for entity B. + + +- entities + - A + - @x + - B.ts a special public API just for code inside `entities/B/` + - index.ts the regular public API + + +Then the code inside `entities/B/` can import from `entities/A/@x/B`: + +```ts +import type { EntityA } from "entities/A/@x/B"; +``` + +The notation `A/@x/B` is meant to be read as "A crossed with B". + +:::note + +Try to keep cross-imports to a minimum, and **only use this notation on the Entities layer**, where eliminating cross-imports is often unreasonable. + +::: + +## Issues with index files + +Index files like `index.js`, also known as barrel files, are the most common way to define a public API. They are easy to make, but they are known to cause problems with certain bundlers and frameworks. + +### Circular imports + +Circular import is when two or more files import each other in a circle. + +
+ Three files importing each other in a circle + Three files importing each other in a circle +
+ Pictured above: three files, `fileA.js`, `fileB.js`, and `fileC.js`, importing each other in a circle. +
+
+ +These situations are often difficult for bundlers to deal with, and in some cases they might even lead to runtime errors that might be difficult to debug. + +Circular imports can occur without index files, but having an index file presents a clear opportunity to accidentally create a circular import. It often happens when you have two objects exposed in the public API of a slice, for example, `HomePage` and `loadUserStatistics`, and the `HomePage` needs to access `loadUserStatistics`, but it does it like this: + +```jsx title="pages/home/ui/HomePage.jsx" +import { loadUserStatistics } from "../"; // importing from pages/home/index.js + +export function HomePage() { /* … */ } +``` + +```js title="pages/home/index.js" +export { HomePage } from "./ui/HomePage"; +export { loadUserStatistics } from "./api/loadUserStatistics"; +``` + +This situation creates a circular import, because `index.js` imports `ui/HomePage.jsx`, but `ui/HomePage.jsx` imports `index.js`. + +To prevent this issue, consider these two principles. If you have two files, and one imports from the other: +- When they are in the same slice, always use _relative_ imports and write the full import path +- When they are in different slices, always use _absolute_ imports, for example, with an alias + +### Large bundles and broken tree-shaking in Shared \{#large-bundles\} + +Some bundlers might have a hard time tree-shaking (removing code that isn't imported) when you have an index file that re-exports everything. + +Usually this isn't a problem for public APIs, because the contents of a module are usually quite closely related, so you would rarely need to import one thing and tree-shake away the other. However, there are two very common cases when the normal rules of public API in FSD may lead to issues — `shared/ui` and `shared/lib`. + +These two folders are both collections of unrelated things that often aren't all needed in one place. For example, `shared/ui` might have modules for every component in the UI library: + + +- shared/ + - ui/ + - button/ + - text-field/ + - carousel/ + - accordion/ + + +This problem is made worse when one of these modules has a heavy dependency, like a syntax highlighter or a drag'n'drop library. You don't want to pull those into every page that uses something from `shared/ui`, for example, a button. + +If your bundles grow undesirably due to a single public API in `shared/ui` or `shared/lib`, it's recommended to instead have a separate index file for each component or library: + + +- shared/ + - ui/ + - button/ + - index.js + - text-field/ + - index.js + + +Then the consumers of these components can import them directly like this: + +```js title="pages/sign-in/ui/SignInPage.jsx" +import { Button } from '@/shared/ui/button'; +import { TextField } from '@/shared/ui/text-field'; +``` + +### No real protection against side-stepping the public API + +When you create an index file for a slice, you don't actually forbid anyone from not using it and importing directly. This is especially a problem for auto-imports, because there are several places from which an object can be imported, so the IDE has to decide that for you. Sometimes it might choose to import directly, breaking the public API rule on slices. + +To catch these issues automatically, we recommend using [Steiger][ext-steiger], an architectural linter with a ruleset for Feature-Sliced Design. + +### Worse performance of bundlers on large projects + +Having a large amount of index files in a project can slow down the development server, as noted by TkDodo in [his article "Please Stop Using Barrel Files"][ext-please-stop-using-barrel-files]. + +There are several things you can do to tackle this issue: +1. The same advice as in ["Large bundles and broken tree-shaking in Shared" issue](#large-bundles) — have separate index files for each component/library in `shared/ui` and `shared/lib` instead of one big one +2. Avoid having index files in segments on layers that have slices. + For example, if you have an index for the feature "comments", `📄 features/comments/index.js`, there's no reason to have another index for the `ui` segment of that feature, `📄 features/comments/ui/index.js`. +3. If you have a very big project, there's a good chance that your application can be split into several big chunks. + For example, Google Docs has very different responsibilities for the document editor and for the file browser. You can create a monorepo setup where each package is a separate FSD root, with its own set of layers. Some packages can only have the Shared and Entities layers, others might only have Pages and App, others still might include their own small Shared, but still use the big one from another package too. + +[import-rule-on-layers]: /docs/reference/layers#import-rule-on-layers +[ext-steiger]: https://github.com/feature-sliced/steiger +[ext-please-stop-using-barrel-files]: https://tkdodo.eu/blog/please-stop-using-barrel-files diff --git a/src/content/docs/reference/slices-segments.mdx b/src/content/docs/reference/slices-segments.mdx new file mode 100644 index 0000000000..a203af8e48 --- /dev/null +++ b/src/content/docs/reference/slices-segments.mdx @@ -0,0 +1,69 @@ +--- +title: Slices and segments +sidebar: + order: 2 +--- + +## Slices + +Slices are the second level in the organizational hierarchy of Feature-Sliced Design. Their main purpose is to group code by its meaning for the product, business, or just the application. + +The names of slices are not standardized because they are directly determined by the business domain of your application. For example, a photo gallery might have slices `photo`, `effects`, `gallery-page`. A social network would require different slices, for example, `post`, `comments`, `news-feed`. + +The layers Shared and App don't contain slices. That is because Shared should contain no business logic at all, hence has no meaning for the product, and App should contain only code that concerns the entire application, so no splitting is necessary. + +### Zero coupling and high cohesion \{#zero-coupling-high-cohesion\} + +Slices are meant to be independent and highly cohesive groups of code files. The graphic below might help to visualize the tricky concepts of _cohesion_ and _coupling_: + +
+ + +
+ Image inspired by https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/ +
+
+ +An ideal slice is independent from other slices on its layer (zero coupling) and contains most of the code related to its primary goal (high cohesion). + +The independence of slices is enforced by the [import rule on layers][layers--import-rule]: + +> _A module (file) in a slice can only import other slices when they are located on layers strictly below._ + +### Public API rule on slices + +Inside a slice, the code could be organized in any way that you want. That doesn't pose any issues as long as the slice provides a good public API for other slices to use it. This is enforced with the **public API rule on slices**: + +> _Every slice (and segment on layers that don't have slices) must contain a public API definition._ +> +> _Modules outside of this slice/segment can only reference the public API, not the internal file structure of the slice/segment._ + +Read more about the rationale of public APIs and the best practices on creating one in the [Public API reference][ref-public-api]. + +### Slice groups + +Closely related slices can be structurally grouped in a folder, but they should exercise the same isolation rules as other slices — there should be **no code sharing** in that folder. + +![Features "compose", "like" and "delete" grouped in a folder "post". In that folder there is also a file "some-shared-code.ts" that is crossed out to imply that it's not allowed.](../../../../static/img/graphic-nested-slices.svg) + +## Segments + +Segments are the third and final level in the organizational hierarchy, and their purpose is to group code by its technical nature. + +There a few standardized segment names: + +- `ui` — everything related to UI display: UI components, date formatters, styles, etc. +- `api` — backend interactions: request functions, data types, mappers, etc. +- `model` — the data model: schemas, interfaces, stores, and business logic. +- `lib` — library code that other modules on this slice need. +- `config` — configuration files and feature flags. + +See the [Layers page][layers--layer-definitions] for examples of what each of these segments might be used for on different layers. + +You can also create custom segments. The most common places for custom segments are the App layer and the Shared layer, where slices don't make sense. + +Make sure that the name of these segments describes the purpose of the content, not its essence. For example, `components`, `hooks`, and `types` are bad segment names because they aren't that helpful when you're looking for code. + +[layers--layer-definitions]: /docs/reference/layers#layer-definitions +[layers--import-rule]: /docs/reference/layers#import-rule-on-layers +[ref-public-api]: /docs/reference/public-api diff --git a/src/styles/custom.css b/src/styles/custom.css new file mode 100644 index 0000000000..453200960d --- /dev/null +++ b/src/styles/custom.css @@ -0,0 +1,4 @@ +:root[data-theme=dark] [src$=\#light-mode-only], +:root[data-theme=light] [src$=\#dark-mode-only] { + display: none +} From c2d24fdc4cdafa0d7c7284e446a124064987b661 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 00:37:23 +0100 Subject: [PATCH 18/45] chore: add fsd logo --- astro.config.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/astro.config.mjs b/astro.config.mjs index e629851666..1c12555fcd 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -15,6 +15,10 @@ export default defineConfig({ customCss: [ './src/styles/custom.css', ], + logo: { + src: './static/img/brand/logo-primary.png', + replacesTitle: true, + }, locales: { root: { label: 'English', From 89e2a4ddd90e67dec2d24a1df5caf1425f739c5c Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 00:41:32 +0100 Subject: [PATCH 19/45] docs: change home page --- src/content/docs/index.mdx | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx index 915cfaca8a..4efdc0a017 100644 --- a/src/content/docs/index.mdx +++ b/src/content/docs/index.mdx @@ -4,25 +4,8 @@ description: Get started building your docs site with Starlight. template: splash --- -import { Card, CardGrid } from '@astrojs/starlight/components'; +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; -## Next steps - - - - Edit `src/content/docs/index.mdx` to see this page change. - - - Delete `template: splash` in `src/content/docs/index.mdx` to display a - sidebar on this page. - - - Add Markdown or MDX files to `src/content/docs` to create new pages. - - - Edit your `sidebar` and other config in `astro.config.mjs`. - - - Learn more in [the Starlight Docs](https://starlight.astro.build/). - + + From 47d72fff9a95b8ed4c73288a0115a4e302a964e1 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 00:47:04 +0100 Subject: [PATCH 20/45] chore: replace build script with astro --- astro.config.mjs | 1 + package.json | 3 ++- pnpm-lock.yaml | 30 +++++++++++------------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index 1c12555fcd..db786d9406 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -5,6 +5,7 @@ import remarkHeaderId from 'remark-heading-id'; // https://astro.build/config export default defineConfig({ + outDir: './build', markdown: { remarkPlugins: [remarkHeaderId], }, diff --git a/package.json b/package.json index 773d62c7fd..cf2f0d30f4 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "start:ja": "docusaurus start --locale ja", "start:zh": "docusaurus start --locale zh", "start:vi": "docusaurus start --locale vi", - "build": "docusaurus build", + "build": "astro build", "swizzle": "docusaurus swizzle", "format": "prettier --cache --experimental-cli --write .", "test": "pnpm run test:lint && pnpm run build", @@ -73,6 +73,7 @@ "prettier": "^3.6.2", "remark-heading-id": "^1.0.1", "sass": "^1.93.2", + "sharp": "^0.34.5", "stylelint": "^16.24.0", "stylelint-config-recess-order": "^5.1.1", "stylelint-config-recommended": "^14.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4451d4549c..07ba7eb10c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,6 +135,9 @@ importers: sass: specifier: ^1.93.2 version: 1.93.2 + sharp: + specifier: ^0.34.5 + version: 0.34.5 stylelint: specifier: ^16.24.0 version: 16.24.0(typescript@5.9.2) @@ -1434,9 +1437,6 @@ packages: '@emnapi/core@1.5.0': resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - '@emnapi/runtime@1.8.1': resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} @@ -10280,11 +10280,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.5.0': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 @@ -10460,8 +10455,7 @@ snapshots: '@humanwhocodes/object-schema@1.2.1': {} - '@img/colour@1.0.0': - optional: true + '@img/colour@1.0.0': {} '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: @@ -10701,7 +10695,7 @@ snapshots: '@napi-rs/wasm-runtime@1.0.5': dependencies: '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.8.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -12457,7 +12451,7 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.6) postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: '@rspack/core': 1.5.7 webpack: 5.101.3(@swc/core@1.13.19) @@ -12667,8 +12661,7 @@ snapshots: detect-libc@2.1.1: {} - detect-libc@2.1.2: - optional: true + detect-libc@2.1.2: {} detect-node@2.1.0: {} @@ -15306,7 +15299,7 @@ snapshots: got: 12.6.1 registry-auth-token: 5.1.0 registry-url: 6.0.1 - semver: 7.7.2 + semver: 7.7.3 package-manager-detector@1.6.0: {} @@ -15582,7 +15575,7 @@ snapshots: cosmiconfig: 8.3.6(typescript@5.9.2) jiti: 1.21.7 postcss: 8.5.6 - semver: 7.7.2 + semver: 7.7.3 webpack: 5.101.3(@swc/core@1.13.19) transitivePeerDependencies: - typescript @@ -16539,7 +16532,7 @@ snapshots: semver-diff@4.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 semver@5.7.2: {} @@ -16678,7 +16671,6 @@ snapshots: '@img/sharp-win32-arm64': 0.34.5 '@img/sharp-win32-ia32': 0.34.5 '@img/sharp-win32-x64': 0.34.5 - optional: true shebang-command@2.0.0: dependencies: @@ -17460,7 +17452,7 @@ snapshots: is-yarn-global: 0.4.1 latest-version: 7.0.0 pupa: 3.3.0 - semver: 7.7.2 + semver: 7.7.3 semver-diff: 4.0.0 xdg-basedir: 5.1.0 From da53d3975b814dad80b62314e083d6ce56bd07b7 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 17:21:01 +0100 Subject: [PATCH 21/45] docs: add favicon --- astro.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/astro.config.mjs b/astro.config.mjs index db786d9406..1372c45e09 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -12,6 +12,7 @@ export default defineConfig({ integrations: [ starlight({ title: 'Feature-Sliced Design', + favicon: './static/img/favicon/adaptive.svg', defaultLocale: 'root', customCss: [ './src/styles/custom.css', From b09f16de37c4ccd7e5e64a56bb1e2e8d29ddc31e Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 17:21:28 +0100 Subject: [PATCH 22/45] docs: add missing en pages --- astro.config.mjs | 21 +++ src/content/docs/about/alternatives.mdx | 139 +++++++++++++++ src/content/docs/about/mission.mdx | 51 ++++++ src/content/docs/about/motivation.mdx | 149 ++++++++++++++++ .../docs/about/promote/for-company.mdx | 16 ++ src/content/docs/about/promote/for-team.mdx | 16 ++ .../docs/about/promote/integration.mdx | 25 +++ .../about/promote/partial-application.mdx | 10 ++ .../docs/about/understanding/abstractions.mdx | 24 +++ .../docs/about/understanding/architecture.mdx | 97 +++++++++++ .../about/understanding/knowledge-types.mdx | 31 ++++ .../docs/about/understanding/naming.mdx | 48 ++++++ .../docs/about/understanding/needs-driven.mdx | 162 ++++++++++++++++++ .../docs/about/understanding/signals.mdx | 19 ++ 14 files changed, 808 insertions(+) create mode 100644 src/content/docs/about/alternatives.mdx create mode 100644 src/content/docs/about/mission.mdx create mode 100644 src/content/docs/about/motivation.mdx create mode 100644 src/content/docs/about/promote/for-company.mdx create mode 100644 src/content/docs/about/promote/for-team.mdx create mode 100644 src/content/docs/about/promote/integration.mdx create mode 100644 src/content/docs/about/promote/partial-application.mdx create mode 100644 src/content/docs/about/understanding/abstractions.mdx create mode 100644 src/content/docs/about/understanding/architecture.mdx create mode 100644 src/content/docs/about/understanding/knowledge-types.mdx create mode 100644 src/content/docs/about/understanding/naming.mdx create mode 100644 src/content/docs/about/understanding/needs-driven.mdx create mode 100644 src/content/docs/about/understanding/signals.mdx diff --git a/astro.config.mjs b/astro.config.mjs index 1372c45e09..99794513a8 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -56,6 +56,27 @@ export default defineConfig({ { label: '📚 Reference', autogenerate: { directory: 'reference' } + }, + { + label: '🍰 About', + items: [{ + label: 'Mission', + slug: 'about/mission' + }, { + label: 'Motivation', + slug: 'about/motivation' + }, { + label: 'Alternatives', + slug: 'about/alternatives' + }, { + label: 'Understanding', + autogenerate: { directory: 'about/understanding' }, + collapsed: true, + }, { + label: 'Promote', + autogenerate: { directory: 'about/promote' }, + collapsed: true, + }], } ], }), diff --git a/src/content/docs/about/alternatives.mdx b/src/content/docs/about/alternatives.mdx new file mode 100644 index 0000000000..a0400b91e9 --- /dev/null +++ b/src/content/docs/about/alternatives.mdx @@ -0,0 +1,139 @@ +--- +title: Alternatives +sidebar: + order: 3 + badge: + text: WIP + variant: caution +--- + +import { FileTree } from '@astrojs/starlight/components'; + +History of architecture approaches + +## Big Ball of Mud + +> What is it; Why is it so common; When it starts to bring problems; What to do and how does FSD help in this + +- [(Article) Oleg Isonen - Last words on UI architecture before an AI takes over](https://oleg008.medium.com/last-words-on-ui-architecture-before-an-ai-takes-over-468c78f18f0d) +- [(Report) Julia Nikolaeva, iSpring - Big Ball of Mud and other problems of the monolith, we have handled](http://youtu.be/gna4Ynz1YNI) +- [(Article) DD - Big Ball of mud](https://thedomaindrivendesign.io/big-ball-of-mud/) + + +## Smart & Dumb components + +> About the approach; About applicability in the frontend; Methodology position + +About obsolescence, about a new view from the methodology + +Why component-containers approach is evil? + +- [(Article) Den Abramov-Presentation and Container Components (TLDR: deprecated)](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) + + +## Design Principles + +> What are we talking about; FSD position + +SOLID, GRASP, KISS, YAGNI, ... - and why they don't work well together in practice + +And how does it aggregate these practices + +- [(Talk) Ilya Azin - Feature-Sliced Design (fragment about Design Principles)](https://youtu.be/SnzPAr_FJ7w?t=380) + + +## DDD + +> About the approach; Why does it work poorly in practice + +What is the difference, how does it improve applicability, where does it adopt practices + +- [(Article) DDD, Hexagonal, Onion, Clean, CQRS, ... How I put it all together](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/) +- [(Talk) Ilya Azin - Feature-Sliced Design (fragment about Clean Architecture, DDD)](https://youtu.be/SnzPAr_FJ7w?t=528) + + +## Clean Architecture + +> About the approach; About applicability in the frontend; FSD position + +How are they similar (to many), how are they different + +- [(Thread) About use-case/interactor in the methodology](https://t.me/feature_sliced/3897) +- [(Thread) About DI in the methodology](https://t.me/feature_sliced/4592) +- [(Article) Alex Bespoyasov - Clean Architecture on frontend](https://bespoyasov.me/blog/clean-architecture-on-frontend/) +- [(Article) DDD, Hexagonal, Onion, Clean, CQRS, ... How I put it all together](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/) +- [(Talk) Ilya Azin - Feature-Sliced Design (fragment about Clean Architecture, DDD)](https://youtu.be/SnzPAr_FJ7w?t=528) +- [(Article) Misconceptions of Clean Architecture](http://habr.com/ru/company/mobileup/blog/335382/) + + +## Frameworks + +> About applicability in the frontend; Why frameworks do not solve problems; why there is no single approach; FSD position + +Framework-agnostic, conventional-approach + +- [(Article) About the reasons for creating the methodology (fragment about frameworks)](/docs/about/motivation) +- [(Thread) About the applicability of the methodology for different frameworks](https://t.me/feature_sliced/3867) + + +## Atomic Design + +### What is it? +In Atomic Design, the scope of responsibility is divided into standardized layers. + +Atomic Design is broken down into **5 layers** (from top to bottom): + +1. `pages` - Functionality similar to the `pages` layer in FSD. +2. `templates` - Components that define the structure of a page without tying to specific content. +3. `organisms` - Modules consisting of molecules that have business logic. +4. `molecules` - More complex components that generally do not contain business logic. +5. `atoms` - UI components without business logic. + +Modules at one layer interact only with modules in the layers below, similar to FSD. +That is, molecules are built from atoms, organisms from molecules, templates from organisms, and pages from templates. +Atomic Design also implies the use of Public API within modules for isolation. + +### Applicability to frontend + +Atomic Design is relatively common in projects. Atomic Design is more popular among web designers than in development. +Web designers often use Atomic Design to create scalable and easily maintainable designs. +In development, Atomic Design is often mixed with other architectural methodologies. + +However, since Atomic Design focuses on UI components and their composition, a problem arises with implementing +business logic within the architecture. + +The problem is that Atomic Design does not provide a clear level of responsibility for business logic, +leading to its distribution across various components and levels, complicating maintenance and testing. +The business logic becomes blurred, making it difficult to clearly separate responsibilities and rendering +the code less modular and reusable. + +### How does it relate to FSD? + +In the context of FSD, some elements of Atomic Design can be applied to create flexible and scalable UI components. +The `atoms` and `molecules` layers can be implemented in `shared/ui` in FSD, simplifying the reuse and +maintenance of basic UI elements. + + +- shared/ + - ui/ + - atoms/ + - molecules/ + + +A comparison of FSD and Atomic Design shows that both methodologies strive for modularity and reusability +but focus on different aspects. Atomic Design is oriented towards visual components and their composition. +FSD focuses on dividing the application's functionality into independent modules and their interconnections. + +- [Atomic Design Methodology](https://atomicdesign.bradfrost.com/table-of-contents/) +- [(Thread) About applicability in shared / ui](https://t.me/feature_sliced/1653) +- [(Video) Briefly about Atomic Design](https://youtu.be/Yi-A20x2dcA) +- [(Talk) Ilya Azin - Feature-Sliced Design (fragment about Atomic Design)](https://youtu.be/SnzPAr_FJ7w?t=587) + +## Feature Driven + +> About the approach; About applicability in the frontend; FSD position + +About compatibility, historical development and comparison + +- [(Talk) Oleg Isonen - Feature Driven Architecture](https://youtu.be/BWAeYuWFHhs) +- [Feature Driven-Short specification (from the point of view of FSD)](https://github.com/feature-sliced/documentation/tree/rc/feature-driven) diff --git a/src/content/docs/about/mission.mdx b/src/content/docs/about/mission.mdx new file mode 100644 index 0000000000..b7fd00b594 --- /dev/null +++ b/src/content/docs/about/mission.mdx @@ -0,0 +1,51 @@ +--- +title: Mission +sidebar: + order: 1 +--- + +Here we describe the goals and limitations of the applicability of the methodology-which we are guided by when developing the methodology + +- We see our goal as a balance between ideology and simplicity +- We won't be able to make a silver bullet that fits everyone + +**Nevertheless, the methodology should be close and accessible to a fairly wide range of developers** + +## Goals + +### Intuitive clarity for a wide range of developers + +The methodology should be accessible - for most of the team in projects + +*Because even with all the future tools , it will not be enough, if only experienced seniors/leads will understand the methodology* + +### Solving everyday problems + +The methodology should set out the reasons and solutions to our everyday problems when developing projects + +**And also-attach tools to all this (cli, linters)** + +So that developers can use a *battle-tested* approach that allows them to bypass long-standing problems of architecture and development + +> *@sergeysova: Imagine, that a developer writes code within the framework of the methodology and he has problems 10 times less often, simply because other people have thought out the solution to many problems.* + +## Limitations + +We do not want to *impose our point of view*, and at the same time we understand that *many of our habits, as developers, interfere from day to day* + +Everyone has their own level of experience in designing and developing systems, **therefore, it is worth understanding the following:** + +- **Will not work**: very simple, very clear, for everyone + > *@sergeysova: Some concepts cannot be intuitively understood until you encounter problems and spend years solving them.* + > + > - *In math world: is graph theory.* + > - *In physics: quantum mechanics.* + > - *In programming: application architecture.* + +- **Possible and desirable**: simplicity, extensibility + +## See also + +- [Architecture problems][refs-architecture--problems] + +[refs-architecture--problems]: /docs/about/understanding/architecture#problems diff --git a/src/content/docs/about/motivation.mdx b/src/content/docs/about/motivation.mdx new file mode 100644 index 0000000000..231ff514b2 --- /dev/null +++ b/src/content/docs/about/motivation.mdx @@ -0,0 +1,149 @@ +--- +title: Motivation +sidebar: + order: 2 +--- + +import { Aside } from '@astrojs/starlight/components'; + +The main idea of **Feature-Sliced Design** is to facilitate and reduce the cost of developing complex and developing projects, based on [combining research results, discussing the experience of various kinds of a wide range of developers][ext-discussions]. + +Obviously, this will not be a silver bullet, and of course, the methodology will have its own [limits of applicability][refs-mission]. + +Nevertheless, there are reasonable questions regarding *the feasibility of such a methodology as a whole* + + + +## Why are there not enough existing solutions? +> It usually, these arguments: +> +> - *"Why you need some new methodology, if you already have long-established approaches and principles of design such as `SOLID`, `KISS`, `YAGNI`, `DDD`, `GRASP`, `DRY`, etc."* +> - *"All the problems are solved by good project documentation, tests, and structured processes"* +> - *"Problems would not have happened if all developers are following all the above"* +> - *"Everything was invented before you, you just can't use it"* +> - *"Take \{FRAMEWORK_NAME\} - everything has already been decided for you there"* + +### Principles alone are not enough + +**The existence of principles alone is not enough to design a good architecture** + +Not everyone knows them completely, even fewer understand and apply them correctly + +*The design principles are too general, and do not give a specific answer to the question: "How to design the structure and architecture of a scalable and flexible application?"* + +### Processes don't always work + +*Documentation/Tests/Processes* are, of course, good, but alas, even at high costs for them - **they do not always solve the problems posed by the architecture and the introduction of new people into the project** + +- The time of entry of each developer into the project is not greatly reduced, because the documentation will most often come out huge / outdated +- Constantly make sure that everyone understands architecture in the same way-it also requires a huge amount of resources +- Do not forget about the bus-factor + +### Existing frameworks cannot be applied everywhere + +- Existing solutions usually have a high entry threshold, which makes it difficult to find new developers +- Also, most often, the choice of technology has already been determined before the onset of serious problems in the project, and therefore you need to be able to "work with what is" - **without being tied to the technology** + +> Q: *"In my project `React/Vue/Redux/Effector/Mobx/{YOUR_TECH}` - how can I better build the structure of entities and the relationships between them?"* + +### As a result + +We get *"unique as snowflakes"* projects, each of which requires a long immersion of the employee, and knowledge that is unlikely to be applicable on another project + +> @sergeysova: *"This is exactly the situation that currently exists in our field of frontend development: each lead will invent different architectures and project structures, while it is not a fact that these structures will pass the test of time, as a result, a maximum of two people can develop the project besides him, and each new developer needs to be immersed again."* + +## Why do developers need the methodology? + +### Focus on business features, not on architecture problems + +The methodology allows you to save resources on designing a scalable and flexible architecture, instead directing the attention of developers to the development of the main functionality. At the same time, the architectural solutions themselves are standardized from project to project. + +*A separate question is that the methodology should earn the trust of the community, so that another developer can get acquainted with it and rely on it in solving the problems of his project within the time available to him* + +### An experience-proven solution + +The methodology is designed for developers who are aimed at *a proven solution for designing complex business logic* + +*However, it is clear that the methodology is generally about a set of best-practices, articles that address certain problems and cases during development. Therefore, the methodology will also be useful for the rest of the developers-who somehow face problems during development and design* + +### Project Health + +The methodology will allow *to solve and track the problems of the project in advance, without requiring a huge amount of resources* + +**Most often, technical debt accumulates and accumulates over time, and the responsibility for its resolution lies on both the lead and the team** + +The methodology will allow you to *warn* possible problems in the scaling and development of the project in advance + +## Why does a business need a methodology? + +### Fast onboarding + +With the methodology, you can hire a person to the project who **is already previously familiar with this approach, and not train again** + +*People start to understand and benefit the project faster, and there are additional guarantees to find people for the next iterations of the project* + +### An experience-proven solution + +With the methodology, the business will get *a solution for most of the issues that arise during the development of systems* + +Since most often a business wants to get a framework / solution that would solve the lion's share of problems during the development of the project + +### Applicability for different stages of the project + +The methodology can benefit the project *both at the stage of project support and development, and at the MVP stage* + +Yes, the most important thing for MVP is *"features, not the architecture laid down for the future"*. But even in conditions of limited deadlines, knowing the best-practices from the methodology, you can *"do with little blood"*, when designing the MVP version of the system, finding a reasonable compromise +(rather than modeling features "at random") + +*The same can be said about testing* + +## When is our methodology not needed? + +- If the project will live for a short time +- If the project does not need a supported architecture +- If the business does not perceive the connection between the code base and the speed of feature delivery +- If it is more important for the business to close orders as soon as possible, without further support + +### Business Size + +- **Small business** - most often needs a ready-made and very fast solution. Only when the business grows (at least to almost average), he understands that in order for customers to continue using, it is necessary, among other things, to devote time to the quality and stability of the solutions being developed +- **Medium-sized business** - usually understands all the problems of development, and even if it is necessary to *"arrange a race for features"*, he still spends time on quality improvements, refactoring and tests (and of course-on an extensible architecture) +- **Big business** - usually already has an extensive audience, staff, and a much more extensive set of its practices, and probably even its own approach to architecture, so the idea of taking someone else's comes to them not so often + +## Plans + +The main part of the goals [is set out here][refs-mission--goals], but in addition, it is worth talking about our expectations from the methodology in the future + +### Combining experience + +Now we are trying to combine all our diverse experience of the `core-team`, and get a methodology hardened by practice as a result + +Of course, we can get Angular 3.0 as a result, but it is much more important here to **investigate the very problem of designing the architecture of complex systems** + +*And yes - we have complaints about the current version of the methodology, but we want to work together to come to a single and optimal solution (taking into account, among other things, the experience of the community)* + +### Life outside the specification + +If everything goes well, then the methodology will not be limited only to the specification and the toolkit + +- Perhaps there will be reports, articles +- There may be `CODE_MODEs` for migrations to other technologies of projects written according to the methodology +- It is possible that as a result we will be able to reach the maintainers of large technological solutions + - *Especially for React, compared to other frameworks - this is the main problem, because it does not say how to solve certain problems* + +## See also + +- [(Discussion) Don't need a methodology?][disc-src] +- [About the methodology's mission: goals and limitations][refs-mission] +- [Types of knowledge in the project][refs-knowledge] + +[refs-mission]: /docs/about/mission +[refs-mission--goals]: /docs/about/mission#goals +[refs-knowledge]: /docs/about/understanding/knowledge-types + +[disc-src]: https://github.com/feature-sliced/documentation/discussions/27 +[ext-discussions]: https://github.com/feature-sliced/documentation/discussions diff --git a/src/content/docs/about/promote/for-company.mdx b/src/content/docs/about/promote/for-company.mdx new file mode 100644 index 0000000000..a7c00b9e5d --- /dev/null +++ b/src/content/docs/about/promote/for-company.mdx @@ -0,0 +1,16 @@ +--- +title: Promote in company +sidebar: + order: 4 + badge: + text: WIP + variant: caution +--- + +## Do the project and the company need a methodology? + +> About the justification of the application, Those duty + +## How can I submit a methodology to a business? + +## How to prepare and justify a plan to move to the methodology? diff --git a/src/content/docs/about/promote/for-team.mdx b/src/content/docs/about/promote/for-team.mdx new file mode 100644 index 0000000000..fa48ce19e1 --- /dev/null +++ b/src/content/docs/about/promote/for-team.mdx @@ -0,0 +1,16 @@ +--- +title: Promote in team +sidebar: + order: 3 + badge: + text: WIP + variant: caution +--- + +- Onboard newcomers +- Development Guidelines ("where to search N module", etc...) +- New approach for tasks + +## See also +- [(Thread) The simplicity of the old approaches and the importance of mindfulness](https://t.me/feature_sliced/3360) +- [(Thread) About the convenience of searching by layers](https://t.me/feature_sliced/1918) diff --git a/src/content/docs/about/promote/integration.mdx b/src/content/docs/about/promote/integration.mdx new file mode 100644 index 0000000000..0fee7ab52f --- /dev/null +++ b/src/content/docs/about/promote/integration.mdx @@ -0,0 +1,25 @@ +--- +title: Integration aspects +sidebar: + order: 1 +--- + +## Summary + +First 5 minutes (RU): + + + +## Also + +**Advantages**: +- [Overview](/docs/get-started/overview) +- CodeReview +- Onboarding + + +**Disadvantages:** +- Mental complexity +- High entry threshold +- "Layers hell" +- Typical problems of feature-based approaches diff --git a/src/content/docs/about/promote/partial-application.mdx b/src/content/docs/about/promote/partial-application.mdx new file mode 100644 index 0000000000..e01b7e07d2 --- /dev/null +++ b/src/content/docs/about/promote/partial-application.mdx @@ -0,0 +1,10 @@ +--- +title: Partial Application +sidebar: + order: 2 + badge: + text: WIP + variant: caution +--- + +> How to partially apply the methodology? Does it make sense? What if I ignore it? diff --git a/src/content/docs/about/understanding/abstractions.mdx b/src/content/docs/about/understanding/abstractions.mdx new file mode 100644 index 0000000000..9fd9ace0fc --- /dev/null +++ b/src/content/docs/about/understanding/abstractions.mdx @@ -0,0 +1,24 @@ +--- +title: Abstractions +sidebar: + order: 6 + badge: + text: WIP + variant: caution +--- + +## The law of leaky abstractions + +## Why are there so many abstractions + +> Abstractions help to cope with the complexity of the project. The question is - will these abstractions be specific only for this project, or will we try to derive general abstractions based on the specifics of the frontend + +> Architecture and applications in general are inherently complex, and the only question is how to better distribute and describe this complexity + +## About scopes of responsibility + +> About optional abstractions + +## See also +- [About the need for new layers](https://t.me/feature_sliced/2801) +- [About the difficulty in understanding the methodology and layers](https://t.me/feature_sliced/2619) diff --git a/src/content/docs/about/understanding/architecture.mdx b/src/content/docs/about/understanding/architecture.mdx new file mode 100644 index 0000000000..f0972bfc68 --- /dev/null +++ b/src/content/docs/about/understanding/architecture.mdx @@ -0,0 +1,97 @@ +--- +title: About architecture +sidebar: + order: 1 +--- + +import { Aside } from '@astrojs/starlight/components'; + +## Problems + +Usually, the conversation about architecture is raised when the development stops due to certain problems in the project. + +### Bus-factor & Onboarding + +Only a limited number of people understand the project and its architecture + +**Examples:** + +- *"It's difficult to add a person to the development"* +- *"For every problem, everyone has their own opinion on how to get around" (let's envy the angular)* +- *"I don't understand what is happening in this big piece of monolith"* + +### Implicit and uncontrolled consequences + +A lot of implicit side effects during development/refactoring *("everything depends on everything")* + +**Examples:** + +- *"The feature imports the feature"* +- *"I updated the store of one page, and the functionality fell off on the other"* +- *"The logic is smeared all over the application, and it is impossible to track where the beginning is, where the end is"* + +### Uncontrolled reuse of logic + +It is difficult to reuse/modify existing logic + +At the same time, there are usually [two extremes](https://github.com/feature-sliced/documentation/discussions/14): + +- Either the logic is written completely from scratch for each module *(with possible repetitions in the existing codebase)* +- Either there is a tendency to transfer all-all implemented modules to `shared` folders, thereby creating a large dump of modules *from it (where most are used only in one place)* + +**Examples:** + +- *"I have **N** implementations of the same business logic in my project, for which I still pay"* +- *"There are 6 different components of the button/pop-up/... In the project"* +- *"Dump of helpers"* + +## Requirements + +Therefore, it seems logical to present the desired *requirements for an ideal architecture:* + + + +### Explicitness + +- It should be **easy to master and explain** the project and its architecture to the team +- The structure should reflect the real **business values of the project** +- There must be explicit **side effects and connections** between abstractions +- It should be **easy to detect duplicate logic** without interfering with unique implementations +- There should be no **dispersion of logic** throughout the project +- There should not be **too many heterogeneous abstractions and rules** for a good architecture + +### Control + +- A good architecture should **speed up the solution of tasks, the introduction of features** +- It should be possible to control the development of the project +- It should be easy to **expand, modify, delete the code** +- The * decomposition and isolation of** functionality must be observed +- Each component of the system must be **easily replaceable and removable** + - *[No need to optimize for changes][ext-kof-not-modification] - we can't predict the future* + - *[Better-optimize for deletion][ext-kof-but-removing] - based on the context that already exists* + +### Adaptability + +- A good architecture should be applicable **to most projects** + - *With existing infrastructure solutions* + - *At any stage of development* +- There should be no dependence on the framework and platform +- It should be possible to **easily scale the project and the team**, with the possibility of parallelization of development +- It should be easy **to adapt to changing requirements and circumstances** + +## See also + +- [(React Berlin Talk) Oleg Isonen - Feature Driven Architecture][ext-kof] +- [(React SPB Meetup #1) Sergey Sova - Feature Slices][ext-slices-spb] +- [(Article) About project modularization][ext-medium] +- [(Article) About Separation of Concerns and structuring by features][ext-ryanlanciaux] + +[ext-kof-not-modification]: https://youtu.be/BWAeYuWFHhs?t=1631 +[ext-kof-but-removing]: https://youtu.be/BWAeYuWFHhs?t=1666 + +[ext-slices-spb]: https://t.me/feature_slices +[ext-kof]: https://youtu.be/BWAeYuWFHhs +[ext-medium]: https://alexmngn.medium.com/why-react-developers-should-modularize-their-applications-d26d381854c1 +[ext-ryanlanciaux]: https://ryanlanciaux.com/blog/2017/08/20/a-feature-based-approach-to-react-development/ diff --git a/src/content/docs/about/understanding/knowledge-types.mdx b/src/content/docs/about/understanding/knowledge-types.mdx new file mode 100644 index 0000000000..88e2ca4f49 --- /dev/null +++ b/src/content/docs/about/understanding/knowledge-types.mdx @@ -0,0 +1,31 @@ +--- +title: Knowledge types in the project +sidebar: + label: Knowledge types + order: 3 +--- + +import { Aside } from '@astrojs/starlight/components'; + +The following "types of knowledge" can be distinguished in any project: + +* **Fundamental knowledge** + Knowledge that does not change much over time, such as algorithms, computer science, programming language mechanisms and its APIs. + +* **Technology stack** + Knowledge of the set of technical solutions used in a project, including programming languages, frameworks, and libraries. + +* **Project knowledge** + Knowledge that is specific to the current project and not valuable outside of it. This knowledge is essential for newly-onboarded developers to be able to contribute effectively. + + + +## See also \{#see-also\} + +- [(Video 🇷🇺) Ilya Klimov - On Types of Knowledge][ext-klimov] + +[ext-klimov]: https://youtu.be/4xyb_tA-uw0?t=249 diff --git a/src/content/docs/about/understanding/naming.mdx b/src/content/docs/about/understanding/naming.mdx new file mode 100644 index 0000000000..644366b492 --- /dev/null +++ b/src/content/docs/about/understanding/naming.mdx @@ -0,0 +1,48 @@ +--- +title: Naming +sidebar: + order: 4 +--- + +Different developers have different experiences and contexts, which can lead to misunderstandings on the team when the same entities are called differently. For example: + +- Components for display can be called "ui", "components", "ui-kit", "views", … +- The code that is reused throughout the application can be called "core", "shared", "app", … +- Business logic code can be called "store", "model", "state", … + +## Naming in Feature-Sliced Design \{#naming-in-fsd\} + +The methodology uses specific terms such as: + +- "app", "process", "page", "feature", "entity", "shared" as layer names, +- "ui', "model", "lib", "api", "config" as segment names. + +It is very important to stick to these terms to prevent confusion among team members and new developers joining the project. Using standard names also helps when asking for help from the community. + +## Naming Conflicts \{#when-can-naming-interfere\} + +Naming conflicts can occur when terms used in the FSD methodology overlap with terms used in the business: + +- `FSD#process` vs simulated process in an application, +- `FSD#page` vs log page, +- `FSD#model` vs car model. + +For example, a developer who sees the word "process" in the code will spend extra time trying to figure out what process is meant. Such **collisions can disrupt the development process**. + +When the project glossary contains terminology specific to FSD, it is critical to be careful when discussing these terms with the team and technical disinterested parties. + +To communicate effectively with the team, it is recommended that the abbreviation "FSD" be used to prefix the methodology terms. For example, when talking about a process, you might say, "We can put this process on the FSD features layer." + +Conversely, when communicating with non-technical stakeholders, it is better to limit the use of FSD terminology and refrain from mentioning the internal structure of the code base. + +## See also \{#see-also\} + +- [(Discussion) Adaptability of naming][disc-src] +- [(Discussion) Entity Naming Survey][disc-naming] +- [(Discussion) "processes" vs "flows" vs ...][disc-processes] +- [(Discussion) "model" vs "store" vs ...][disc-model] + +[disc-model]: https://github.com/feature-sliced/documentation/discussions/68 +[disc-naming]: https://github.com/feature-sliced/documentation/discussions/31#discussioncomment-464894 +[disc-processes]: https://github.com/feature-sliced/documentation/discussions/20 +[disc-src]: https://github.com/feature-sliced/documentation/discussions/16 diff --git a/src/content/docs/about/understanding/needs-driven.mdx b/src/content/docs/about/understanding/needs-driven.mdx new file mode 100644 index 0000000000..57763cc5ae --- /dev/null +++ b/src/content/docs/about/understanding/needs-driven.mdx @@ -0,0 +1,162 @@ +--- +title: Needs driven +sidebar: + order: 2 +--- + +import { Aside } from '@astrojs/starlight/components'; + + + +## Why? + +To choose a clear name for an entity and understand its components, **you need to clearly understand what task will be solved with the help of all this code.** + +> _@sergeysova: During development, we try to give each entity or function a name that clearly reflects the intentions and meaning of the code being executed._ + +_After all, without understanding the task, it is impossible to write the right tests that cover the most important cases, put down errors that help the user in the right places, even it is banal not to interrupt the user's flow because of fixable non-critical errors._ + +## What tasks are we talking about? + +Frontend develops applications and interfaces for end users, so we solve the tasks of these consumers. + +When a person comes to us, **he wants to solve some of his pain or close a need.** + +_The task of managers and analysts is to formulate this need, and implement developers taking into account the features of web development (loss of communication, backend error, typo, missed the cursor or finger)._ + +**This very goal, with which the user came, is the task of the developers.** + +> _One small solved problem is a feature in the Feature-Sliced Design methodology — you need to cut the entire scope of project tasks into small goals._ + +## How does this affect development? + +### Task decomposition + +When a developer begins to implement a task, in order to simplify the understanding and support of the code, he mentally **cuts it into stages**: + +* first _split into top-level entities_ and _implement them_, +* then these entities _split into smaller ones_ +* and so on + +_In the process of splitting into entities, the developer is forced to give them a name that would clearly reflect his idea and help to understand what task the code solves when reading the listing_ +_At the same time, we do not forget that we are trying to help the user reduce pain or realize needs_ + +### Understanding the essence of the task + +But to give a clear name to an entity, **the developer must know enough about its purpose** + +* how is he going to use this entity, +* what part of the user's task does it implement, where else can this entity be applied, +* in what other tasks can it participate, +* and so on + +It is not difficult to draw a conclusion: **while the developer will reflect on the name of entities within the framework of the methodology, he will be able to find poorly formulated tasks even before writing the code.** + +> How to give a name to an entity if you do not understand well what tasks it can solve, how can you even divide a task into entities if you do not understand it well? + +## How to formulate it? + +**To formulate a task that is solved by features, you need to understand the task itself**, and this is already the responsibility of the project manager and analysts. + +_The methodology can only tell the developer what tasks the product manager should pay close attention to._ + +> _@sergeysova: the Whole frontend is primarily a display of information, any component in the first turn, displays, and then the task "to show the user something" has no practical value._ +> +> _Even without taking into account the specifics of the frontend can ask, "why do I have to show you", so you can continue to ask until't get out of pain or the need of the consumer._ + +As soon as we were able to get to the basic needs or pains, we can go back and figure out **how exactly your product or service can help the user with his goals** + +Any new task in your tracker is aimed at solving business problems, and the business tries to solve the user's tasks at the same time earning money on it. This means that each task has certain goals, even if they are not spelled out in the description text. + +_**The developer must clearly understand what goal this or that task is pursuing**, but not every company can afford to build processes perfectly, although this is a separate conversation, nevertheless, the developer may well "ping" the right managers himself to find out this and do his part of the work effectively._ + +## And what is the benefit? + +Now let's look at the whole process from beginning to end. + +### 1. Understanding user tasks + +When a developer understands his pain and how the business closes them, he can offer solutions that are not available to the business due to the specifics of web development. + +> But of course, all this can work only if the developer is not indifferent to what he is doing and for what, otherwise _why then the methodology and some approaches?_ + +### 2. Structuring and ordering + +With the understanding of tasks comes **a clear structure both in the head and in the tasks along with the code** + +### 3. Understanding the feature and its components + +**One feature is one useful functionality for the user** + +* When several features are implemented in one feature, this is **a violation of borders** +* The feature can be indivisible and growing - **and this is not bad** +* **Bad** - when the feature does not answer the question _"What is the business value for the user?"_ +* There can be no "map-office" feature + * But `booking-meeting-on-the-map`, `search-for-an-employee`, `change-of-workplace` - **yes** + +> _@sergeysova: The point is that the feature contains only code that implements the functionality itself_, without unnecessary details and internal solutions (ideally)* +> +> *Open the feature code **and see only what relates to the task** - no more* + +### 4. Profit + +Business very rarely turns its course radically in the other direction, which means **the reflection of business tasks in the frontend application code is a very significant profit.** + +_Then you don't have to explain to each new team member what this or that code does, and in general why it was added - **everything will be explained through the business tasks that are already reflected in the code.**_ + +> What is called ["Business Language" in Domain Driven Development][ext-ubiq-lang] + +--- + +## Back to reality + +If business processes are understood and good names are given at the design stage - _then it is not particularly problematic to transfer this understanding and logic to the code._ + +**However, in practice**, tasks and functionality are usually developed "too" iteratively and (or) there is no time to think through the design. + +**As a result, the feature makes sense today, and if you expand this feature in a month, you can rewrite the gender of the project.** + +> *[[From the discussion][disc-src]]: The developer tries to think 2-3 steps ahead, taking into account future wishes, but here he rests on his own experience* +> +> _Burns experience engineer usually immediately looking 10 steps ahead, and understand where one feature to divide and combine with the other_ +> +> _But sometimes that comes the task which had to face the experience, and nowhere to take the understanding of how literacy to decompose, with the least unfortunate consequences in the future_ + +## The role of methodology + +**The methodology helps to solve the problems of developers, so that it is easier to solve the problems of users.** + +There is no solution to the problems of developers only for the sake of developers + +But in order for the developer to solve his tasks, **you need to understand the user's tasks** - on the contrary, it will not work + +### Methodology requirements + +It becomes clear that you need to identify at least two requirements for **Feature-Sliced Design**: + +1. The methodology should tell **how to create features, processes and entities** + + * Which means it should clearly explain _how to divide the code between them_, which means that the naming of these entities should also be laid down in the specification. + +2. The methodology should help the architecture **[easily adapt to the changing requirements of the project][refs-arch--adaptability]** + +## See also + +* [(Post) Stimulation for a clear formulation of tasks (+ discussion)][disc-src] + > _**The current article** is an adaptation of this discussion, you can read the full uncut version at the link_ +* [(Discussion) How to break the functionality and what it is][tg-src] +* [(Article) "How to better organize your applications"][ext-medium] + +[refs-arch--adaptability]: architecture#adaptability + +[ext-medium]: https://alexmngn.medium.com/how-to-better-organize-your-react-applications-2fd3ea1920f1 +[disc-src]: https://t.me/sergeysova/318 +[tg-src]: https://t.me/atomicdesign/18972 +[ext-ubiq-lang]: https://thedomaindrivendesign.io/developing-the-ubiquitous-language diff --git a/src/content/docs/about/understanding/signals.mdx b/src/content/docs/about/understanding/signals.mdx new file mode 100644 index 0000000000..07b8fba8a3 --- /dev/null +++ b/src/content/docs/about/understanding/signals.mdx @@ -0,0 +1,19 @@ +--- +title: Signals of architecture +sidebar: + order: 5 + badge: + text: WIP + variant: caution +--- + +> If there is a limitation on the part of the architecture, then there are obvious reasons for this, and consequences if they are ignored + +> The methodology and architecture gives signals, and how to deal with it depends on what risks you are ready to take on and what is most suitable for your team) + +## See also + +- [(Thread) About signals from architecture and dataflow](https://t.me/feature_sliced/2070) +- [(Thread) About the fundamental nature of architecture](https://t.me/feature_sliced/2492) +- [(Thread) About highlighting weak points](https://t.me/feature_sliced/3979) +- [(Thread) How to understand that the data model is swollen](https://t.me/feature_sliced/4228) From ba284d8a0ebb12cd6c701488dc64ae75689d90d0 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 17:25:46 +0100 Subject: [PATCH 23/45] docs: add llms.txt support --- astro.config.mjs | 5 +++++ package.json | 1 + pnpm-lock.yaml | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/astro.config.mjs b/astro.config.mjs index 99794513a8..fb8e8f3de0 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -2,10 +2,12 @@ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import remarkHeaderId from 'remark-heading-id'; +import starlightLlmsTxt from 'starlight-llms-txt'; // https://astro.build/config export default defineConfig({ outDir: './build', + site: 'https://fsd.how', markdown: { remarkPlugins: [remarkHeaderId], }, @@ -21,6 +23,9 @@ export default defineConfig({ src: './static/img/brand/logo-primary.png', replacesTitle: true, }, + plugins: [ + starlightLlmsTxt(), + ], locales: { root: { label: 'English', diff --git a/package.json b/package.json index cf2f0d30f4..10182964bb 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "remark-heading-id": "^1.0.1", "sass": "^1.93.2", "sharp": "^0.34.5", + "starlight-llms-txt": "^0.7.0", "stylelint": "^16.24.0", "stylelint-config-recess-order": "^5.1.1", "stylelint-config-recommended": "^14.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07ba7eb10c..a7982e2353 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -138,6 +138,9 @@ importers: sharp: specifier: ^0.34.5 version: 0.34.5 + starlight-llms-txt: + specifier: ^0.7.0 + version: 0.7.0(@astrojs/starlight@0.37.4(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)))(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)) stylelint: specifier: ^16.24.0 version: 16.24.0(typescript@5.9.2) @@ -2603,6 +2606,9 @@ packages: '@types/bonjour@3.5.13': resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + '@types/braces@3.0.5': + resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==} + '@types/connect-history-api-fallback@1.5.4': resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} @@ -2681,6 +2687,9 @@ packages: '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + '@types/micromatch@4.0.10': + resolution: {integrity: sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -7159,6 +7168,13 @@ packages: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} + starlight-llms-txt@0.7.0: + resolution: {integrity: sha512-KAay6JLXqB0GiNQ481z3Z/h/y4xeAU55TUGLz+npjxcRvN3h/7rDxjmyLiphZF8xfoqqSTduQPanl5Ct4Je6kA==} + engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} + peerDependencies: + '@astrojs/starlight': '>=0.31' + astro: ^5.15.9 + statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} @@ -7622,6 +7638,9 @@ packages: unist-util-remove-position@5.0.0: resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + unist-util-remove@4.0.0: + resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==} + unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} @@ -11254,6 +11273,8 @@ snapshots: dependencies: '@types/node': 22.18.6 + '@types/braces@3.0.5': {} + '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.6 @@ -11343,6 +11364,10 @@ snapshots: '@types/mdx@2.0.13': {} + '@types/micromatch@4.0.10': + dependencies: + '@types/braces': 3.0.5 + '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} @@ -16838,6 +16863,25 @@ snapshots: srcset@4.0.0: {} + starlight-llms-txt@0.7.0(@astrojs/starlight@0.37.4(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)))(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)): + dependencies: + '@astrojs/mdx': 4.3.13(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)) + '@astrojs/starlight': 0.37.4(astro@5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)) + '@types/hast': 3.0.4 + '@types/micromatch': 4.0.10 + astro: 5.16.16(@types/node@22.18.6)(jiti@1.21.7)(lightningcss@1.30.1)(rollup@4.57.0)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2) + github-slugger: 2.0.0 + hast-util-select: 6.0.4 + micromatch: 4.0.8 + rehype-parse: 9.0.1 + rehype-remark: 10.0.1 + remark-gfm: 4.0.1 + remark-stringify: 11.0.0 + unified: 11.0.5 + unist-util-remove: 4.0.0 + transitivePeerDependencies: + - supports-color + statuses@1.5.0: {} statuses@2.0.1: {} @@ -17386,6 +17430,12 @@ snapshots: '@types/unist': 3.0.3 unist-util-visit: 5.0.0 + unist-util-remove@4.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.2 + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 From 0cf06f4493f1aa39816477afe8cce45ad6131ea3 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 17:39:48 +0100 Subject: [PATCH 24/45] docs: follow exiting docs routing --- astro.config.mjs | 20 +++++++++---------- .../docs/{ => docs}/about/alternatives.mdx | 0 src/content/docs/{ => docs}/about/mission.mdx | 0 .../docs/{ => docs}/about/motivation.mdx | 0 .../{ => docs}/about/promote/for-company.mdx | 0 .../{ => docs}/about/promote/for-team.mdx | 0 .../{ => docs}/about/promote/integration.mdx | 0 .../about/promote/partial-application.mdx | 0 .../about/understanding/abstractions.mdx | 0 .../about/understanding/architecture.mdx | 0 .../about/understanding/knowledge-types.mdx | 0 .../{ => docs}/about/understanding/naming.mdx | 0 .../about/understanding/needs-driven.mdx | 0 .../about/understanding/signals.mdx | 0 .../docs/{ => docs}/get-started/faq.mdx | 0 .../docs/{ => docs}/get-started/overview.mdx | 2 +- .../docs/{ => docs}/get-started/tutorial.mdx | 16 +++++++-------- .../guides/examples/api-requests.mdx | 0 .../docs/{ => docs}/guides/examples/auth.mdx | 0 .../guides/examples/page-layout.mdx | 0 .../docs/{ => docs}/guides/examples/types.mdx | 0 .../guides/issues/cross-imports.mdx | 0 .../{ => docs}/guides/issues/desegmented.mdx | 0 .../guides/issues/excessive-entities.mdx | 0 .../guides/migration/from-custom.mdx | 0 .../{ => docs}/guides/migration/from-v1.mdx | 0 .../{ => docs}/guides/migration/from-v2-0.mdx | 0 .../{ => docs}/guides/tech/with-electron.mdx | 0 .../{ => docs}/guides/tech/with-nextjs.mdx | 0 .../{ => docs}/guides/tech/with-nuxtjs.mdx | 0 .../guides/tech/with-react-query.mdx | 0 .../{ => docs}/guides/tech/with-sveltekit.mdx | 0 .../docs/{ => docs}/reference/layers.mdx | 4 ++-- .../docs/{ => docs}/reference/public-api.mdx | 4 ++-- .../{ => docs}/reference/slices-segments.mdx | 6 +++--- src/content/docs/index.mdx | 2 +- 36 files changed, 27 insertions(+), 27 deletions(-) rename src/content/docs/{ => docs}/about/alternatives.mdx (100%) rename src/content/docs/{ => docs}/about/mission.mdx (100%) rename src/content/docs/{ => docs}/about/motivation.mdx (100%) rename src/content/docs/{ => docs}/about/promote/for-company.mdx (100%) rename src/content/docs/{ => docs}/about/promote/for-team.mdx (100%) rename src/content/docs/{ => docs}/about/promote/integration.mdx (100%) rename src/content/docs/{ => docs}/about/promote/partial-application.mdx (100%) rename src/content/docs/{ => docs}/about/understanding/abstractions.mdx (100%) rename src/content/docs/{ => docs}/about/understanding/architecture.mdx (100%) rename src/content/docs/{ => docs}/about/understanding/knowledge-types.mdx (100%) rename src/content/docs/{ => docs}/about/understanding/naming.mdx (100%) rename src/content/docs/{ => docs}/about/understanding/needs-driven.mdx (100%) rename src/content/docs/{ => docs}/about/understanding/signals.mdx (100%) rename src/content/docs/{ => docs}/get-started/faq.mdx (100%) rename src/content/docs/{ => docs}/get-started/overview.mdx (98%) rename src/content/docs/{ => docs}/get-started/tutorial.mdx (99%) rename src/content/docs/{ => docs}/guides/examples/api-requests.mdx (100%) rename src/content/docs/{ => docs}/guides/examples/auth.mdx (100%) rename src/content/docs/{ => docs}/guides/examples/page-layout.mdx (100%) rename src/content/docs/{ => docs}/guides/examples/types.mdx (100%) rename src/content/docs/{ => docs}/guides/issues/cross-imports.mdx (100%) rename src/content/docs/{ => docs}/guides/issues/desegmented.mdx (100%) rename src/content/docs/{ => docs}/guides/issues/excessive-entities.mdx (100%) rename src/content/docs/{ => docs}/guides/migration/from-custom.mdx (100%) rename src/content/docs/{ => docs}/guides/migration/from-v1.mdx (100%) rename src/content/docs/{ => docs}/guides/migration/from-v2-0.mdx (100%) rename src/content/docs/{ => docs}/guides/tech/with-electron.mdx (100%) rename src/content/docs/{ => docs}/guides/tech/with-nextjs.mdx (100%) rename src/content/docs/{ => docs}/guides/tech/with-nuxtjs.mdx (100%) rename src/content/docs/{ => docs}/guides/tech/with-react-query.mdx (100%) rename src/content/docs/{ => docs}/guides/tech/with-sveltekit.mdx (100%) rename src/content/docs/{ => docs}/reference/layers.mdx (93%) rename src/content/docs/{ => docs}/reference/public-api.mdx (96%) rename src/content/docs/{ => docs}/reference/slices-segments.mdx (94%) diff --git a/astro.config.mjs b/astro.config.mjs index fb8e8f3de0..60ecbacbc3 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -39,47 +39,47 @@ export default defineConfig({ sidebar: [ { label: '🚀 Get Started', - autogenerate: { directory: 'get-started' } + autogenerate: { directory: 'docs/get-started' } }, { label: '🎯 Guides', items: [ { label: 'Examples', - autogenerate: { directory: 'guides/examples' } + autogenerate: { directory: 'docs/guides/examples' } }, { label: 'Migration', - autogenerate: { directory: 'guides/migration' } + autogenerate: { directory: 'docs/guides/migration' } }, { label: 'Tech', - autogenerate: { directory: 'guides/tech' } + autogenerate: { directory: 'docs/guides/tech' } } ], }, { label: '📚 Reference', - autogenerate: { directory: 'reference' } + autogenerate: { directory: 'docs/reference' } }, { label: '🍰 About', items: [{ label: 'Mission', - slug: 'about/mission' + slug: 'docs/about/mission' }, { label: 'Motivation', - slug: 'about/motivation' + slug: 'docs/about/motivation' }, { label: 'Alternatives', - slug: 'about/alternatives' + slug: 'docs/about/alternatives' }, { label: 'Understanding', - autogenerate: { directory: 'about/understanding' }, + autogenerate: { directory: 'docs/about/understanding' }, collapsed: true, }, { label: 'Promote', - autogenerate: { directory: 'about/promote' }, + autogenerate: { directory: 'docs/about/promote' }, collapsed: true, }], } diff --git a/src/content/docs/about/alternatives.mdx b/src/content/docs/docs/about/alternatives.mdx similarity index 100% rename from src/content/docs/about/alternatives.mdx rename to src/content/docs/docs/about/alternatives.mdx diff --git a/src/content/docs/about/mission.mdx b/src/content/docs/docs/about/mission.mdx similarity index 100% rename from src/content/docs/about/mission.mdx rename to src/content/docs/docs/about/mission.mdx diff --git a/src/content/docs/about/motivation.mdx b/src/content/docs/docs/about/motivation.mdx similarity index 100% rename from src/content/docs/about/motivation.mdx rename to src/content/docs/docs/about/motivation.mdx diff --git a/src/content/docs/about/promote/for-company.mdx b/src/content/docs/docs/about/promote/for-company.mdx similarity index 100% rename from src/content/docs/about/promote/for-company.mdx rename to src/content/docs/docs/about/promote/for-company.mdx diff --git a/src/content/docs/about/promote/for-team.mdx b/src/content/docs/docs/about/promote/for-team.mdx similarity index 100% rename from src/content/docs/about/promote/for-team.mdx rename to src/content/docs/docs/about/promote/for-team.mdx diff --git a/src/content/docs/about/promote/integration.mdx b/src/content/docs/docs/about/promote/integration.mdx similarity index 100% rename from src/content/docs/about/promote/integration.mdx rename to src/content/docs/docs/about/promote/integration.mdx diff --git a/src/content/docs/about/promote/partial-application.mdx b/src/content/docs/docs/about/promote/partial-application.mdx similarity index 100% rename from src/content/docs/about/promote/partial-application.mdx rename to src/content/docs/docs/about/promote/partial-application.mdx diff --git a/src/content/docs/about/understanding/abstractions.mdx b/src/content/docs/docs/about/understanding/abstractions.mdx similarity index 100% rename from src/content/docs/about/understanding/abstractions.mdx rename to src/content/docs/docs/about/understanding/abstractions.mdx diff --git a/src/content/docs/about/understanding/architecture.mdx b/src/content/docs/docs/about/understanding/architecture.mdx similarity index 100% rename from src/content/docs/about/understanding/architecture.mdx rename to src/content/docs/docs/about/understanding/architecture.mdx diff --git a/src/content/docs/about/understanding/knowledge-types.mdx b/src/content/docs/docs/about/understanding/knowledge-types.mdx similarity index 100% rename from src/content/docs/about/understanding/knowledge-types.mdx rename to src/content/docs/docs/about/understanding/knowledge-types.mdx diff --git a/src/content/docs/about/understanding/naming.mdx b/src/content/docs/docs/about/understanding/naming.mdx similarity index 100% rename from src/content/docs/about/understanding/naming.mdx rename to src/content/docs/docs/about/understanding/naming.mdx diff --git a/src/content/docs/about/understanding/needs-driven.mdx b/src/content/docs/docs/about/understanding/needs-driven.mdx similarity index 100% rename from src/content/docs/about/understanding/needs-driven.mdx rename to src/content/docs/docs/about/understanding/needs-driven.mdx diff --git a/src/content/docs/about/understanding/signals.mdx b/src/content/docs/docs/about/understanding/signals.mdx similarity index 100% rename from src/content/docs/about/understanding/signals.mdx rename to src/content/docs/docs/about/understanding/signals.mdx diff --git a/src/content/docs/get-started/faq.mdx b/src/content/docs/docs/get-started/faq.mdx similarity index 100% rename from src/content/docs/get-started/faq.mdx rename to src/content/docs/docs/get-started/faq.mdx diff --git a/src/content/docs/get-started/overview.mdx b/src/content/docs/docs/get-started/overview.mdx similarity index 98% rename from src/content/docs/get-started/overview.mdx rename to src/content/docs/docs/get-started/overview.mdx index 12e94c65a5..f5ca709c1a 100644 --- a/src/content/docs/get-started/overview.mdx +++ b/src/content/docs/docs/get-started/overview.mdx @@ -61,7 +61,7 @@ Folders inside `📂 app`, `📂 shared`, and `📂 pages/article-reader` are ca Layers, slices, and segments form a hierarchy like this:
- ![Hierarchy of FSD concepts, described below](../../../../static/img/visual_schema.jpg) + ![Hierarchy of FSD concepts, described below](../../../../../static/img/visual_schema.jpg)

Pictured above: three pillars, labeled left to right as "Layers", "Slices", and "Segments" respectively.

diff --git a/src/content/docs/get-started/tutorial.mdx b/src/content/docs/docs/get-started/tutorial.mdx similarity index 99% rename from src/content/docs/get-started/tutorial.mdx rename to src/content/docs/docs/get-started/tutorial.mdx index fb4a543b48..7d2eb61f13 100644 --- a/src/content/docs/get-started/tutorial.mdx +++ b/src/content/docs/docs/get-started/tutorial.mdx @@ -10,7 +10,7 @@ import { FileTree, Code } from '@astrojs/starlight/components'; This tutorial will examine the Real World App, also known as Conduit. Conduit is a basic [Medium](https://medium.com/) clone — it lets you read and write articles as well as comment on the articles of others. -![Conduit home page](../../../../static/img/tutorial/realworld-feed-anonymous.jpg) +![Conduit home page](../../../../../static/img/tutorial/realworld-feed-anonymous.jpg) This is a pretty small application, so we will keep it simple and avoid excessive decomposition. It’s highly likely that the entire app will fit into just three layers: **App**, **Pages**, and **Shared**. If not, we will introduce additional layers as we go. Ready? @@ -48,14 +48,14 @@ In this case, a page is a slice, so modules (files) inside this page can only re ### Close look at the feed
- ![Anonymous user’s perspective](../../../../static/img/tutorial/realworld-feed-anonymous.jpg) + ![Anonymous user’s perspective](../../../../../static/img/tutorial/realworld-feed-anonymous.jpg)
_Anonymous user’s perspective_
- ![Authenticated user’s perspective](../../../../static/img/tutorial/realworld-feed-authenticated.jpg) + ![Authenticated user’s perspective](../../../../../static/img/tutorial/realworld-feed-authenticated.jpg)
_Authenticated user’s perspective_
@@ -146,7 +146,7 @@ In our case, the header is very simple — it’s a static logo and top-level na Let’s also examine a page that’s intended for editing, not reading. For example, the article writer: -![Conduit post editor](../../../../static/img/tutorial/realworld-editor-authenticated.jpg) +![Conduit post editor](../../../../../static/img/tutorial/realworld-editor-authenticated.jpg) It looks trivial, but contains several aspects of application development that we haven’t explored yet — form validation, error states, and data persistence. @@ -234,7 +234,7 @@ export default FeedPage; Then, if you run the dev server and open the application, you should see the Conduit banner! -![The banner of Conduit](../../../../static/img/tutorial/conduit-banner.jpg) +![The banner of Conduit](../../../../../static/img/tutorial/conduit-banner.jpg) ### API client @@ -1215,7 +1215,7 @@ export default function App() { At this point, you should end up with the following on the home page:
- ![The feed page of Conduit, including the header, the feed, and the tags. The tabs are still missing.](../../../../static/img/tutorial/realworld-feed-without-tabs.jpg) + ![The feed page of Conduit, including the header, the feed, and the tags. The tabs are still missing.](../../../../../static/img/tutorial/realworld-feed-without-tabs.jpg)
The feed page of Conduit, including the header, the feed, and the tags. The tabs are still missing.
@@ -1959,7 +1959,7 @@ export function Comments() { And with that our article reader is also complete! The buttons to follow the author, like a post, and leave a comment should now function as expected.
- ![Article reader with functioning buttons to like and follow](../../../../static/img/tutorial/realworld-article-reader.jpg) + ![Article reader with functioning buttons to like and follow](../../../../../static/img/tutorial/realworld-article-reader.jpg)
Article reader with functioning buttons to like and follow
@@ -2257,7 +2257,7 @@ export default ArticleEditPage; We’re done now! Log in and try creating a new article. Or “forget” to write the article and see the validation kick in.
- ![The Conduit article editor, with the title field saying “New article” and the rest of the fields empty. Above the form there are two errors: “**Describe what this article is about” and “Write the article itself”.**](../../../../static/img/tutorial/realworld-article-editor.jpg) + ![The Conduit article editor, with the title field saying “New article” and the rest of the fields empty. Above the form there are two errors: “**Describe what this article is about” and “Write the article itself”.**](../../../../../static/img/tutorial/realworld-article-editor.jpg)
The Conduit article editor, with the title field saying “New article” and the rest of the fields empty. Above the form there are two errors: **“Describe what this article is about”** and **“Write the article itself”**.
diff --git a/src/content/docs/guides/examples/api-requests.mdx b/src/content/docs/docs/guides/examples/api-requests.mdx similarity index 100% rename from src/content/docs/guides/examples/api-requests.mdx rename to src/content/docs/docs/guides/examples/api-requests.mdx diff --git a/src/content/docs/guides/examples/auth.mdx b/src/content/docs/docs/guides/examples/auth.mdx similarity index 100% rename from src/content/docs/guides/examples/auth.mdx rename to src/content/docs/docs/guides/examples/auth.mdx diff --git a/src/content/docs/guides/examples/page-layout.mdx b/src/content/docs/docs/guides/examples/page-layout.mdx similarity index 100% rename from src/content/docs/guides/examples/page-layout.mdx rename to src/content/docs/docs/guides/examples/page-layout.mdx diff --git a/src/content/docs/guides/examples/types.mdx b/src/content/docs/docs/guides/examples/types.mdx similarity index 100% rename from src/content/docs/guides/examples/types.mdx rename to src/content/docs/docs/guides/examples/types.mdx diff --git a/src/content/docs/guides/issues/cross-imports.mdx b/src/content/docs/docs/guides/issues/cross-imports.mdx similarity index 100% rename from src/content/docs/guides/issues/cross-imports.mdx rename to src/content/docs/docs/guides/issues/cross-imports.mdx diff --git a/src/content/docs/guides/issues/desegmented.mdx b/src/content/docs/docs/guides/issues/desegmented.mdx similarity index 100% rename from src/content/docs/guides/issues/desegmented.mdx rename to src/content/docs/docs/guides/issues/desegmented.mdx diff --git a/src/content/docs/guides/issues/excessive-entities.mdx b/src/content/docs/docs/guides/issues/excessive-entities.mdx similarity index 100% rename from src/content/docs/guides/issues/excessive-entities.mdx rename to src/content/docs/docs/guides/issues/excessive-entities.mdx diff --git a/src/content/docs/guides/migration/from-custom.mdx b/src/content/docs/docs/guides/migration/from-custom.mdx similarity index 100% rename from src/content/docs/guides/migration/from-custom.mdx rename to src/content/docs/docs/guides/migration/from-custom.mdx diff --git a/src/content/docs/guides/migration/from-v1.mdx b/src/content/docs/docs/guides/migration/from-v1.mdx similarity index 100% rename from src/content/docs/guides/migration/from-v1.mdx rename to src/content/docs/docs/guides/migration/from-v1.mdx diff --git a/src/content/docs/guides/migration/from-v2-0.mdx b/src/content/docs/docs/guides/migration/from-v2-0.mdx similarity index 100% rename from src/content/docs/guides/migration/from-v2-0.mdx rename to src/content/docs/docs/guides/migration/from-v2-0.mdx diff --git a/src/content/docs/guides/tech/with-electron.mdx b/src/content/docs/docs/guides/tech/with-electron.mdx similarity index 100% rename from src/content/docs/guides/tech/with-electron.mdx rename to src/content/docs/docs/guides/tech/with-electron.mdx diff --git a/src/content/docs/guides/tech/with-nextjs.mdx b/src/content/docs/docs/guides/tech/with-nextjs.mdx similarity index 100% rename from src/content/docs/guides/tech/with-nextjs.mdx rename to src/content/docs/docs/guides/tech/with-nextjs.mdx diff --git a/src/content/docs/guides/tech/with-nuxtjs.mdx b/src/content/docs/docs/guides/tech/with-nuxtjs.mdx similarity index 100% rename from src/content/docs/guides/tech/with-nuxtjs.mdx rename to src/content/docs/docs/guides/tech/with-nuxtjs.mdx diff --git a/src/content/docs/guides/tech/with-react-query.mdx b/src/content/docs/docs/guides/tech/with-react-query.mdx similarity index 100% rename from src/content/docs/guides/tech/with-react-query.mdx rename to src/content/docs/docs/guides/tech/with-react-query.mdx diff --git a/src/content/docs/guides/tech/with-sveltekit.mdx b/src/content/docs/docs/guides/tech/with-sveltekit.mdx similarity index 100% rename from src/content/docs/guides/tech/with-sveltekit.mdx rename to src/content/docs/docs/guides/tech/with-sveltekit.mdx diff --git a/src/content/docs/reference/layers.mdx b/src/content/docs/docs/reference/layers.mdx similarity index 93% rename from src/content/docs/reference/layers.mdx rename to src/content/docs/docs/reference/layers.mdx index 2488d9bb02..60054eda4f 100644 --- a/src/content/docs/reference/layers.mdx +++ b/src/content/docs/docs/reference/layers.mdx @@ -10,8 +10,8 @@ Layers are the first level of organisational hierarchy in Feature-Sliced Design. There are **7 layers** in total, arranged from most responsibility and dependency to least: -A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out. -A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out. +A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out. +A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out. 1. App 2. Processes (deprecated) diff --git a/src/content/docs/reference/public-api.mdx b/src/content/docs/docs/reference/public-api.mdx similarity index 96% rename from src/content/docs/reference/public-api.mdx rename to src/content/docs/docs/reference/public-api.mdx index 029e9f9ca4..d5fa6f5d5c 100644 --- a/src/content/docs/reference/public-api.mdx +++ b/src/content/docs/docs/reference/public-api.mdx @@ -70,8 +70,8 @@ Index files like `index.js`, also known as barrel files, are the most common way Circular import is when two or more files import each other in a circle.
- Three files importing each other in a circle - Three files importing each other in a circle + Three files importing each other in a circle + Three files importing each other in a circle
Pictured above: three files, `fileA.js`, `fileB.js`, and `fileC.js`, importing each other in a circle.
diff --git a/src/content/docs/reference/slices-segments.mdx b/src/content/docs/docs/reference/slices-segments.mdx similarity index 94% rename from src/content/docs/reference/slices-segments.mdx rename to src/content/docs/docs/reference/slices-segments.mdx index a203af8e48..fbe61f7e33 100644 --- a/src/content/docs/reference/slices-segments.mdx +++ b/src/content/docs/docs/reference/slices-segments.mdx @@ -17,8 +17,8 @@ The layers Shared and App don't contain slices. That is because Shared should co Slices are meant to be independent and highly cohesive groups of code files. The graphic below might help to visualize the tricky concepts of _cohesion_ and _coupling_:
- - + +
Image inspired by https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/
@@ -44,7 +44,7 @@ Read more about the rationale of public APIs and the best practices on creating Closely related slices can be structurally grouped in a folder, but they should exercise the same isolation rules as other slices — there should be **no code sharing** in that folder. -![Features "compose", "like" and "delete" grouped in a folder "post". In that folder there is also a file "some-shared-code.ts" that is crossed out to imply that it's not allowed.](../../../../static/img/graphic-nested-slices.svg) +![Features "compose", "like" and "delete" grouped in a folder "post". In that folder there is also a file "some-shared-code.ts" that is crossed out to imply that it's not allowed.](../../../../../static/img/graphic-nested-slices.svg) ## Segments diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx index 4efdc0a017..963aafa09d 100644 --- a/src/content/docs/index.mdx +++ b/src/content/docs/index.mdx @@ -7,5 +7,5 @@ template: splash import { LinkCard, CardGrid } from '@astrojs/starlight/components'; - + From 844e28f17eca26bac8f70c996e22ffe03eb78c30 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 18:59:19 +0100 Subject: [PATCH 25/45] add missing root pages --- src/content/docs/docs/branding.mdx | 81 ++++++++++++++++++++++++++++++ src/content/docs/docs/llms.mdx | 18 +++++++ 2 files changed, 99 insertions(+) create mode 100644 src/content/docs/docs/branding.mdx create mode 100644 src/content/docs/docs/llms.mdx diff --git a/src/content/docs/docs/branding.mdx b/src/content/docs/docs/branding.mdx new file mode 100644 index 0000000000..2c7993af07 --- /dev/null +++ b/src/content/docs/docs/branding.mdx @@ -0,0 +1,81 @@ +--- +title: Branding Guidelines +--- + +import { Aside } from '@astrojs/starlight/components'; + +FSD's visual identity is based on its core-concepts: `Layered`, `Sliced self-contained parts`, `Parts & Compose`, `Segmented`. +But also we tend to design simple, pretty identity, which should convey the FSD philisophy and be easy to recognize. + +**Please, use FSD's identity "as-is", without changes but with our assets for your comfort.** This brand guide will help you to use FSD's identity correctly. + + + +## Title + +- ✅ **Correct:** `Feature-Sliced Design`, `FSD` +- ❌ **Incorrect:** `Feature-Sliced`, `Feature Sliced`, `FeatureSliced`, `feature-sliced`, `feature sliced`, `FS` + +## Emojii + +The cake 🍰 image represents FSD core concepts quite well, so it has been chosen as our signature emoji + +> Example: *"🍰 Architectural design methodology for Frontend projects"* + +## Logo & Palette + +FSD has few variations of logo for different context, but it recommended to prefer **primary** + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ThemeLogo (Ctrl/Cmd + Click for download)Usage
primary
(#29BEDC, #517AED)
logo-primaryPreferred in most cases
flat
(#3193FF)
logo-flatFor one-color context
monochrome
(#FFF)
logo-monochromeFor grayscale context
square
(#3193FF)
logo-squareFor square boundaries
+ +## Banners & Schemes + +banner-primary +banner-monochrome + +## Social Preview + +Work in progress... + +## Presentation template + +Work in progress... + +## See also + +- [Discussion (github)](https://github.com/feature-sliced/documentation/discussions/399) +- [History of development with references (figma)](https://www.figma.com/file/RPphccpoeasVB0lMpZwPVR/FSD-Brand?node-id=0%3A1) diff --git a/src/content/docs/docs/llms.mdx b/src/content/docs/docs/llms.mdx new file mode 100644 index 0000000000..2d7af028fa --- /dev/null +++ b/src/content/docs/docs/llms.mdx @@ -0,0 +1,18 @@ +--- +title: Docs for LLMs +--- + +This page provides links and guidance for LLM crawlers. + +- Spec: https://llmstxt.org/ + +### Files + +- llms.txt +- llms-small.txt +- llms-full.txt + +### Notes + +- Files are served from the site root, regardless of the current page path. +- In deployments with a non-root base URL (e.g., `/documentation/`), the links above are automatically prefixed. From b841a29c65f17ba7cdadf328c53fe85df9f8f30b Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 18:59:34 +0100 Subject: [PATCH 26/45] fix language order and social links --- astro.config.mjs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index 60ecbacbc3..b54d246b7f 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -33,9 +33,28 @@ export default defineConfig({ }, ru: { label: 'Русский', - } + }, + uz: { + label: 'O\'zbekcha', + }, + kr: { + label: '한국어', + lang: 'ko', + }, + ja: { + label: '日本語', + }, + vi: { + label: 'Tiếng Việt', + }, + zh: { + label: '中文', + }, }, - social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }, { icon: 'discord', label: 'Discord', href: 'https://discord.gg/S8MzWTUsmp' }], + social: [ + { icon: 'github', label: 'GitHub', href: 'https://github.com/feature-sliced/documentation' }, + { icon: 'discord', label: 'Discord', href: 'https://discord.gg/S8MzWTUsmp' } + ], sidebar: [ { label: '🚀 Get Started', From 04fc231cad858e5195b3426207e0fd122dfb49c5 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 19:22:55 +0100 Subject: [PATCH 27/45] add sidebar ru translations --- astro.config.mjs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/astro.config.mjs b/astro.config.mjs index b54d246b7f..a9ab94b913 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -58,46 +58,89 @@ export default defineConfig({ sidebar: [ { label: '🚀 Get Started', + translations: { + ru: '🚀 Начало работы', + }, autogenerate: { directory: 'docs/get-started' } }, { label: '🎯 Guides', + translations: { + ru: '🎯 Гайды', + }, items: [ { label: 'Examples', + translations: { + ru: 'Примеры', + }, autogenerate: { directory: 'docs/guides/examples' } }, { label: 'Migration', + translations: { + ru: 'Миграция', + }, autogenerate: { directory: 'docs/guides/migration' } }, { label: 'Tech', + translations: { + ru: 'Технологии', + }, autogenerate: { directory: 'docs/guides/tech' } + }, + { + label: 'Code smells & Issues', + translations: { + ru: 'Известные проблемы', + }, + autogenerate: { directory: 'docs/guides/issues' } } ], }, { label: '📚 Reference', + translations: { + ru: '📚 Справочник', + }, autogenerate: { directory: 'docs/reference' } }, { label: '🍰 About', + translations: { + ru: '🍰 О нас', + }, items: [{ label: 'Mission', + translations: { + ru: 'Миссия', + }, slug: 'docs/about/mission' }, { label: 'Motivation', + translations: { + ru: 'Мотивация', + }, slug: 'docs/about/motivation' }, { label: 'Alternatives', + translations: { + ru: 'Альтернативы', + }, slug: 'docs/about/alternatives' }, { label: 'Understanding', + translations: { + ru: 'Понимание', + }, autogenerate: { directory: 'docs/about/understanding' }, collapsed: true, }, { label: 'Promote', + translations: { + ru: 'Продвижение', + }, autogenerate: { directory: 'docs/about/promote' }, collapsed: true, }], From 48001acdb6a6af76669373863ea1863af990ecd8 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 19:38:18 +0100 Subject: [PATCH 28/45] add ru translations for get-started --- src/content/docs/ru/docs/get-started/faq.mdx | 69 + .../docs/ru/docs/get-started/overview.mdx | 147 ++ .../docs/ru/docs/get-started/tutorial.mdx | 2262 +++++++++++++++++ 3 files changed, 2478 insertions(+) create mode 100644 src/content/docs/ru/docs/get-started/faq.mdx create mode 100644 src/content/docs/ru/docs/get-started/overview.mdx create mode 100644 src/content/docs/ru/docs/get-started/tutorial.mdx diff --git a/src/content/docs/ru/docs/get-started/faq.mdx b/src/content/docs/ru/docs/get-started/faq.mdx new file mode 100644 index 0000000000..dd35bf8c7e --- /dev/null +++ b/src/content/docs/ru/docs/get-started/faq.mdx @@ -0,0 +1,69 @@ +--- +title: FAQ +sidebar: + order: 3 +--- + +import { Aside } from '@astrojs/starlight/components'; + + + +### Существует ли тулкит или линтер? \{#is-there-a-toolkit-or-a-linter\} + +Да! У нас есть линтер [Steiger][ext-steiger] для проверки архитектуры вашего проекта и [генераторы папок][ext-tools] через CLI или IDE. + +### Где хранить layout/template страниц? \{#where-to-store-the-layouttemplate-of-pages\} + +Если вам нужны простые шаблоны разметки, вы можете хранить их в `shared/ui`. Если вам нужно использовать более высокие слои, есть несколько вариантов: + +- Возможно, вам вообще не нужны лейауты? Если макет состоит всего из нескольких строк, разумно будет дублировать код в каждой странице, а не пытаться абстрагировать его. +- Если вам нужны лейауты, вы можете хранить их как отдельные виджеты или страницы, и компоновать их в конфигурации роутера в App. Вложенный роутинг — еще один вариант. + +### В чем отличие feature от entity? \{#what-is-the-difference-between-feature-and-entity\} + +_Entity_ — это понятие из реальной жизни, с которым работает ваше приложение. _Feature_ — это взаимодействие, представляющее реальную ценность для пользователей; что-то, что люди хотят делать с сущностями. + +Для получения дополнительной информации, а также примеров, см. страницу [про слайсы][reference-entities] в разделе Reference. + +### Могу ли я вкладывать страницы/фичи/сущности друг в друга? \{#can-i-embed-pagesfeaturesentities-into-each-other\} + +Да, но это вложение должно происходить в более высоких слоях. Например, внутри виджета вы можете импортировать обе фичи, а затем вставить одну фичу в другую через пропсы/вложение. + +Вы не можете импортировать одну фичу из другой фичи, это запрещено [**правилом импортов для слоёв**][import-rule-layers]. + +### А что с Atomic Design? \{#what-about-atomic-design\} + +Текущая версия методологии не обязывает, но и не запрещает использовать Atomic Design вместе с Feature-Sliced Design. + +При этом Atomic Design [хорошо применяется](https://t.me/feature_sliced/1653) для `ui` сегмента модулей. + +### Есть ли какие-нибудь полезные ресурсы/статьи/т.д. по FSD? \{#are-there-any-useful-resourcesarticlesetc-about-fsd\} + +Да! https://github.com/feature-sliced/awesome + +### Зачем мне нужен Feature-Sliced Design? \{#why-do-i-need-feature-sliced-design\} + +Он помогает вам и вашей команде быстро ознакомиться с проектом с точки зрения его основных компонентов, приносящих бизнес-ценность. Стандартизированная архитектура помогает ускорить онбординг и разрешать споры о структуре кода. См. страницу [Мотивация][motivation], чтобы узнать больше о том, почему FSD был создан. + +### Нужна ли архитектура/методология начинающему разработчику? \{#does-a-novice-developer-need-an-architecturemethodology\} + +Скорее да, чем нет + +*Обычно, если проектировать и разрабатывать проект в одиночку - все идет гладко. Но если появляются паузы в разработке, добавляются новые разработчики в команду - тогда-то и наступают проблемы* + +### Как мне работать с контекстом авторизации? \{#how-do-i-work-with-the-authorization-context\} + +Ответили [здесь](/docs/guides/examples/auth) + +[ext-steiger]: https://github.com/feature-sliced/steiger +[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools +[import-rule-layers]: /docs/reference/layers#import-rule-on-layers +[reference-entities]: /docs/reference/layers#entities +[motivation]: /docs/about/motivation +[telegram]: https://t.me/feature_sliced +[discord]: https://discord.gg/S8MzWTUsmp +[github-discussions]: https://github.com/feature-sliced/documentation/discussions diff --git a/src/content/docs/ru/docs/get-started/overview.mdx b/src/content/docs/ru/docs/get-started/overview.mdx new file mode 100644 index 0000000000..4c2911bc68 --- /dev/null +++ b/src/content/docs/ru/docs/get-started/overview.mdx @@ -0,0 +1,147 @@ +--- +title: Обзор +sidebar: + order: 1 +--- + +import { Aside, FileTree } from '@astrojs/starlight/components'; + +**Feature-Sliced Design** (FSD) — это архитектурная методология для проектирования фронтенд-приложений. Проще говоря, это набор правил и соглашений по организации кода. Главная цель этой методологии — сделать проект понятнее и стабильнее в условиях постоянно меняющихся бизнес-требований. + +Помимо набора правил, FSD — это также целый инструментарий. У нас есть [линтер][ext-steiger] для проверки архитектуры вашего проекта, [генераторы папок][ext-tools] через CLI или IDE, а также богатая библиотека [примеров][examples]. + +## Подходит ли FSD мне? \{#is-it-right-for-me} + +FSD можно внедрять в проектах и командах любого размера. Она подходит для вашего проекта, если: + +- Вы занимаетесь **фронтенд**-разработкой (интерфейсы для сайтов, мобильных/десктопных приложений, и т. д.) +- Вы разрабатываете **приложение**, а не библиотеку + +И это все! Нет никаких ограничений на используемый вами язык программирования, фреймворк или стейт-менеджер. Ещё вы можете внедрять FSD постепенно, использовать его в монорепозиториях, и масштабировать его хоть до луны, разделяя ваше приложение на пакеты и внедряя FSD в каждом из них по отдельности. + +Если у вас уже есть архитектура, и вы подумываете перейти на FSD, убедитесь, что текущая архитектура **создает проблемы** в вашей команде. Например, если ваш проект стал слишком большим и переплетённым, чтобы эффективно разрабатывать новые функции, или если вы ожидаете, что в команду придет много новых участников. Если текущая архитектура работает, возможно, ее не стоит менять. Но если вы всё же решите перейти, ознакомьтесь с рекомендациями в разделе [Миграция][migration]. + +## Базовый пример \{#basic-example} + +Вот простой проект, реализующий FSD: + + +- app/ +- pages/ +- shared/ + + +Эти папки верхнего уровня называются _слоями_. Давайте посмотрим глубже: + + +- app/ + - routes/ + - analytics/ +- pages + - home/ + - article-reader/ + - ui/ + - api/ + - settings/ +- shared/ + - ui/ + - api/ + + +Папки внутри `📂 pages` называются _слайсами_. Они делят слой по домену (в данном случае, по страницам). + +Папки внутри `📂 app`, `📂 shared` и `📂 pages/article-reader` называются _сегментами_, и они делят слайсы (или слои) по техническому назначению, то есть по тому, для чего предназначен код. + +## Понятия \{#concepts} + +Слои, слайсы и сегменты образуют иерархию, как показано на схеме: + +
+ ![Иерархия концепций FSD, описанная ниже](../../../../../../static/img/visual_schema.jpg) + +
+

На картинке выше: три столбика, обозначенные слева направо как "Слои", "Слайсы" и "Сегменты" соответственно.

+

Столбик "Слои" содержит семь делений, расположенных сверху вниз и обозначенных "app", "processes", "pages", "widgets", "features", "entities" и "shared". Деление "processes" зачеркнуто. Деление "entities" соединено со вторым столбиком "Слайсы", показывая, что второй столбик является содержимым "entities".

+

Столбик "Слайсы" содержит три деления, расположенных сверху вниз и обозначенных "user", "post" и "comment". Деление "post" соединено со столбиком "Сегменты" таким же образом, что и содержимое "post".

+

Столбик "Сегменты" содержит три деления, расположенных сверху вниз и обозначенных "ui", "model" и "api".

+
+
+ +### Слои \{#layers} + +Слои стандартизированы во всех проектах FSD. Вам не обязательно использовать все слои, но их названия важны. На данный момент их семь (сверху вниз): + +1. **App** — всё, благодаря чему приложение запускается — роутинг, точки входа, глобальные стили, провайдеры и т. д. +2. **Processes** (процессы, устаревший) — сложные межстраничные сценарии. +3. **Pages** (страницы) — полные страницы или большие части страницы при вложенном роутинге. +4. **Widgets** (виджеты) — большие самодостаточные куски функциональности или интерфейса, обычно реализующие целый пользовательский сценарий. +5. **Features** (фичи) — _повторно используемые_ реализации целых фич продукта, то есть действий, приносящих бизнес-ценность пользователю. +6. **Entities** (сущности) — бизнес-сущности, с которыми работает проект, например `user` или `product`. +7. **Shared** — переиспользуемый код, особенно когда он отделён от специфики проекта/бизнеса, хотя это не обязательно. + + + +Фишка слоев в том, что модули на одном слое могут знать только о модулях со слоев строго ниже, и как следствие, импортировать только из них. + +### Слайсы \{#slices} + +Дальше идут слайсы, они делят слой по предметной области. Вы можете называть ваши слайсы как угодно, и создавать их сколько угодно. Слайсы помогают не теряться в проекте, потому что группируют тесно связанный по смыслу код. + +Слайсы не могут использовать другие слайсы на том же слое, и это обеспечивает сильную связанность кода внутри слайса и слабую сцепленность между слайсами. + +### Сегменты \{#segments} + +Слайсы, а также слои App и Shared, состоят из сегментов, а сегменты группируют код по его назначению. Имена сегментов не зафиксированы стандартом, но существует несколько общепринятых имен для наиболее распространенных целей: + +- `ui` — всё, что связано с отображением: UI-компоненты, форматтеры дат, стили и т.д. +- `api` — взаимодействие с бэкендом: функции запросов, типы данных, мапперы. +- `model` — модель данных: схемы валидации, интерфейсы, хранилища и бизнес-логика. +- `lib` — библиотечный код, который нужен другим модулям этого слайса. +- `config` — файлы конфигурации и фиче-флаги. + +Обычно этих сегментов достаточно для большинства слоев, поэтому свои собственные сегменты обычно создают только в Shared или App, но это не жёсткое правило. + +## Преимущества \{#advantages} + +- **Однородность** + Поскольку структура стандартизирована, проекты становятся более единообразными, что облегчает набор новых участников в команду. + +- **Устойчивость к изменениям и рефакторингу** + Модуль на одном слое не может использовать другие модули на том же слое или слоях выше. + Это позволяет вам вносить изолированные правки без непредвиденных последствий для остальной части приложения. + +- **Контролируемое переиспользование логики** + В зависимости от уровня вы можете сделать код либо очень переиспользуемым, либо очень локальным. + Это сохраняет баланс между соблюдением принципа **DRY** и практичностью. + +- **Ориентация на потребности бизнеса и пользователей** + Приложение разделено на бизнес-домены, и при именовании поощряется использование терминологии бизнеса, чтобы вы могли делать полезную работу в продукте, не вникая полностью во все другие несвязанные части проекта. + +## Постепенное внедрение \{#incremental-adoption} + +Если у вас есть существующая кодовая база, которую вы хотите перенести на FSD, мы предлагаем следующую стратегию. На нашем собственном опыте миграции она хорошо себя зарекомендовала. + +1. Начните постепенно формировать слои App и Shared, чтобы создать фундамент. + +2. Раскидайте весь существующий интерфейсный код по виджетам и страницам, даже если у них пока что есть зависимости, нарушающие правила FSD. + +3. Постепенно исправляйте нарушения правил на импорты, а по ходу извлекайте сущности и, возможно, фичи. + +Рекомендуется воздержаться от добавления новых крупных сущностей во время рефакторинга, а также рефакторинга по частям. + +## Следующие шаги \{#next-steps} + +- **Хотите разобраться в том, как мыслить по-FSD-шному?** Прочтите [Туториал][tutorial]. +- **Предпочитаете учиться на примерах?** У нас их много в разделе [Примеры][examples]. +- **Есть вопросы?** Загляните в наш [чат Telegram][ext-telegram] и спросите у сообщества. + +[tutorial]: /docs/get-started/tutorial +[examples]: /examples +[migration]: /docs/guides/migration/from-custom +[ext-steiger]: https://github.com/feature-sliced/steiger +[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools +[ext-telegram]: https://t.me/feature_sliced diff --git a/src/content/docs/ru/docs/get-started/tutorial.mdx b/src/content/docs/ru/docs/get-started/tutorial.mdx new file mode 100644 index 0000000000..3c3d614e8e --- /dev/null +++ b/src/content/docs/ru/docs/get-started/tutorial.mdx @@ -0,0 +1,2262 @@ +--- +title: Туториал +sidebar: + order: 2 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +## Часть 1. На бумаге + +В этом руководстве мы рассмотрим приложение Real World App, также известное как Conduit. Conduit является упрощённым клоном [Medium](https://medium.com/) — он позволяет вам читать и писать статьи в блогах, а также комментировать статьи других людей. + +![Главная страница Conduit](../../../../../../static/img/tutorial/realworld-feed-anonymous.jpg) + +Это довольно небольшое приложение, поэтому мы не станем сильно усложнять разработку излишней декомпозицией. Вероятнее всего, что всё приложение поместится в три слоя: **App**, **Pages** и **Shared**. Если нет, будем вводить дополнительные слои по ходу. Готовы? + +### Начните с перечисления страниц + +Если мы посмотрим на скриншот выше, мы можем предположить, что по крайней мере, есть следующие страницы: + +- Домашняя (лента статей) +- Войти и зарегистрироваться +- Просмотр статей +- Редактор статей +- Просмотр профилей людей +- Редактор профиля (настройки) + +Каждая из этих страниц станет отдельным *слайсом* на *слое* Pages. Вспомните из обзора, что слайсы — это просто папки внутри слоев, а слои — это просто папки с заранее определенными названиями, например, `pages`. + +Таким образом, наша папка Pages будет выглядеть так: + + +- pages/ + - feed/ лента + - sign-in/ войти/зарегистрироваться + - article-read/ просмотр статей + - article-edit/ редактор статей + - profile/ профиль + - settings/ настройки + + +Ключевое отличие Feature-Sliced Design от произвольной структуры кода заключается в том, что страницы не могут зависеть друг от друга. То есть одна страница не может импортировать код с другой страницы. Это связано с **правилом импорта для слоёв**: + +*Модуль (файл) в слайсе может импортировать другие слайсы только в том случае, если они расположены на слоях строго ниже.* + +В этом случае страница является слайсом, поэтому модули (файлы) внутри этой страницы могут импортировать код только из слоев ниже, а не из других страниц. + +### Пристальный взгляд на ленту + +
+ ![Перспектива анонимного посетителя](../../../../../../static/img/tutorial/realworld-feed-anonymous.jpg) +
+ _Перспектива анонимного посетителя_ +
+
+ +
+ ![Перспектива авторизованного пользователя](../../../../../../static/img/tutorial/realworld-feed-authenticated.jpg) +
+ _Перспектива авторизованного пользователя_ +
+
+ +На странице ленты есть три динамических области: + +1. Ссылки для логина, показывающие статус авторизации +2. Список тэгов, фильтрующих ленту +3. Одна—две ленты статей, у каждой статьи кнопка лайка + +Ссылки для логина — часть заголовка, общего для всех страниц, так что пока что отложим их. + +#### Список тэгов + +Чтобы создать список тэгов, нам нужно получить все доступные тэги, отобразить каждый тэг как чип ([chip](https://m3.material.io/components/chips/overview)) и сохранить выбранные тэги в хранилище на стороне клиента. Эти операции относятся к категориям «взаимодействие с API», «пользовательский интерфейс» и «хранение данных». В Feature-Sliced Design код делится по назначению с помощью *сегментов*. Сегменты — это папки в слайсах, и они могут иметь произвольные названия, описывающие их цель, но некоторые цели настолько распространены, что существует несколько общепринятых названий: + +- 📂 `api/` для взаимодействия с бэкендом +- 📂 `ui/` для кода, отвечающего за отображение и внешний вид +- 📂 `model/` для хранения данных и бизнес-логики +- 📂 `config/` для фиче-флагов, переменных окружения и других форм конфигурации + +Мы поместим код, который получает тэги, в `api`, сам компонент тэга в `ui`, а взаимодействие с хранилищем в `model`. + +#### Статьи + +Следуя той же логике, мы можем разбить ленту статей на те же три сегмента: + +- 📂 `api/`: получить постраничный список статей с количеством лайков, оставить лайк +- 📂 `ui/`: + - список вкладок, который может отображать дополнительную вкладку при выборе тэга + - отдельная статья + - рабочая пагинация +- 📂 `model/`: клиентское хранилище загруженных постов и текущей страницы (при необходимости) + +### Переиспользование общего кода + + Страницы, как правило, очень отличаются по своей цели, но что-то остается одинаковым по всему приложению — например, UI-кит, соответствующий языку дизайна, или соглашение на бэкенде, что все делается через REST API с конкретным методом аутентификации. Поскольку слайсы должны быть изолированными, переиспользование кода происходит за счёт слоя ниже, **Shared**. + +Shared отличается от других слоев тем, что он содержит сегменты, а не слайсы. Таким образом, слой Shared представляет собой гибрид между слоем и слайсом. + +Обычно код в Shared не планируется заранее, а извлекается по ходу разработки, потому что только во время разработки становится ясно, какие части кода действительно переиспользуются. Тем не менее, полезно держать в голове, какой код имеет смысл хранить в Shared: + +- 📂 `ui/` — UI-кит, только внешний вид, без бизнес-логики. Например, кнопки, диалоги, поля форм. +- 📂 `api/` — удобные обёртки вокруг запросов на бэкенд (например, обёртка над `fetch()` в случае веба) +- 📂 `config/` — обработка переменных окружения +- 📂 `i18n/` — конфигурация поддержки разных языков +- 📂 `router/` — примитивы и константы маршрутизации + +Это лишь примеры сегментов в Shared, вы можете опустить любой из них или создать свой собственный. Единственное, что нужно помнить при создании новых сегментов — названия сегментов должны описывать **цель (почему), а не суть (что)**. Такие названия как `components` , `hooks` или `modals` *не стоит* использовать, потому что они описывают, что содержат эти файлы по сути, а не то, с какой целью писался этот код. Как следствие таких названий, команде приходится копаться в таких папках, чтоб найти нужное. Помимо этого, несвязанный код лежит рядом, из-за чего при рефакторинге затрагивается большая часть приложения, что усложняет ревью и тестирование. + +### Определите строгий публичный API + +В контексте Feature-Sliced Design термин *публичный API* означает, что слайс или сегмент объявляет, что из него могут импортировать другие модули в проекте. Например, в JavaScript это может быть файл `index.js`, который переэкспортирует объекты из других файлов в слайсе. Это обеспечивает свободу рефакторинга внутри слайса до тех пор, пока контракт с внешним миром (т.е. публичный API) остается неизменным. + +Для слоя Shared, на котором нет слайсов, обычно удобнее определить публичный API (он же индекс) на уровне сегментов, а не один индекс на весь слой. В таком случае импорты из Shared естественным образом организуются по назначению. Для других слоев, на которых слайсы есть, верно обратное — обычно практичнее определить один индекс на слайс и позволить слайсу самому контролировать набор сегментов внутри, потому что другие слои обычно имеют гораздо меньше экспортов и чаще рефакторятся. + +Наши слайсы/сегменты будут выглядеть друг для друга следующим образом: + + +- pages/ + - feed/ + - index + - sign-in/ + - index + - article-read/ + - index + - ... +- shared/ + - ui/ + - index + - api/ + - index + - ... + + +Все, что находится внутри папок типа `pages/feed` или `shared/ui` , известно только этим папкам, и нет никаких гарантий по содержанию этих папок. + +### Крупные переиспользуемые блоки интерфейса + +Ранее мы хотели отдельно вернуться к переиспользуемому заголовку приложения. Собирать его заново на каждой странице было бы непрактично, поэтому мы его переиспользуем. У нас уже есть слой Shared для переиспользования кода, однако, в случае крупных блоков интерфейса в Shared есть нюанс — слой Shared не должен знать о слоях выше. + +Между слоями Shared и Pages есть три других слоя: Entities, Features и Widgets. В других проектах на этих слоях может лежать что-то, что хочется использовать в крупном переиспользуемом блоке, и тогда мы не сможем поместить этот блок в Shared, потому что тогда ему придется импортировать со слоёв выше, а это запрещено. Тут приходит на помощь слой Widgets. Он расположен выше Shared, Entities и Features, поэтому он может использовать их всех. + +В нашем случае заголовок очень простой — это статический логотип и навигация верхнего уровня. Навигация должна спросить у API, авторизован ли сейчас пользователь, но это может быть решено простым импортом из сегмента `api`. Поэтому мы оставим наш заголовок в Shared. + +### Пристальный взгляд на страницу с формой + +Давайте также рассмотрим страницу, на которой можно не только читать, но и редактировать. К примеру, редактор статей: + +![Редактор статей в Conduit](../../../../../../static/img/tutorial/realworld-editor-authenticated.jpg) + +Она выглядит тривиально, но содержит несколько аспектов разработки приложений, которые мы еще не исследовали — валидацию форм, состояние ошибки и постоянное хранение данных. + +Для создания этой страницы нам нужно несколько полей и кнопок из Shared, которые мы соберём в форму в сегменте `ui` этой страницы. Затем, в сегменте `api` мы определим изменяющий запрос, чтобы создать статью на бэкенде. + +Чтобы проверить запрос перед отправкой, нам нужна схема валидации, и хорошим местом для нее является сегмент `model` , поскольку это модель данных. Там же мы сгенерируем сообщение об ошибке, а отобразим его с помощью ещё одного компонента в сегменте `ui`. + +Чтобы улучшить пользовательский опыт, мы также можем сохранять введённые данные постоянно, чтобы предотвратить случайную потерю при закрытии браузера. Это тоже подходит под сегмент `model`. + +### Итоги + +Мы разобрали несколько страниц и пришли к базовой структуре нашего приложения: + +1. Слой Shared + 1. `ui` будет содержать наш переиспользуемый UI-кит + 2. `api` будет содержать наши примитивы для взаимодействия с бэкендом + 3. Остальное разложим по ходу написания кода +2. Слой Pages — для каждой страницы отдельный слайс + 1. `ui` будет содержать саму страницу и составляющие её блоки + 2. `api` будет содержать более специализированные функции получения данных, использующие `shared/api` + 3. `model` может содержать клиентское хранилище данных, которые мы будем отображать + +Давайте создадим это приложение! + +## Часть 2. В коде + +Теперь, когда у нас есть план, давайте воплотим его в жизнь. Мы будем использовать React и [Remix](https://remix.run/). + +Для этого проекта уже есть готовый шаблон, cклонируйте его с GitHub, чтобы начать работу: [https://github.com/feature-sliced/tutorial-conduit/tree/clean](https://github.com/feature-sliced/tutorial-conduit/tree/clean) + +Установите зависимости с помощью `npm install` и запустите сервер с помощью `npm run dev`. Откройте [http://localhost:3000](http://localhost:3000/), и вы увидите пустое приложение. + +### Разложим по страницам + +Давайте начнем с создания пустых компонентов для всех наших страниц. Выполните следующую команду в своем проекте: + +```bash +npx fsd pages feed sign-in article-read article-edit profile settings --segments ui +``` + +Это создаст папки наподобие `pages/feed/ui/` и индексный файл `pages/feed/index.ts` для каждой страницы. + +### Подключим страницу фида + +Давайте подключим корневой маршрут (`/`) нашего приложения к странице фида. Создайте компонент `FeedPage.tsx` в `pages/feed/ui` и поместите в него следующее: + +```tsx title="pages/feed/ui/FeedPage.tsx" +export function FeedPage() { + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+
+ ); +} +``` + +Затем ре-экспортируйте этот компонент в публичном API страницы фида, файл `pages/feed/index.ts`: + +```tsx title="pages/feed/index.ts" +export { FeedPage } from "./ui/FeedPage"; +``` + +Теперь подключите его к корневому маршруту. В Remix маршрутизация работает на файлах, и файлы маршрутов находятся в папке `app/routes`, что хорошо сочетается с Feature-Sliced Design. + +Используйте компонент `FeedPage` в `app/routes/_index.tsx`: + +```tsx title="app/routes/_index.tsx" +import type { MetaFunction } from "@remix-run/node"; +import { FeedPage } from "pages/feed"; + +export const meta: MetaFunction = () => { + return [{ title: "Conduit" }]; +}; + +export default FeedPage; +``` + +Затем, если вы запустите dev-сервер и откроете приложение, вы должны увидеть баннер Conduit! + +![Баннер Conduit](../../../../../../static/img/tutorial/conduit-banner.jpg) + +### API-клиент + +Чтобы общаться с бэкендом RealWorld, давайте создадим удобный API-клиент в Shared. Создайте два сегмента, `api` для клиента и `config` для таких переменных как базовый URL бэкенда: + +```bash +npx fsd shared --segments api config +``` + +Затем создайте `shared/config/backend.ts`: + +```tsx title="shared/config/backend.ts" +export { mockBackendUrl as backendBaseUrl } from "mocks/handlers"; +``` + +```tsx title="shared/config/index.ts" +export { backendBaseUrl } from "./backend"; +``` + +Поскольку проект RealWorld предоставляет [спецификацию OpenAPI](https://github.com/gothinkster/realworld/blob/main/api/openapi.yml), мы можем автоматически сгенерировать типы для нашего API-клиента. Мы будем использовать [пакет `openapi-fetch`](https://openapi-ts.pages.dev/openapi-fetch/), в котором дополнительно есть генератор типов. + +Выполните следующую команду, чтобы сгенерировать актуальные типы для API: + +```bash +npm run generate-api-types +``` + +В результате будет создан файл `shared/api/v1.d.ts`. Мы воспользуемся этим файлом в `shared/api/client.ts` для создания типизированного клиента API: + +```tsx title="shared/api/client.ts" +import createClient from "openapi-fetch"; + +import { backendBaseUrl } from "shared/config"; +import type { paths } from "./v1"; + +export const { GET, POST, PUT, DELETE } = createClient({ baseUrl: backendBaseUrl }); +``` + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; +``` + +### Реальные данные в ленте + +Теперь мы можем перейти к получению статей из бэкенда и добавлению их в ленту. Начнем с реализации компонента предпросмотра статьи. + +Создайте `pages/feed/ui/ArticlePreview.tsx` со следующим содержимым: + +```tsx title="pages/feed/ui/ArticlePreview.tsx" +export function ArticlePreview({ article }) { /* TODO */ } +``` + +Поскольку мы пишем на TypeScript, было бы неплохо иметь типизированный объект статьи Article. Если мы изучим сгенерированный `v1.d.ts`, то увидим, что объект Article доступен через `components["schemas"]["Article"]`. Поэтому давайте создадим файл с нашими моделями данных в Shared и экспортируем модели: + +```tsx title="shared/api/models.ts" +import type { components } from "./v1"; + +export type Article = components["schemas"]["Article"]; +``` + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; + +export type { Article } from "./models"; +``` + +Теперь мы можем вернуться к компоненту предпросмотра статьи и заполнить разметку данными. Обновите компонент, добавив в него следующее содержимое: + +```tsx title="pages/feed/ui/ArticlePreview.tsx" +import { Link } from "@remix-run/react"; +import type { Article } from "shared/api"; + +interface ArticlePreviewProps { + article: Article; +} + +export function ArticlePreview({ article }: ArticlePreviewProps) { + return ( +
+
+ + + +
+ + {article.author.username} + + + {new Date(article.createdAt).toLocaleDateString(undefined, { + dateStyle: "long", + })} + +
+ +
+ +

{article.title}

+

{article.description}

+ Read more... +
    + {article.tagList.map((tag) => ( +
  • + {tag} +
  • + ))} +
+ +
+ ); +} +``` + +Кнопка "Мне нравится" пока ничего не делает, мы исправим это, когда перейдем на страницу чтения статей и реализуем функцию "Мне нравится". + +Теперь мы можем получить статьи и отобразить кучу этих карточек предпросмотра. Получение данных в Remix осуществляется с помощью *загрузчиков* — серверных функций, которые собирают те данные, которые нужны странице. Загрузчики взаимодействуют с API от имени страницы, поэтому мы поместим их в сегмент `api` страницы: + +```tsx title="pages/feed/api/loader.ts" +import { json } from "@remix-run/node"; + +import { GET } from "shared/api"; + +export const loader = async () => { + const { data: articles, error, response } = await GET("/articles"); + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return json({ articles }); +}; +``` + +Чтобы подключить его к странице, нам нужно экспортировать его с именем `loader` из файла маршрута: + +```tsx title="pages/feed/index.ts" +export { FeedPage } from "./ui/FeedPage"; +export { loader } from "./api/loader"; +``` + +```tsx title="app/routes/_index.tsx" +import type { MetaFunction } from "@remix-run/node"; +import { FeedPage } from "pages/feed"; + +export { loader } from "pages/feed"; + +export const meta: MetaFunction = () => { + return [{ title: "Conduit" }]; +}; + +export default FeedPage; +``` + +И последний шаг — отображение этих карточек в ленте. Обновите `FeedPage` следующим кодом: + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; + +export function FeedPage() { + const { articles } = useLoaderData(); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ {articles.articles.map((article) => ( + + ))} +
+
+
+
+ ); +} +``` + +### Фильтрация по тегам + +Что касается тегов, то наша задача — получить их из бэкенда и запомнить выбранный пользователем тег. Мы уже знаем, как загружать из бэкенда — это еще один запрос от функции-загрузчика. Мы будем использовать удобную функцию `promiseHash` из пакета `remix-utils`, который уже установлен. + +Обновите файл загрузчика, `pages/feed/api/loader.ts`, следующим кодом: + +```tsx title="pages/feed/api/loader.ts" +import { json } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async () => { + return json( + await promiseHash({ + articles: throwAnyErrors(GET("/articles")), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +Вы можете заметить, что мы вынесли обработку ошибок в общую функцию `throwAnyErrors`. Она выглядит довольно полезной, так что, возможно, мы захотим переиспользовать её позже, а пока давайте просто заметим этот факт. + +Теперь перейдем к списку тегов. Он должен быть интерактивным - щелчок по тегу должен выбрать этот тег. По традиции Remix, мы будем использовать параметры запроса в URL в качестве хранилища для выбранного тега. Пусть браузер позаботится о хранилище, а мы сосредоточимся на более важных вещах. + +Обновите `pages/feed/ui/FeedPage.tsx` следующим кодом: + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { Form, useLoaderData } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import type { loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; + +export function FeedPage() { + const { articles, tags } = useLoaderData(); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ {articles.articles.map((article) => ( + + ))} +
+ +
+
+

Popular Tags

+ +
+ +
+ {tags.tags.map((tag) => ( + + ))} +
+ +
+
+
+
+
+ ); +} +``` + +Затем нам нужно использовать параметр поиска тегов в нашем загрузчике. Измените функцию `loader` в `pages/feed/api/loader.ts` на следующую: + +```tsx title="pages/feed/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const selectedTag = url.searchParams.get("tag") ?? undefined; + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles", { params: { query: { tag: selectedTag } } }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +И всё, сегмент `model` нам не понадобился. Remix — клёвая штука. + +### Пагинация + +Аналогичным образом мы можем реализовать пагинацию. Не стесняйтесь попробовать реализовать её сами или же просто скопируйте код ниже. В любом случае, осуждать вас некому. + +```tsx title="pages/feed/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +/** Amount of articles on one page. */ +export const LIMIT = 20; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const selectedTag = url.searchParams.get("tag") ?? undefined; + const page = parseInt(url.searchParams.get("page") ?? "", 10); + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles", { + params: { + query: { + tag: selectedTag, + limit: LIMIT, + offset: !Number.isNaN(page) ? page * LIMIT : undefined, + }, + }, + }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { Form, useLoaderData, useSearchParams } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import { LIMIT, type loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; + +export function FeedPage() { + const [searchParams] = useSearchParams(); + const { articles, tags } = useLoaderData(); + const pageAmount = Math.ceil(articles.articlesCount / LIMIT); + const currentPage = parseInt(searchParams.get("page") ?? "1", 10); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ {articles.articles.map((article) => ( + + ))} + +
+ +
    + {Array(pageAmount) + .fill(null) + .map((_, index) => + index + 1 === currentPage ? ( +
  • + {index + 1} +
  • + ) : ( +
  • + +
  • + ), + )} +
+ +
+ +
+
+

Popular Tags

+ +
+ +
+ {tags.tags.map((tag) => ( + + ))} +
+ +
+
+
+
+
+ ); +} +``` + +Ну вот, это тоже сделали. Есть еще список вкладок, который можно реализовать аналогичным образом, но давайте повременим с этим, пока не реализуем аутентификацию. Кстати, о ней! + +### Аутентификация \{#authentication} + +Аутентификация включает в себя две страницы — одну для входа в систему и другую для регистрации. Они, в основном, очень схожие, поэтому имеет смысл держать их в одном слайсе, `sign-in`, чтобы при необходимости можно было переиспользовать код. + +Создайте `RegisterPage.tsx` в сегменте `ui` в `pages/sign-in` со следующим содержимым: + +```tsx title="pages/sign-in/ui/RegisterPage.tsx" +import { Form, Link, useActionData } from "@remix-run/react"; + +import type { register } from "../api/register"; + +export function RegisterPage() { + const registerData = useActionData(); + + return ( +
+
+
+
+

Sign up

+

+ Have an account? +

+ + {registerData?.error && ( +
    + {registerData.error.errors.body.map((error) => ( +
  • {error}
  • + ))} +
+ )} + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ ); +} +``` + +Сейчас нам нужно исправить сломанный импорт. Он обращается к новому сегменту, поэтому создайте его: + +```bash +npx fsd pages sign-in -s api +``` + +Однако прежде чем мы сможем реализовать бэкенд-часть регистрации, нам нужен некоторый инфраструктурный код для Remix для обработки сессий. Отправим его в Shared, на случай, если он понадобится какой-либо другой странице. + +Поместите следующий код в `shared/api/auth.server.ts`. Этот код очень специфичен для Remix, так что не беспокойтесь, если там не все понятно, просто скопируйте и вставьте: + +```tsx title="shared/api/auth.server.ts" +import { createCookieSessionStorage, redirect } from "@remix-run/node"; +import invariant from "tiny-invariant"; + +import type { User } from "./models"; + +invariant( + process.env.SESSION_SECRET, + "SESSION_SECRET must be set for authentication to work", +); + +const sessionStorage = createCookieSessionStorage<{ + user: User; +}>({ + cookie: { + name: "__session", + httpOnly: true, + path: "/", + sameSite: "lax", + secrets: [process.env.SESSION_SECRET], + secure: process.env.NODE_ENV === "production", + }, +}); + +export async function createUserSession({ + request, + user, + redirectTo, +}: { + request: Request; + user: User; + redirectTo: string; +}) { + const cookie = request.headers.get("Cookie"); + const session = await sessionStorage.getSession(cookie); + + session.set("user", user); + + return redirect(redirectTo, { + headers: { + "Set-Cookie": await sessionStorage.commitSession(session, { + maxAge: 60 * 60 * 24 * 7, // 7 days + }), + }, + }); +} + +export async function getUserFromSession(request: Request) { + const cookie = request.headers.get("Cookie"); + const session = await sessionStorage.getSession(cookie); + + return session.get("user") ?? null; +} + +export async function requireUser(request: Request) { + const user = await getUserFromSession(request); + + if (user === null) { + throw redirect("/login"); + } + + return user; +} +``` + +А также экспортируйте модель `User` из файла `models.ts`, расположенного рядом с ним: + +```tsx title="shared/api/models.ts" +import type { components } from "./v1"; + +export type Article = components["schemas"]["Article"]; +export type User = components["schemas"]["User"]; +``` + +Прежде чем этот код заработает, необходимо установить переменную окружения `SESSION_SECRET`. Создайте файл `.env` в корне проекта, пропишите в нем `SESSION_SECRET=`, а затем пробегитесь по клавиатуре, чтобы создать длинную случайную строку. У вас должно получиться что-то вроде этого: + +```bash title=".env" +SESSION_SECRET=несмейтеэтокопировать +``` + +Наконец, добавьте несколько экспортов в публичный API, чтобы использовать этот код: + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; + +export type { Article } from "./models"; + +export { createUserSession, getUserFromSession, requireUser } from "./auth.server"; +``` + +Теперь мы можем написать код, который будет общаться с бэкендом RealWorld для регистрации. Мы сохраним его в `pages/sign-in/api`. Создайте файл `register.ts` и поместите в него следующий код: + +```tsx title="pages/sign-in/api/register.ts" +import { json, type ActionFunctionArgs } from "@remix-run/node"; + +import { POST, createUserSession } from "shared/api"; + +export const register = async ({ request }: ActionFunctionArgs) => { + const formData = await request.formData(); + const username = formData.get("username")?.toString() ?? ""; + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + + const { data, error } = await POST("/users", { + body: { user: { email, password, username } }, + }); + + if (error) { + return json({ error }, { status: 400 }); + } else { + return createUserSession({ + request: request, + user: data.user, + redirectTo: "/", + }); + } +}; +``` + +```tsx title="pages/sign-in/index.ts" +export { RegisterPage } from './ui/RegisterPage'; +export { register } from './api/register'; +``` + +Почти готово! Осталось подключить страницу и действие регистрации к маршруту `/register`. Создайте `register.tsx` в `app/routes`: + +```tsx title="app/routes/register.tsx" +import { RegisterPage, register } from "pages/sign-in"; + +export { register as action }; + +export default RegisterPage; +``` + +Теперь, если вы перейдете на [http://localhost:3000/register,](http://localhost:3000/register) вы сможете создать пользователя! Остальная часть приложения пока что на это не отреагирует, мы займемся этим в ближайшее время. + +Аналогичным образом мы можем реализовать страницу входа в систему. Попробуйте сами или просто возьмите код и двигайтесь дальше: + +```tsx title="pages/sign-in/api/sign-in.ts" +import { json, type ActionFunctionArgs } from "@remix-run/node"; + +import { POST, createUserSession } from "shared/api"; + +export const signIn = async ({ request }: ActionFunctionArgs) => { + const formData = await request.formData(); + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + + const { data, error } = await POST("/users/login", { + body: { user: { email, password } }, + }); + + if (error) { + return json({ error }, { status: 400 }); + } else { + return createUserSession({ + request: request, + user: data.user, + redirectTo: "/", + }); + } +}; +``` + +```tsx title="pages/sign-in/ui/SignInPage.tsx" +import { Form, Link, useActionData } from "@remix-run/react"; + +import type { signIn } from "../api/sign-in"; + +export function SignInPage() { + const signInData = useActionData(); + + return ( +
+
+
+
+

Sign in

+

+ Need an account? +

+ + {signInData?.error && ( +
    + {signInData.error.errors.body.map((error) => ( +
  • {error}
  • + ))} +
+ )} + +
+
+ +
+
+ +
+ +
+
+
+
+
+ ); +} +``` + +```tsx title="pages/sign-in/index.ts" +export { RegisterPage } from './ui/RegisterPage'; +export { register } from './api/register'; +export { SignInPage } from './ui/SignInPage'; +export { signIn } from './api/sign-in'; +``` + +```tsx title="app/routes/login.tsx" +import { SignInPage, signIn } from "pages/sign-in"; + +export { signIn as action }; + +export default SignInPage; +``` + +Теперь давайте дадим пользователям возможность попасть на эти страницы. + +### Хэдер + +Как мы уже говорили в первой части, хэдер приложения обычно размещается либо в Widgets, либо в Shared. Мы поместим его в Shared, потому что он очень прост, и вся бизнес-логика может быть сохранена за его пределами. Давайте создадим для него место: + +```bash +npx fsd shared ui +``` + +Теперь создайте `shared/ui/Header.tsx` со следующим содержимым: + +```tsx title="shared/ui/Header.tsx" +import { useContext } from "react"; +import { Link, useLocation } from "@remix-run/react"; + +import { CurrentUser } from "../api/currentUser"; + +export function Header() { + const currentUser = useContext(CurrentUser); + const { pathname } = useLocation(); + + return ( + + ); +} +``` + +Экспортируйте этот компонент из `shared/ui`: + +```tsx title="shared/ui/index.ts" +export { Header } from "./Header"; +``` + +В хэдере мы полагаемся на контекст, расположенный в `shared/api`. Создайте ещё его: + +```tsx title="shared/api/currentUser.ts" +import { createContext } from "react"; + +import type { User } from "./models"; + +export const CurrentUser = createContext(null); +``` + +```tsx title="shared/api/index.ts" +export { GET, POST, PUT, DELETE } from "./client"; + +export type { Article } from "./models"; + +export { createUserSession, getUserFromSession, requireUser } from "./auth.server"; +export { CurrentUser } from "./currentUser"; +``` + +Теперь давайте добавим хэдер на страницу. Мы хотим, чтобы он был на каждой странице, поэтому имеет смысл просто добавить его в корневой маршрут и обернуть аутлет (место, в которое будет отрендерена страница) провайдером контекста `CurrentUser`. Таким образом, все наше приложение, включая хэдер, получит доступ к объекту текущего пользователя. Мы также добавим загрузчик для получения объекта текущего пользователя из cookies. Добавьте следующее в `app/root.tsx`: + +```tsx title="app/root.tsx" +import { cssBundleHref } from "@remix-run/css-bundle"; +import type { LinksFunction, LoaderFunctionArgs } from "@remix-run/node"; +import { + Links, + LiveReload, + Meta, + Outlet, + Scripts, + ScrollRestoration, + useLoaderData, +} from "@remix-run/react"; + +import { Header } from "shared/ui"; +import { getUserFromSession, CurrentUser } from "shared/api"; + +export const links: LinksFunction = () => [ + ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), +]; + +export const loader = ({ request }: LoaderFunctionArgs) => + getUserFromSession(request); + +export default function App() { + const user = useLoaderData(); + + return ( + + + + + + + + + + + + + +
+ + + + + + + + ); +} +``` + +В итоге на главной странице должно получиться следующее: + +
+ ![Страница фида Conduit, на которой есть хэдер, фид и теги. Вкладки по-прежнему отсутствуют.](../../../../../../static/img/tutorial/realworld-feed-without-tabs.jpg) + +
Страница фида Conduit, на которой есть хэдер, фид и теги. Вкладки по-прежнему отсутствуют.
+
+ +### Вкладки + +Теперь, когда мы можем определить состояние аутентификации, давайте также быстренько реализуем вкладки и лайки, чтоб закончить со страницей ленты. Нам нужна еще одна форма, но этот файл страницы становится слишком большим, поэтому давайте перенесем эти формы в соседние файлы. Мы создадим `Tabs.tsx`, `PopularTags.tsx` и `Pagination.tsx` со следующим содержимым: + +```tsx title="pages/feed/ui/Tabs.tsx" +import { useContext } from "react"; +import { Form, useSearchParams } from "@remix-run/react"; + +import { CurrentUser } from "shared/api"; + +export function Tabs() { + const [searchParams] = useSearchParams(); + const currentUser = useContext(CurrentUser); + + return ( +
+
+
    + {currentUser !== null && ( +
  • + +
  • + )} +
  • + +
  • + {searchParams.has("tag") && ( +
  • + + {searchParams.get("tag")} + +
  • + )} +
+
+
+ ); +} +``` + +```tsx title="pages/feed/ui/PopularTags.tsx" +import { Form, useLoaderData } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import type { loader } from "../api/loader"; + +export function PopularTags() { + const { tags } = useLoaderData(); + + return ( +
+

Popular Tags

+ +
+ +
+ {tags.tags.map((tag) => ( + + ))} +
+ +
+ ); +} +``` + +```tsx title="pages/feed/ui/Pagination.tsx" +import { Form, useLoaderData, useSearchParams } from "@remix-run/react"; +import { ExistingSearchParams } from "remix-utils/existing-search-params"; + +import { LIMIT, type loader } from "../api/loader"; + +export function Pagination() { + const [searchParams] = useSearchParams(); + const { articles } = useLoaderData(); + const pageAmount = Math.ceil(articles.articlesCount / LIMIT); + const currentPage = parseInt(searchParams.get("page") ?? "1", 10); + + return ( +
+ +
    + {Array(pageAmount) + .fill(null) + .map((_, index) => + index + 1 === currentPage ? ( +
  • + {index + 1} +
  • + ) : ( +
  • + +
  • + ), + )} +
+ + ); +} +``` + +И теперь мы можем значительно упростить саму страницу с фидом: + +```tsx title="pages/feed/ui/FeedPage.tsx" +import { useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { ArticlePreview } from "./ArticlePreview"; +import { Tabs } from "./Tabs"; +import { PopularTags } from "./PopularTags"; +import { Pagination } from "./Pagination"; + +export function FeedPage() { + const { articles } = useLoaderData(); + + return ( +
+
+
+

conduit

+

A place to share your knowledge.

+
+
+ +
+
+
+ + + {articles.articles.map((article) => ( + + ))} + + +
+ +
+ +
+
+
+
+ ); +} +``` + +Нам также нужно учесть новую вкладку в функции-загрузчике: + +```tsx title="pages/feed/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET, requireUser } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + /* unchanged */ +} + +/** Amount of articles on one page. */ +export const LIMIT = 20; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const url = new URL(request.url); + const selectedTag = url.searchParams.get("tag") ?? undefined; + const page = parseInt(url.searchParams.get("page") ?? "", 10); + + if (url.searchParams.get("source") === "my-feed") { + const userSession = await requireUser(request); + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles/feed", { + params: { + query: { + limit: LIMIT, + offset: !Number.isNaN(page) ? page * LIMIT : undefined, + }, + }, + headers: { Authorization: `Token ${userSession.token}` }, + }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); + } + + return json( + await promiseHash({ + articles: throwAnyErrors( + GET("/articles", { + params: { + query: { + tag: selectedTag, + limit: LIMIT, + offset: !Number.isNaN(page) ? page * LIMIT : undefined, + }, + }, + }), + ), + tags: throwAnyErrors(GET("/tags")), + }), + ); +}; +``` + +Прежде чем мы отложим страницу ленты, давайте добавим код, который будет обрабатывать лайки к постам. Измените ваш `ArticlePreview.tsx` на следующий: + +```tsx title="pages/feed/ui/ArticlePreview.tsx" +import { Form, Link } from "@remix-run/react"; +import type { Article } from "shared/api"; + +interface ArticlePreviewProps { + article: Article; +} + +export function ArticlePreview({ article }: ArticlePreviewProps) { + return ( +
+
+ + + +
+ + {article.author.username} + + + {new Date(article.createdAt).toLocaleDateString(undefined, { + dateStyle: "long", + })} + +
+
+ +
+
+ +

{article.title}

+

{article.description}

+ Read more... +
    + {article.tagList.map((tag) => ( +
  • + {tag} +
  • + ))} +
+ +
+ ); +} +``` + +Этот код отправит POST-запрос на `/article/:slug` с `_action=favorite`, чтобы отметить статью как любимую. Пока это не работает, но как только мы начнем работать над читалкой статей, мы реализуем и это. + +И на этом мы официально закончили работу над фидом! Ура! + +### Читалка статей + +Во-первых, нам нужны данные. Давайте создадим загрузчик: + +```bash +npx fsd pages article-read -s api +``` + +```tsx title="pages/article-read/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import invariant from "tiny-invariant"; +import type { FetchResponse } from "openapi-fetch"; +import { promiseHash } from "remix-utils/promise"; + +import { GET, getUserFromSession } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async ({ request, params }: LoaderFunctionArgs) => { + invariant(params.slug, "Expected a slug parameter"); + const currentUser = await getUserFromSession(request); + const authorization = currentUser + ? { Authorization: `Token ${currentUser.token}` } + : undefined; + + return json( + await promiseHash({ + article: throwAnyErrors( + GET("/articles/{slug}", { + params: { + path: { slug: params.slug }, + }, + headers: authorization, + }), + ), + comments: throwAnyErrors( + GET("/articles/{slug}/comments", { + params: { + path: { slug: params.slug }, + }, + headers: authorization, + }), + ), + }), + ); +}; +``` + +```tsx title="pages/article-read/index.ts" +export { loader } from "./api/loader"; +``` + +Теперь мы можем подключить его к маршруту `/article/:slug`, создав файл маршрута `article.$slug.tsx`: + +```tsx title="app/routes/article.$slug.tsx" +export { loader } from "pages/article-read"; +``` + +Сама страница состоит из трех основных блоков — заголовка статьи с действиями (повторяется дважды), тела статьи и раздела комментариев. Это разметка страницы, она не особенно интересна: + +```tsx title="pages/article-read/ui/ArticleReadPage.tsx" +import { useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { ArticleMeta } from "./ArticleMeta"; +import { Comments } from "./Comments"; + +export function ArticleReadPage() { + const { article } = useLoaderData(); + + return ( +
+
+
+

{article.article.title}

+ + +
+
+ +
+
+
+

{article.article.body}

+
    + {article.article.tagList.map((tag) => ( +
  • + {tag} +
  • + ))} +
+
+
+ +
+ +
+ +
+ +
+ +
+
+
+ ); +} +``` + +Более интересными являются `ArticleMeta` и `Comments`. Они содержат операции записи, такие как лайкнуть статью, оставить комментарий и т. д. Чтобы они заработали, нам сначала нужно реализовать бэкенд-часть. Создайте файл `action.ts` в сегменте `api` этой страницы: + +```tsx title="pages/article-read/api/action.ts" +import { redirect, type ActionFunctionArgs } from "@remix-run/node"; +import { namedAction } from "remix-utils/named-action"; +import { redirectBack } from "remix-utils/redirect-back"; +import invariant from "tiny-invariant"; + +import { DELETE, POST, requireUser } from "shared/api"; + +export const action = async ({ request, params }: ActionFunctionArgs) => { + const currentUser = await requireUser(request); + + const authorization = { Authorization: `Token ${currentUser.token}` }; + + const formData = await request.formData(); + + return namedAction(formData, { + async delete() { + invariant(params.slug, "Expected a slug parameter"); + await DELETE("/articles/{slug}", { + params: { path: { slug: params.slug } }, + headers: authorization, + }); + return redirect("/"); + }, + async favorite() { + invariant(params.slug, "Expected a slug parameter"); + await POST("/articles/{slug}/favorite", { + params: { path: { slug: params.slug } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async unfavorite() { + invariant(params.slug, "Expected a slug parameter"); + await DELETE("/articles/{slug}/favorite", { + params: { path: { slug: params.slug } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async createComment() { + invariant(params.slug, "Expected a slug parameter"); + const comment = formData.get("comment"); + invariant(typeof comment === "string", "Expected a comment parameter"); + await POST("/articles/{slug}/comments", { + params: { path: { slug: params.slug } }, + headers: { ...authorization, "Content-Type": "application/json" }, + body: { comment: { body: comment } }, + }); + return redirectBack(request, { fallback: "/" }); + }, + async deleteComment() { + invariant(params.slug, "Expected a slug parameter"); + const commentId = formData.get("id"); + invariant(typeof commentId === "string", "Expected an id parameter"); + const commentIdNumeric = parseInt(commentId, 10); + invariant( + !Number.isNaN(commentIdNumeric), + "Expected a numeric id parameter", + ); + await DELETE("/articles/{slug}/comments/{id}", { + params: { path: { slug: params.slug, id: commentIdNumeric } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async followAuthor() { + const authorUsername = formData.get("username"); + invariant( + typeof authorUsername === "string", + "Expected a username parameter", + ); + await POST("/profiles/{username}/follow", { + params: { path: { username: authorUsername } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + async unfollowAuthor() { + const authorUsername = formData.get("username"); + invariant( + typeof authorUsername === "string", + "Expected a username parameter", + ); + await DELETE("/profiles/{username}/follow", { + params: { path: { username: authorUsername } }, + headers: authorization, + }); + return redirectBack(request, { fallback: "/" }); + }, + }); +}; +``` + +Реэкспортируйте её из слайса, а затем из маршрута. Пока мы здесь, давайте также подключим саму страницу: + +```tsx title="pages/article-read/index.ts" +export { ArticleReadPage } from "./ui/ArticleReadPage"; +export { loader } from "./api/loader"; +export { action } from "./api/action"; +``` + +```tsx title="app/routes/article.$slug.tsx" +import { ArticleReadPage } from "pages/article-read"; + +export { loader, action } from "pages/article-read"; + +export default ArticleReadPage; +``` + +Теперь, несмотря на то, что мы еще не реализовали кнопку лайка в читалке, кнопка лайка в ленте начнет работать! Это потому, что она тоже отправляет запросы на этот маршрут. Попробуйте лайкнуть что-нибудь. + +`ArticleMeta` и `Comments` — это, опять же, просто формы. Мы уже делали это раньше, давайте возьмем их код и пойдем дальше: + +```tsx title="pages/article-read/ui/ArticleMeta.tsx" +import { Form, Link, useLoaderData } from "@remix-run/react"; +import { useContext } from "react"; + +import { CurrentUser } from "shared/api"; +import type { loader } from "../api/loader"; + +export function ArticleMeta() { + const currentUser = useContext(CurrentUser); + const { article } = useLoaderData(); + + return ( +
+
+ + + + +
+ + {article.article.author.username} + + {article.article.createdAt} +
+ + {article.article.author.username == currentUser?.username ? ( + <> + + Edit Article + +    + + + ) : ( + <> + + +    + + + )} +
+
+ ); +} +``` + +```tsx title="pages/article-read/ui/Comments.tsx" +import { useContext } from "react"; +import { Form, Link, useLoaderData } from "@remix-run/react"; + +import { CurrentUser } from "shared/api"; +import type { loader } from "../api/loader"; + +export function Comments() { + const { comments } = useLoaderData(); + const currentUser = useContext(CurrentUser); + + return ( +
+ {currentUser !== null ? ( +
+
+ +
+
+ + +
+
+ ) : ( +
+
+

+ Sign in +   or   + Sign up +   to add comments on this article. +

+
+
+ )} + + {comments.comments.map((comment) => ( +
+
+

{comment.body}

+
+ +
+ + + +   + + {comment.author.username} + + {comment.createdAt} + {comment.author.username === currentUser?.username && ( + +
+ + +
+
+ )} +
+
+ ))} +
+ ); +} +``` + +А вместе с этим и наша читалка статей! Кнопки "Подписаться на автора", "Мне нравится" и "Оставить комментарий" теперь должны работать как положено. + +
+ ![Читалка статей с рабочими кнопками подписки и лайка](../../../../../../static/img/tutorial/realworld-article-reader.jpg) + +
Читалка статей с рабочими кнопками подписки и лайка
+
+ +### Редактор статей + +Это последняя страница, которую мы рассмотрим в этом руководстве, и самая интересная часть здесь — это то, как мы будем проверять данные формы. + +Сама страница, `article-edit/ui/ArticleEditPage.tsx`, будет довольно простой, дополнительная логика будет скрыта в двух других компонентах: + +```tsx title="pages/article-edit/ui/ArticleEditPage.tsx" +import { Form, useLoaderData } from "@remix-run/react"; + +import type { loader } from "../api/loader"; +import { TagsInput } from "./TagsInput"; +import { FormErrors } from "./FormErrors"; + +export function ArticleEditPage() { + const article = useLoaderData(); + + return ( +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+
+
+
+
+
+ ); +} +``` + +Эта страница получает текущую статью (если мы пишем статью не с нуля) и заполняет соответствующие поля формы. Мы уже видели это. Интересной частью является `FormErrors`, потому что он будет получать результат проверки и отображать его пользователю. Давайте посмотрим: + +```tsx title="pages/article-edit/ui/FormErrors.tsx" +import { useActionData } from "@remix-run/react"; +import type { action } from "../api/action"; + +export function FormErrors() { + const actionData = useActionData(); + + return actionData?.errors != null ? ( +
    + {actionData.errors.map((error) => ( +
  • {error}
  • + ))} +
+ ) : null; +} +``` + +Здесь мы предполагаем, что наш экшн будет возвращать поле `errors`, массив понятных человеку сообщений об ошибках. К экшну мы перейдем чуть позже. + +Еще один компонент — это поле ввода тегов. Это обычное поле ввода с дополнительным предпросмотром выбранных тегов. Здесь особо не на что смотреть: + +```tsx title="pages/article-edit/ui/TagsInput.tsx" +import { useEffect, useRef, useState } from "react"; + +export function TagsInput({ + name, + defaultValue, +}: { + name: string; + defaultValue?: Array; +}) { + const [tagListState, setTagListState] = useState(defaultValue ?? []); + + function removeTag(tag: string): void { + const newTagList = tagListState.filter((t) => t !== tag); + setTagListState(newTagList); + } + + const tagsInput = useRef(null); + useEffect(() => { + tagsInput.current && (tagsInput.current.value = tagListState.join(",")); + }, [tagListState]); + + return ( + <> + + setTagListState(e.target.value.split(",").filter(Boolean)) + } + /> +
+ {tagListState.map((tag) => ( + + + [" ", "Enter"].includes(e.key) && removeTag(tag) + } + onClick={() => removeTag(tag)} + >{" "} + {tag} + + ))} +
+ + ); +} +``` + +Теперь перейдем к API-части. Загрузчик должен посмотреть на URL, и если в нем есть ссылка на статью, это означает, что мы редактируем существующую статью, и ее данные должны быть загружены. В противном случае ничего не возвращается. Давайте создадим этот загрузчик: + +```tsx title="pages/article-edit/api/loader.ts" +import { json, type LoaderFunctionArgs } from "@remix-run/node"; +import type { FetchResponse } from "openapi-fetch"; + +import { GET, requireUser } from "shared/api"; + +async function throwAnyErrors( + responsePromise: Promise>, +) { + const { data, error, response } = await responsePromise; + + if (error !== undefined) { + throw json(error, { status: response.status }); + } + + return data as NonNullable; +} + +export const loader = async ({ params, request }: LoaderFunctionArgs) => { + const currentUser = await requireUser(request); + + if (!params.slug) { + return { article: null }; + } + + return throwAnyErrors( + GET("/articles/{slug}", { + params: { path: { slug: params.slug } }, + headers: { Authorization: `Token ${currentUser.token}` }, + }), + ); +}; +``` + +Экшн примет новые значения полей, прогонит их через нашу схему данных и, если все правильно, зафиксирует изменения в бэкенде, либо обновив существующую статью, либо создав новую: + +```tsx title="pages/article-edit/api/action.ts" +import { json, redirect, type ActionFunctionArgs } from "@remix-run/node"; + +import { POST, PUT, requireUser } from "shared/api"; +import { parseAsArticle } from "../model/parseAsArticle"; + +export const action = async ({ request, params }: ActionFunctionArgs) => { + try { + const { body, description, title, tags } = parseAsArticle( + await request.formData(), + ); + const tagList = tags?.split(",") ?? []; + + const currentUser = await requireUser(request); + const payload = { + body: { + article: { + title, + description, + body, + tagList, + }, + }, + headers: { Authorization: `Token ${currentUser.token}` }, + }; + + const { data, error } = await (params.slug + ? PUT("/articles/{slug}", { + params: { path: { slug: params.slug } }, + ...payload, + }) + : POST("/articles", payload)); + + if (error) { + return json({ errors: error }, { status: 422 }); + } + + return redirect(`/article/${data.article.slug ?? ""}`); + } catch (errors) { + return json({ errors }, { status: 400 }); + } +}; +``` + +Наша схема данных будет ещё и парсить `FormData`, что позволяет нам удобно получать чистые поля или просто бросать ошибки для обработки в конце. Вот как может выглядеть эта функция парсинга: + +```tsx title="pages/article-edit/model/parseAsArticle.ts" +export function parseAsArticle(data: FormData) { + const errors = []; + + const title = data.get("title"); + if (typeof title !== "string" || title === "") { + errors.push("Give this article a title"); + } + + const description = data.get("description"); + if (typeof description !== "string" || description === "") { + errors.push("Describe what this article is about"); + } + + const body = data.get("body"); + if (typeof body !== "string" || body === "") { + errors.push("Write the article itself"); + } + + const tags = data.get("tags"); + if (typeof tags !== "string") { + errors.push("The tags must be a string"); + } + + if (errors.length > 0) { + throw errors; + } + + return { title, description, body, tags: data.get("tags") ?? "" } as { + title: string; + description: string; + body: string; + tags: string; + }; +} +``` + +Возможно, она покажется немного длинной и повторяющейся, но такова цена, которую мы платим за читаемые сообщения об ошибках. Это может быть и схема Zod, например, но тогда нам придется выводить сообщения об ошибках на фронтенде, а эта форма не стоит таких сложностей. + +Последний шаг — подключение страницы, загрузчика и действия к маршрутам. Поскольку мы аккуратно поддерживаем и создание, и редактирование, мы можем экспортировать одно и то же действие как из `editor._index.tsx`, так и из `editor.$slug.tsx`: + +```tsx title="pages/article-edit/index.ts" +export { ArticleEditPage } from "./ui/ArticleEditPage"; +export { loader } from "./api/loader"; +export { action } from "./api/action"; +``` + +```tsx title="app/routes/editor._index.tsx, app/routes/editor.$slug.tsx (одинаковое содержимое)" +import { ArticleEditPage } from "pages/article-edit"; + +export { loader, action } from "pages/article-edit"; + +export default ArticleEditPage; +``` + +Мы закончили! Войдите в систему и попробуйте создать новую статью. Или “забудьте” написать статью и посмотрите, как сработает валидация. + +
+ ![Редактор статей Conduit, в поле заголовка которого написано “New article”, а остальные поля пусты. Над формой есть две ошибки: “**Describe what this article is about**” и “**Write the article itself**”.](../../../../../../static/img/tutorial/realworld-article-editor.jpg) + +
Редактор статей Conduit, в поле заголовка которого написано “New article”, а остальные поля пусты. Над формой есть две ошибки: **“Describe what this article is about”** и **“Write the article itself**”.
+
+ +Страницы профиля и настроек очень похожи на страницы чтения и редактирования статей, они оставлены в качестве упражнения для читателя, то есть для вас :) From 9f28aab9f595d1fe1b2272255dd4e693e7c93fb6 Mon Sep 17 00:00:00 2001 From: Solant Date: Fri, 6 Feb 2026 19:56:58 +0100 Subject: [PATCH 29/45] migrate ru/docs/about articles --- .../docs/ru/docs/about/alternatives.mdx | 141 +++++++++++++++ src/content/docs/ru/docs/about/mission.mdx | 50 ++++++ src/content/docs/ru/docs/about/motivation.mdx | 150 ++++++++++++++++ .../ru/docs/about/promote/for-company.mdx | 16 ++ .../docs/ru/docs/about/promote/for-team.mdx | 16 ++ .../ru/docs/about/promote/integration.mdx | 25 +++ .../about/promote/partial-application.mdx | 10 ++ .../docs/about/understanding/abstractions.mdx | 24 +++ .../docs/about/understanding/architecture.mdx | 99 +++++++++++ .../about/understanding/knowledge-types.mdx | 31 ++++ .../ru/docs/about/understanding/naming.mdx | 48 ++++++ .../docs/about/understanding/needs-driven.mdx | 161 ++++++++++++++++++ .../ru/docs/about/understanding/signals.mdx | 19 +++ 13 files changed, 790 insertions(+) create mode 100644 src/content/docs/ru/docs/about/alternatives.mdx create mode 100644 src/content/docs/ru/docs/about/mission.mdx create mode 100644 src/content/docs/ru/docs/about/motivation.mdx create mode 100644 src/content/docs/ru/docs/about/promote/for-company.mdx create mode 100644 src/content/docs/ru/docs/about/promote/for-team.mdx create mode 100644 src/content/docs/ru/docs/about/promote/integration.mdx create mode 100644 src/content/docs/ru/docs/about/promote/partial-application.mdx create mode 100644 src/content/docs/ru/docs/about/understanding/abstractions.mdx create mode 100644 src/content/docs/ru/docs/about/understanding/architecture.mdx create mode 100644 src/content/docs/ru/docs/about/understanding/knowledge-types.mdx create mode 100644 src/content/docs/ru/docs/about/understanding/naming.mdx create mode 100644 src/content/docs/ru/docs/about/understanding/needs-driven.mdx create mode 100644 src/content/docs/ru/docs/about/understanding/signals.mdx diff --git a/src/content/docs/ru/docs/about/alternatives.mdx b/src/content/docs/ru/docs/about/alternatives.mdx new file mode 100644 index 0000000000..829747b0f8 --- /dev/null +++ b/src/content/docs/ru/docs/about/alternatives.mdx @@ -0,0 +1,141 @@ +--- +title: Альтернативы +sidebar: + order: 3 + badge: + text: WIP + variant: caution +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +История архитектурных подходов + +## Big Ball of Mud + +> Что это; Почему так распространено; Когда это начинает приносить проблемы; Как быть; И как помогает в этом FSD + +- [(Статья) Oleg Isonen - Last words on UI architecture before an AI takes over](https://oleg008.medium.com/last-words-on-ui-architecture-before-an-ai-takes-over-468c78f18f0d) +- [(Доклад) Юлия Николаева, iSpring - Big Ball of Mud и другие проблемы монолита, с которыми мы справились](https://youtu.be/gna4Ynz1YNI) +- [(Статья) DDD - Big Ball of mud](https://thedomaindrivendesign.io/big-ball-of-mud/) + + +## Smart & Dumb components + +> О подходе; О применимости в фронтенде; позиция FSD + +Про устарелость, про новый взгляд со стороны методологии + +Почему компонентно/контейнерный подход - зло + + +- [(Статья) Dan Abramov - Presentational and Container Components (TLDR: deprecated)](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) + + +## Design Principles + +> Про что речь; позиция FSD + +SOLID, GRASP, KISS, YAGNI, ... - и почему они плохо работают вместе на практике + +И как она агрегирует эти практики + +- [(Доклад) Илья Азин - Feature-Sliced Design (фрагмент про Принципы проектирования)](https://youtu.be/SnzPAr_FJ7w?t=380) + + +## DDD + +> О подходе; Почему плохо работает на практике + +В чем отличие, чем улучшает применимость, где перенимает практики + +## См. также \{#see-also\} + +- [(Статья) DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/) +- [(Доклад) Илья Азин - Feature-Sliced Design (фрагмент про Clean Architecture, DDD)](https://youtu.be/SnzPAr_FJ7w?t=528) + +## Clean Architecture + +> О подходе; О применимости в фронтенде; позиция FSD + +В чем схожи (многим), чем отличаются + +- [(Тред) Про use-case/interactor в методологии](https://t.me/feature_sliced/3897) +- [(Тред) Про DI в методологии](https://t.me/feature_sliced/4592) +- [(Статья) Александр Беспоясов - Чистая архитектура на фронтенде](https://bespoyasov.ru/blog/clean-architecture-on-frontend/) +- [(Статья) DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/) +- [(Доклад) Илья Азин - Feature-Sliced Design (фрагмент про Clean Architecture, DDD)](https://youtu.be/SnzPAr_FJ7w?t=528) +- [(Статья) Заблуждения Clean Architecture](https://habr.com/ru/company/mobileup/blog/335382/) + + +## Frameworks + +> О применимости в фронтенде; Почему они не решают проблему; Почему это не единственный подход; позиция FSD + +Framework-agnostic, conventional-подход + +- [(Статья) Про причины создания методологии (фрагмент про фреймворки)](/docs/about/motivation) +- [(Тред) Про применимость методологии для разных фреймворков](https://t.me/feature_sliced/3867) + + +## Atomic Design + +### Что это? + +В Atomic Design сфера ответственности разделена на стандартизированные слои. + +Atomic Design разбивается на **5 слоев** (сверху вниз): + +1. `pages` - Назначение аналогично слою `pages` в FSD. +2. `templates` - Компоненты задающие структуру страницы, без привязки к контенту. +3. `organisms` - Модули состоящие из молекул, обладающие бизнес логикой. +4. `moleculs` - Более сложные компоненты, в которых, как правило, нет бизнес логики. +5. `atoms` - UI компоненты без бизнес логики. + +Модули на одном слое взаимодействуют только с модулями, находящимися на слоях ниже, как в FSD. +То есть, молекулы строятся из атомов, организмы из молекул, шаблоны из организмов, страницы из шаблонов. +Также Atomic Design подразумевает использование **Public API** внутри модулей для изоляции. + +### Применимость во фронтенде +Atomic Design относительно часто встречается в проектах. Atomic Design популярнее среди веб-дизайнеров, +нежели в разработке. Веб-дизайнеры часто используют Atomic Design для создания масштабируемых и легко поддерживаемых дизайнов. +В разработке Atomic Design часто смешивается с другими архитектурными методологиями. + +Однако, так как Atomic Design концентрирует внимание на UI компонентах и их композицию, возникает проблема реализации +бизнес логики в рамках архитектуры. + +Проблема заключается в том, что Atomic Design не предусматривает четкого уровня ответственности для бизнес-логики, +что приводит к распределению по различным компонентам и уровням, усложняя поддержку и тестирование. +Бизнес-логика становится размыта, что затрудняет четкое разделение ответственности и делает код менее +модульным и переиспользуемым. + +### Как оно сочетается с FSD? +В контексте FSD некоторые элементы Atomic Design могут быть применены для +создания гибких и масштабируемых UI компонентов. Слои `atoms` и `molecules` можно реализовать в +`shared/ui` в FSD, что упрощает переиспользование и поддержку базовых UI элементов. + + +- shared + - ui + - atoms/ + - molecules/ + - ... + + +Сравнение FSD и Atomic Design показывает, что обе методологии стремятся к модульности и переиспользованию, +но акцентируют внимание на разных аспектах. Atomic Design ориентирован на визуальные компоненты +и их композицию. FSD фокусируется на разбиении функциональности приложения на независимые модули и их взаимосвязи. + +- [Методология Atomic Design](https://atomicdesign.bradfrost.com/table-of-contents/) +- [(Тред) Про применимость в shared/ui](https://t.me/feature_sliced/1653) +- [(Видео) Кратко о Atomic Design](https://youtu.be/Yi-A20x2dcA) +- [(Доклад) Илья Азин - Feature-Sliced Design (фрагмент про Atomic Design)](https://youtu.be/SnzPAr_FJ7w?t=587) + +## Feature Driven + +> О подходе; О применимости в фронтенде; позиция FSD + +Про совместимость, историческое развитие и сравнение + +- [(Доклад) Oleg Isonen - Feature Driven Architecture](https://youtu.be/BWAeYuWFHhs) +- [Feature Driven - Краткая спецификация (с точки зрения FSD)](https://github.com/feature-sliced/documentation/tree/rc/feature-driven) diff --git a/src/content/docs/ru/docs/about/mission.mdx b/src/content/docs/ru/docs/about/mission.mdx new file mode 100644 index 0000000000..8e82481c9d --- /dev/null +++ b/src/content/docs/ru/docs/about/mission.mdx @@ -0,0 +1,50 @@ +--- +title: Миссия +sidebar: + order: 1 +--- + +Здесь описаны цели и ограничения применимости методологии, которыми мы руководствуемся при разработке методологии + +- Мы видим нашу цель, как баланс между идеологией и простотой +- Мы не сможем сделать серебряную пулю, которая подходит всем + +**Тем не менее, хотелось бы, чтобы методология была близка и доступна достаточно обширному кругу разработчиков** + +## Цели \{#goals} + +### Интуитивная понятность для широкого круга разработчиков \{#intuitive-clarity-for-a-wide-range-of-developers} + +Методология должна быть доступна - большей части команды в проектах + +*Т.к. даже со всем будущим инструментарием - будет недостаточно того, чтобы методологию понимали только прожженные сеньоры/лиды* + +### Решение повседневных проблем \{#solving-everyday-problems} + +В методологии должны быть изложены причины и решения наших повседневных проблем при разработке проектов + +**А также - приложить ко всему этому инструментарий (cli, линтеры)** +Чтобы разработчики могли использовать *выверенный опытом* подход, позволяющий обходить давние проблемы архитектуры и разработки + +> *@sergeysova: Представьте: разработчик пишет код в рамках методологии, и у него проблемы возникают раз в 10 реже, просто потому что другие люди продумали решение многих проблем.* + +## Ограничения \{#limitations} + +Мы не хотим *навязывать нашу точку зрения*, и одновременно понимаем - что *многие наши привычки, как разработчиков, мешают изо дня в день* + +У всех свой уровень опыта проектирования и разработки систем, **поэтому стоит понимать следующее:** + +- **Не выйдет**: очень просто, очень понятно, для всех + > *@sergeysova: Некоторые концепции невозможно интуитивно понять, пока не столкнешься с проблемами и не проведешь за решением годы.* + > + > - *Пример из математики — теория графов.* + > - *Пример из физики — квантовая механика.* + > - *Пример из программирования — архитектура приложений.* + > +- **Возможны и желательны**: простота, расширяемость + +## См. также \{#see-also} + +- [Проблемы архитектуры][refs-architecture--problems] + +[refs-architecture--problems]: /docs/about/understanding/architecture#problems diff --git a/src/content/docs/ru/docs/about/motivation.mdx b/src/content/docs/ru/docs/about/motivation.mdx new file mode 100644 index 0000000000..b5e7d37218 --- /dev/null +++ b/src/content/docs/ru/docs/about/motivation.mdx @@ -0,0 +1,150 @@ +--- +title: Мотивация +sidebar: + order: 2 +--- + +import { Aside, FileTree } from '@astrojs/starlight/components'; + +Главная идея **Feature-Sliced Design** - облегчить и удешевить разработку комплексных и развивающихся проектов, на основании [объединения результатов исследований, обсуждения опыта разного рода широкого круга разработчиков][ext-discussions]. + +Очевидно, что это не будет серебряной пулей, и само собой, у методологии будут свои [границы применимости][refs-mission]. + +Тем не менее, возникают резонные вопросы, касаемо *целесообразности такой методологии в целом* + + + +## Почему не хватает существующих решений? \{#intuitive-clarity-for-a-wide-range-of-developers} + +> Речь обычно о таких аргументах: +> +> - *"Зачем нужна отдельная новая методология, если уже есть давно зарекомендовавшие себя подходы и принципы проектирования `SOLID`, `KISS`, `YAGNI`, `DDD`, `GRASP`, `DRY` и т.д."* +> - *"Все проблемы проекта решаются хорошей документацией, тестами и выстроенными процессами"* +> - *"Проблем бы и не было - если бы все разработчики следовали всему выше перечисленному"* +> - *"Все придумано уже до вас, вы просто не можете этим пользоваться"* +> - *"Возьмите \{FRAMEWORK_NAME\} - там решено уже все за вас"* + +### Одних принципов недостаточно \{#principles-alone-are-not-enough} + +**Только существования принципов недостаточно для проектирования хорошей архитектуры** + +Не все их знают до конца, еще меньше правильно понимают и применяют + +*Принципы проектирования слишком общие, и не дают конкретного ответа на вопрос: "А как спроектировать структуру и архитектуру масштабируемого и гибкого приложения?"* + +### Процессы не всегда работают \{#processes-dont-always-work} + +*Документация/Тесты/Процессы* - это, конечно, хорошо, но увы, даже при больших затратах на них - **они не всегда решают поставленных проблем по архитектуре и внедрению новых людей в проект** + +- Время входа каждого разработчика в проект не сильно уменьшается, т.к. документация чаще всего выйдет огромной / устаревшей +- Постоянно следить за тем, что каждый понимает архитектуру одинаково - требует также колоссального количества ресурсов +- Не забываем и про bus-factor + +### Существующие фреймворки не везде могут быть применены \{#existing-frameworks-cannot-be-applied-everywhere} + +- Имеющиеся решения, как правило, имеют высокий порог входа, из-за чего сложно найти новых разработчиков +- Также, чаще всего, выбор технологии уже определен до наступления серьезных проблем в проекте, а потому нужно уметь "работать с тем что есть" - **не привязываясь к технологии** + +> Q: *"У меня в проекте `React/Vue/Redux/Effector/Mobx/{YOUR_TECH}` - как мне лучше выстроить структуру сущностей и связи между ними?"* + +### По итогу \{#as-a-result} + +Получаем *"уникальные как снежинки"* проекты, каждый из которых требует длительного погружения сотрудника, и знания, которые вряд ли будут применимы на другом проекте + +> @sergeysova: *"Это ровно та ситуация, которая сейчас есть в нашей сфере frontend разработки: каждый лид напридумает себе различных архитектур и структур проекта, при этом не факт, что эти структуры пройдут проверку временем, в итоге кроме него развивать проект могут максимум два человека и каждого нового разработчика нужно погружать снова."* + +## Зачем методология разработчикам? \{#why-do-developers-need-the-methodology} + +### Концентрация на бизнес-фичах, а не на проблемах архитектуры \{#focus-on-business-features-not-on-architecture-problems} + +Методология позволяет экономить ресурсы на проектировании масштабируемой и гибкой архитектуры, вместо этого направляя внимание разработчиков на разработку основной функциональности. При этом стандартизируются и сами архитектурные решения из проекта в проект. + +*Отдельный вопрос, что методология должна заслужить доверие комьюнити, чтобы другой разработчик мог в имеющиеся у него сроки ознакомиться и положиться на нее при решении проблем своего проекта* + +### Проверенное опытом решение \{#an-experience-proven-solution} + +Методология рассчитана на разработчиков, нацеленных на *проверенное опытом решение по проектированию комплексной бизнес-логики* + +*Однако ясно, что методология - это в целом про набор best-practices, статьи, рассматривающие определенные проблемы и кейсы при разработке. Поэтому - польза от методологии будет и для остального круга разработчиков - кто так или иначе сталкивается с проблемами при разработке и проектировании* + +### Здоровье проекта \{#project-health} + +Методология позволит *заблаговременно решать и отслеживать проблемы проекта, не требуя огромного количества ресурсов* + +**Чаще всего тех.долг копится и копится со временем, и ответственность за его разрешение лежит и на лиде, и на команде** + +Методология же позволит *заранее предупреждать* возможные проблемы при масштабировании и развитии проекта + +## Зачем методология бизнесу? \{#why-does-a-business-need-a-methodology} + +### Быстрый onboarding \{#fast-onboarding} + +С методологией можно нанять человека в проект, который **уже предварительно знаком с таким подходом, а не обучать заново** + +*Люди начинают быстрее вникать и приносить пользу проекту, а также появляются дополнительные гарантии найти людей на следующие итерации проекта* + +### Проверенное опытом решение \{#an-experience-proven-solution-1} + +С методологией бизнес получит *решение для большинства вопросов, возникающих при разработке систем* + +Поскольку чаще всего бизнес хочет получить фреймворк/решение, которое бы решало львиную долю проблем при развитии проекта + +### Применимость для разных стадий проекта \{#applicability-for-different-stages-of-the-project} + +Методология может принести пользу проекту *как на этапе поддержки и развития проекта, так и на этапе MVP* + +Да, на MVP чаще всего важнее *"фичи, а не заложенная на будущее архитектура"*. Но даже в условиях ограниченных сроков, зная best-practices из методологии - можно *"обойтись малой кровью"*, при проектировании MVP-версии системы, находя разумный компромисс +(нежели лепить фичи "как попало") + +*То же самое можно сказать и про тестирование* + +## Когда наша методология не нужна? \{#when-is-our-methodology-not-needed} + +- Если проект будет жить короткое время +- Если проект не нуждается в поддерживаемой архитектуре +- Если бизнес не воспринимает связь кодовой базы и скорости доставки фич +- Если бизнесу важнее поскорей закрыть заказы, без дальнейшей поддержки + +### Размеры бизнеса \{#business-size} + +- **Малый бизнес** - чаще всего нуждается в готовом и очень быстром решении. Только при росте бизнеса (хотя бы до почти среднего), он понимает - чтобы клиенты продолжали пользоваться, нужно в том числе уделить время качеству и стабильности разрабатываемых решений +- **Средний бизнес** - обычно понимает все проблемы разработки, и даже если приходится *"устраивать гонку за фичами"*, он все равно уделяет время на доработки по качеству, рефакторинг и тесты (и само собой - на расширяемую архитектуру) +- **Большой бизнес** - обычно уже имеет обширную аудиторию, штат сотрудников, и гораздо более обширный набор своих практик, и наверное даже - свой подход к архитектуре, поэтому идея взять чужую - им приходит не так часто + +## Планы \{#plans} + +Основная часть целей [изложена здесь][refs-mission--goals], но помимо этого, стоит проговорить и наши ожидания от методологии в будущем + +### Объединение опыта \{#combining-experience} + +Сейчас мы пытаемся объединить весь наш разнородный опыт `core-team`, и получить по итогу закаленную практикой методологию + +Конечно, мы можем получить по итогу Angular 3.0., но гораздо важней здесь - **исследовать саму проблему проектирования архитектуры сложных систем** + +*И да - у нас есть претензии и к текущей версии методологии, но мы хотим общими усилиями прийти к единому и оптимальному решению (учитывая, в том числе, и опыт комьюнити)* + +### Жизнь вне спецификации \{#life-outside-the-specification} + +Если все сложится хорошо, то методология не будет ограничиваться только спецификацией и тулкитом + +- Возможно будут и доклады, статьи +- Возможно будут `CODE_MODEs` для миграций на другие технологии проектов, написанных согласно методологии +- Не исключено, что по итогу сможем дойти и до мейнтейнеров крупных технологических решений + - *Особенно для React, по сравнению с другими фреймворками - это главная проблема, т.к. он не говорит как решать определенные проблемы* + +## См. также \{#see-also} + +- [(Обсуждение) Методология не нужна?][disc-src] +- [О миссии методологии: цели и ограничения][refs-mission] +- [Типы знаний в проекте][refs-knowledge] + +[refs-mission]: /docs/about/mission +[refs-mission--goals]: /docs/about/mission#goals +[refs-knowledge]: /docs/about/understanding/knowledge-types + +[disc-src]: https://github.com/feature-sliced/documentation/discussions/27 +[ext-discussions]: https://github.com/feature-sliced/documentation/discussions diff --git a/src/content/docs/ru/docs/about/promote/for-company.mdx b/src/content/docs/ru/docs/about/promote/for-company.mdx new file mode 100644 index 0000000000..5612a38111 --- /dev/null +++ b/src/content/docs/ru/docs/about/promote/for-company.mdx @@ -0,0 +1,16 @@ +--- +title: Продвижение в компании +sidebar: + order: 4 + badge: + text: WIP + variant: caution +--- + +## Нужна ли методология проекту и компании? \{#do-the-project-and-the-company-need-a-methodology} + +> Про оправданность применения и техдолг + +## Как подать методологию бизнесу? \{#how-can-i-submit-a-methodology-to-a-business} + +## Как подготовить и оправдать план по переезду на методологию? \{#how-to-prepare-and-justify-a-plan-to-move-to-the-methodology} diff --git a/src/content/docs/ru/docs/about/promote/for-team.mdx b/src/content/docs/ru/docs/about/promote/for-team.mdx new file mode 100644 index 0000000000..5f3ff60346 --- /dev/null +++ b/src/content/docs/ru/docs/about/promote/for-team.mdx @@ -0,0 +1,16 @@ +--- +title: Продвижение в команде +sidebar: + order: 3 + badge: + text: WIP + variant: caution +--- + +- Онбординг новых людей +- Гайдлайны по разработке (где искать модули и т.п.) +- Новый подход к задачам + +## См. также \{#see-also} +- [(Тред) Простота старых подходов и значение осознанности](https://t.me/feature_sliced/3360) +- [(Тред) Про удобство поиска по слоям](https://t.me/feature_sliced/1918) diff --git a/src/content/docs/ru/docs/about/promote/integration.mdx b/src/content/docs/ru/docs/about/promote/integration.mdx new file mode 100644 index 0000000000..00f74debf0 --- /dev/null +++ b/src/content/docs/ru/docs/about/promote/integration.mdx @@ -0,0 +1,25 @@ +--- +title: Аспекты интеграции +sidebar: + order: 1 +--- + +## Что получаем в конечном счете? \{#summary} + +См. первые 5 минут: + + + +## Также \{#also} + +**Преимущества**: +- [Overview](/docs/get-started/overview#advantages) +- CodeReview +- Onboarding + + +**Недостатки:** +- Ментальная сложность +- Высокий порог входа +- "Layers hell" +- Типичные проблемы feature-based подхода diff --git a/src/content/docs/ru/docs/about/promote/partial-application.mdx b/src/content/docs/ru/docs/about/promote/partial-application.mdx new file mode 100644 index 0000000000..f54ea19496 --- /dev/null +++ b/src/content/docs/ru/docs/about/promote/partial-application.mdx @@ -0,0 +1,10 @@ +--- +title: Частичное применение +sidebar: + order: 2 + badge: + text: WIP + variant: caution +--- + +> Как частично применять методологию? Имеет ли смысл? Что если игнорировать? diff --git a/src/content/docs/ru/docs/about/understanding/abstractions.mdx b/src/content/docs/ru/docs/about/understanding/abstractions.mdx new file mode 100644 index 0000000000..23516d8432 --- /dev/null +++ b/src/content/docs/ru/docs/about/understanding/abstractions.mdx @@ -0,0 +1,24 @@ +--- +title: Абстракции +sidebar: + order: 6 + badge: + text: WIP + variant: caution +--- + +## Закон дырявых абстракций \{#the-law-of-leaky-abstractions} + +## Почему так много абстракций \{#why-are-there-so-many-abstractions} + +> Абстракции помогают справляться со сложностью проекта. Вопрос в том - будут ли эти абстракции специфичны только для этого проекта, или же мы попытаемся вывести общие абстракции на основании специфики фронтенда + +> Архитектура и приложения в целом изначально сложны, и вопрос только в том как эту сложность лучше распределять и описывать + +## Про скоупы ответственности \{#about-scopes-of-responsibility} + +> Про опциональные абстракции + +## См. также \{#see-also} +- [Про необходимость в новых слоях](https://t.me/feature_sliced/2801) +- [Про сложность в понимании методологии и слоев](https://t.me/feature_sliced/2619) diff --git a/src/content/docs/ru/docs/about/understanding/architecture.mdx b/src/content/docs/ru/docs/about/understanding/architecture.mdx new file mode 100644 index 0000000000..f494f84f61 --- /dev/null +++ b/src/content/docs/ru/docs/about/understanding/architecture.mdx @@ -0,0 +1,99 @@ +--- +title: Об архитектуре +sidebar: + order: 1 +--- + +import { Aside } from '@astrojs/starlight/components'; + +## Проблемы \{#problems} + +Обычно, разговор об архитектуре поднимается, когда разработка стопорится из-за тех или иных проблем в проекте. + +### Bus-factor & Onboarding + +Проект и его архитектуру понимает лишь ограниченный круг людей + +**Примеры:** + +- *"Сложно добавить человека в разработку"* +- *"На каждую проблему - у каждого свое мнение как обходить" (позавидуем ангуляру)* +- *"Не понимаю что происходит в этом большом куске монолита"* + +### Неявные и неконтролируемые последствия \{#implicit-and-uncontrolled-consequences} + +Множество неявных сайд-эффектов при разработке/рефакторинге *("все зависит от всего")* + +**Примеры:** + +- *"Фича импортирует фичу"* +- *"Я обновил(а) стор одной страницы, а отвалилась функциональность на другой"* +- *"Логика размазана по всему приложению, и невозможно отследить - где начало, где конец"* + +### Неконтролируемое переиспользование логики \{#uncontrolled-reuse-of-logic} + +Сложно переиспользовать/модифицировать существующую логику + +При этом, обычно есть [две крайности](https://github.com/feature-sliced/documentation/discussions/14): + +- Либо под каждый модуль пишется логика полностью с нуля *(с возможными повторениями в имеющейся кодовой базе)* +- Либо идет тенденция переносить все-все реализуемые модули в `shared` папки, тем самым создавая из нее большую свалку из модулей *(где большинство используется только в одном месте)* + +**Примеры:** + +- *"У меня в проекте есть n-реализаций одной и той же бизнес-логики, за что приходится ежедневно расплачиваться"* +- *"В проекте есть 6 разных компонентов кнопки/попапа/..."* +- *"Свалка хелперов"* + +## Требования \{#requirements} + +Поэтому кажется логичным предъявить желаемые *требования к идеальной архитектуре:* + + + +### Explicitness + +- Должно быть **легко осваивать и объяснять** команде проект и его архитектуру +- Структура должна отображать реальные **бизнес-ценности проекта** +- Должны быть явными **сайд-эффекты и связи** между абстракциями +- Должно быть **легко обнаруживать дублирование логики**, не мешая уникальным реализациям +- Не должно быть **распыления логики** по всему проекту +- Не должно быть **слишком много разнородных абстракций и правил** для хорошей архитектуры + +### Control + +- Хорошая архитектура должна **ускорять решение задач, внедрение фич** +- Должна быть возможность контролировать разработку проекта +- Должно быть легко **расширять, модифицировать, удалять код** +- Должна соблюдаться **декомпозиция и изолированность** функциональности +- Каждый компонент системы должен быть **легко заменяемым и удаляемым** + - *[Не нужно оптимизировать под изменения][ext-kof-not-modification] - мы не можем предсказывать будущее* + - *[Лучше - оптимизировать под удаление][ext-kof-but-removing] - на основании того контекста, который уже имеется* + +### Adaptability + +- Хорошая архитектура должна быть применима **к большинству проектов** + - *С уже существующими инфраструктурными решениями* + - *На любой стадии развития* +- Не должно быть зависимости от фреймворка и платформы +- Должна быть возможность **легко масштабировать проект и команду**, с возможностью параллелизации разработки +- Должно быть легко **подстраиваться под изменяющиеся требования и обстоятельства** + +## См. также \{#see-also} + +- [(React Berlin Talk) Oleg Isonen - Feature Driven Architecture][ext-kof] +- [(React SPB Meetup #1) Sergey Sova - Feature Slices][ext-slices-spb] +- [(Статья) Про модуляризацию проектов][ext-medium] +- [(Статья) Про Separation of Concerns и структурирование по фичам][ext-ryanlanciaux] + +[ext-kof-not-modification]: https://youtu.be/BWAeYuWFHhs?t=1631 +[ext-kof-but-removing]: https://youtu.be/BWAeYuWFHhs?t=1666 + +[ext-slices-spb]: https://t.me/feature_slices +[ext-kof]: https://youtu.be/BWAeYuWFHhs +[ext-medium]: https://alexmngn.medium.com/why-react-developers-should-modularize-their-applications-d26d381854c1 +[ext-ryanlanciaux]: https://ryanlanciaux.com/blog/2017/08/20/a-feature-based-approach-to-react-development/ diff --git a/src/content/docs/ru/docs/about/understanding/knowledge-types.mdx b/src/content/docs/ru/docs/about/understanding/knowledge-types.mdx new file mode 100644 index 0000000000..924224965c --- /dev/null +++ b/src/content/docs/ru/docs/about/understanding/knowledge-types.mdx @@ -0,0 +1,31 @@ +--- +title: Типы знаний в проекте +sidebar: + order: 3 + label: Типы знаний +--- + +import { Aside } from '@astrojs/starlight/components'; + +В любом проекте можно выделить следующие "типы знаний": + +* **Фундаментальные знания** + Знания, которые не сильно меняются со временем, такие как алгоритмы, computer science, механизмы работы языка программирования и его API. + +* **Технологический стек** + Знания о наборе технических решений, используемых в проекте, включая языки программирования, фреймворки и библиотеки. + +* **Проектные знания** + Знания, специфичные для текущего проекта и бесполезные вне этого проекта. Эти знания необходимы новым членам команды, чтоб вносить эффективный вклад. + + + +## См. также \{#see-also} + +- [(Видео) Илья Климов — О типах знаний][ext-klimov] + +[ext-klimov]: https://youtu.be/4xyb_tA-uw0?t=249 diff --git a/src/content/docs/ru/docs/about/understanding/naming.mdx b/src/content/docs/ru/docs/about/understanding/naming.mdx new file mode 100644 index 0000000000..7214b080ce --- /dev/null +++ b/src/content/docs/ru/docs/about/understanding/naming.mdx @@ -0,0 +1,48 @@ +--- +title: Нейминг +sidebar: + order: 4 +--- + +У разных разработчиков различный опыт и контекст, что может привести к недопониманию в команде, когда одни и те же сущности называются по-разному. Например: + +- Компоненты для отображения могут называться "ui", "components", "ui-kit", "views", … +- Код, который повторно используется во всем приложении, может называться "core", "shared", "app", … +- Код бизнес-логики может называться "store", "model", "state", … + +## Нейминг в Feature-Sliced Design \{#naming-in-fsd} + +В методологии используются такие специфические термины, как: + +- "app", "process", "page", "feature", "entity", "shared" как имена слоев, +- "ui', "model", "lib", "api", "config" как имена сегментов. + +Очень важно придерживаться этих терминов, чтобы предотвратить путаницу среди членов команды и новых разработчиков, присоединяющихся к проекту. Использование стандартных названий также помогает при обращении за помощью к сообществу. + +## Конфликты нейминга \{#when-can-naming-interfere} + +Конфликты нейминга могут возникать, когда термины, которые используются в методологии FSD, пересекаются с терминами, используемыми в бизнесе: + +- `FSD#process` vs моделируемый процесс в приложении, +- `FSD#page` vs страница журнала, +- `FSD#model` vs модель автомобиля. + +Например, разработчик, увидев в коде слово "процесс", потратит лишнее время, пытаясь понять, какой процесс подразумевается. Такие **коллизии могут нарушить процесс разработки**. + +Когда глоссарий проекта содержит терминологию, характерную для FSD, крайне важно проявлять осторожность при обсуждении этих терминов с командой и техническими незаинтересованными сторонами. + +Чтобы эффективно общаться с командой, рекомендуется использовать аббревиатуру "FSD" для префиксации терминов методологии. Например, когда речь идет о процессе, можно сказать: "Мы можем поместить этот процесс на слой FSD features". + +И наоборот, при общении с нетехническими заинтересованными сторонами лучше ограничивать использование терминологии FSD, а также воздержаться от упоминания внутренней структуры кодовой базы. + +## См. также \{#see-also} + +- [(Обсуждение) Адаптивность нейминга][disc-src] +- [(Обсуждение) Опрос по неймингу сущностей][disc-naming] +- [(Обсуждение) "processes" vs "flows" vs ...][disc-processes] +- [(Обсуждение) "model" vs "store" vs ...][disc-model] + +[disc-model]: https://github.com/feature-sliced/documentation/discussions/68 +[disc-naming]: https://github.com/feature-sliced/documentation/discussions/31#discussioncomment-464894 +[disc-processes]: https://github.com/feature-sliced/documentation/discussions/20 +[disc-src]: https://github.com/feature-sliced/documentation/discussions/16 \ No newline at end of file diff --git a/src/content/docs/ru/docs/about/understanding/needs-driven.mdx b/src/content/docs/ru/docs/about/understanding/needs-driven.mdx new file mode 100644 index 0000000000..ca58abb8b4 --- /dev/null +++ b/src/content/docs/ru/docs/about/understanding/needs-driven.mdx @@ -0,0 +1,161 @@ +--- +title: О понимании потребностей и о формулировке задач +sidebar: + order: 2 + label: Понимание потребностей +--- + +import { Aside } from '@astrojs/starlight/components'; + + + +## Зачем? \{#why} + +Чтобы подобрать четкое имя сущности и понять ее составляющие, **нужно отчетливо понимать - какая задача будет решена с помощью всего этого кода.** + +> _@sergeysova: Во время разработки, мы пытаемся каждой сущности или функции дать имя, которое четко отражает намерения и смысл выполняемого кода._ + +_Ведь, без понимания задачи, нельзя написать правильные тесты, покрывающие самые важные кейсы, проставить ошибки помогающие пользователю в нужных местах, даже банально не прерывать флоу пользователя из-за исправимых не критичных ошибок._ + +## О каких задачах речь? \{#what-tasks-are-we-talking-about} + +Frontend занимается разработкой приложений и интерфейсов для конечных пользователей, значит мы решаем задачи этих потребителей. + +Когда к нам приходит человек, **он хочет решить какую-то свою боль или закрыть потребность.** + +_Задача менеджеров и аналитиков - сформулировать эту потребность, а разработчиков реализовать с учетом особенностей веб-разработки (потеря связи, ошибка бэкенда, опечатка, промазал курсором или пальцем)._ + +**Эта самая цель, с которой пришёл пользователь и есть задача разработчиков.** + +> _Одна маленькая решенная задача и есть feature в методологии Feature-Sliced Design — нужно нарезать весь скоуп задач проекта на маленькие цели._ + +## Как это влияет на разработку? \{#how-does-this-affect-development} + +### Декомпозиция задачи \{#task-decomposition} + +Когда разработчик принимается реализовывать задачу, для упрощения понимания и поддержки кода, он мысленно **нарезает ее на этапы**: + +- сначала _разбить на верхнеуровневые сущности_ и _реализовать их_, +- затем эти сущности _разбить на более мелкие_ +- и так далее + +_В процессе разбиения на сущности, разработчик вынужден дать им название, которое четко отражало бы его замысел и при чтении листинга помогало понять какую задачу решает код_ + +_При этом не забываем, что пытаемся помочь пользователю уменьшить боль или реализовать потребности_ + +### Понимание сути задачи \{#understanding-the-essence-of-the-task} + +Но чтобы дать четкое название сущности, **разработчик должен знать предостаточно о ее назначении** + +- как он собирается использовать эту сущность, +- какую часть задачи пользователя она реализует, где ещё эту сущность можно применить, +- в каких ещё задачах она может поучаствовать, +- и так далее + +Сделать вывод не сложно: **пока разработчик будет размышлять над названием сущностей в рамках методологии, он сможет найти плохо сформулированные задачи ещё до написания кода.** + +> Как дать название сущности, если плохо понимаешь, какие задачи она может решать, как вообще можно разбить задачу на сущности, если плохо ее понимаешь? + +## Как сформулировать? \{#how-to-formulate-it} + +**Чтобы сформулировать задачу, которая решается фичей, нужно понимать саму задачу**, а это уже область ответственности менеджера проекта и аналитиков. + +_Методология может лишь подсказать разработчику, на какие задачи стоит обратить пристальное внимание менеджеру продукта._ + +> _@sergeysova: Весь frontend это в первую очередь отображение информации, любой компонент в первую очередь что-то отображает, а значит задача "показать пользователю что-то" не имеет практической ценности._ +> +> _Даже без учета специфики frontend можно спросить "а зачем это нужно показывать", так можно продолжать спрашивать до тех пор пока не вылезет боль или потребность потребителя._ + +Как только мы смогли дойти до базовых потребностей или болей, можно идти обратно и разбираться, **а как именно ваш продукт или сервис может помочь пользователю с его целями** + +Любая новая задача в вашем трекере направлена на решение задач бизнеса, а бизнес пытается решить задачи пользователя одновременно заработав на нём. А значит, каждая задача несёт в себе определенные цели, даже если они не прописаны в тексте описания. + +_**Разработчик должен четко понимать, какую цель преследует та или иная задача**, но при этом не каждая компания может позволить себе идеально выстроить процессы, хоть это и отдельный разговор, тем не менее, разработчик вполне может сам "пингануть" нужных менеджеров, чтобы выяснить это и сделать свою часть работы эффективно._ + +## А в чем выгода? \{#and-what-is-the-benefit} + +Посмотрим теперь на весь процесс от начала до конца. + +### 1. Понимание задач пользователей \{#1-understanding-user-tasks} + +Когда разработчик понимает его боли и то, как бизнес их закрывает, он может предлагать решения, которые бизнесу не доступны в силу специфики веб-разработки. + +> Но конечно, все это может работать только если разработчику небезразлично то, что он делает и ради чего, а иначе _зачем тогда методология и какие-то подходы?_ + +### 2. Структуризация и упорядочивание \{#2-structuring-and-ordering} + +С пониманием задач приходит **четкая структура как в голове, так и в задачах вместе с кодом** + +### 3. Понимание фичи и ее составляющих \{#3-understanding-the-feature-and-its-components} + +**Одна фича - это одна полезная функциональность для пользователя** + +- Когда в одной фиче - реализуется несколько - это и есть **нарушение границ** +- Фича может быть неделимой и разрастающейся - **и это неплохо** +- **Плохо** - когда фича не отвечает на вопрос _"А в чем бизнес-ценность для пользователя?"_ + - Не может быть фичи `карта-офиса` + - А вот `бронирование-переговорки-на-карте`, `поиск-сотрудника`, `смена-рабочего-места` - **да** + +> _@sergeysova: Смысл в том, чтобы в фиче лежал только код, реализующий непосредственно саму функциональность, без лишних подробностей и внутренних решений (в идеале)_ +> +> *Открываешь код фичи **и видишь только то, что относится к задаче** - не больше* + +### 4. Profit \{#4-profit} + +Бизнес крайне редко разворачивает свой курс кардинально в другую сторону, а значит **отражение задач бизнеса в коде frontend приложения это весьма существенный профит.** + +_Тогда не придётся объяснять каждому новому члену команды, что делает тот или иной код, и вообще ради чего он добавлялся - **все будет объясняться через задачи бизнеса, которые уже отражены в коде.**_ + +> То, что называется ["Язык бизнеса" в Domain Driven Development][ext-ubiq-lang] + +--- + +## Вернемся к реальности \{#back-to-reality} + +Если бизнес-процессы осмыслены и на стадии дизайна даны хорошие имена - _то перенести это понимание и логику в код не особо проблемно._ + +**Однако на деле**, задачи и функциональность обычно развиваются "слишком" итеративно и (или) нет времени продумывать дизайн. + +**В итоге фича сегодня имеет смысл, а при расширении этой фичи через месяц можно переписать пол проекта.** + +> *[[Из обсуждения][disc-src]]: Разработчик пытается думать на 2-3 шага вперед, учитывая будущие хотелки, но тут упирается в собственный опыт* +> +> _Прожженный опытом инженер обычно сразу смотрит на 10 шагов вперед, и понимает где одну фичу разделить, а где объединить с другой_ +> +> _Но бывает и так, что приходит задача, с которой не приходилось сталкиваться по опыту, и неоткуда взять понимание - как грамотней декомпозировать, с наименьшими печальными последствиями в будущем_ + +## Роль методологии \{#the-role-of-methodology} + +**Методология помогает решить проблемы разработчиков, чтобы тем было проще решать проблемы пользователей.** + +Нет решения задач разработчиков только ради разработчиков + +Но чтобы разработчик решил свои задачи, **нужно понять задачи пользователя** - наоборот не выйдет + +### Требования к методологии \{#methodology-requirements} + +Становится ясно, что нужно выделить как минимум два требования для **Feature-Sliced Design**: + +1. Методология должна рассказывать **как создавать фичи, процессы и сущности** + - А значит должна четко объяснять _как разделять код между ними_, из чего следует, что именование этих сущностей также должно быть заложено в спецификации. +2. Методология должна помогать архитектуре **[легко адаптироваться под изменяющиеся требования проекта][refs-arch--adaptability]** + +## См. также \{#see-also} + +- [(Пост) Стимуляция к четкой формулировке задач (+ обсуждение)][disc-src] + > _**Текущая статья** является адаптацией этого обсуждения, по ссылке можно ознакомиться с полной неурезанной версией_ +- [(Обсуждение) Как разбить функциональность и что из себя она представляет][tg-src] +- [(Статья) "How to better organize your applications"][ext-medium] + +[refs-arch--adaptability]: architecture#adaptability + +[ext-medium]: https://alexmngn.medium.com/how-to-better-organize-your-react-applications-2fd3ea1920f1 +[disc-src]: https://t.me/sergeysova/318 +[tg-src]: https://t.me/atomicdesign/18972 +[ext-ubiq-lang]: https://thedomaindrivendesign.io/developing-the-ubiquitous-language diff --git a/src/content/docs/ru/docs/about/understanding/signals.mdx b/src/content/docs/ru/docs/about/understanding/signals.mdx new file mode 100644 index 0000000000..50cbff3bcc --- /dev/null +++ b/src/content/docs/ru/docs/about/understanding/signals.mdx @@ -0,0 +1,19 @@ +--- +title: Сигналы архитектуры +sidebar: + order: 5 + badge: + text: WIP + variant: caution +--- + +> Если есть ограничение со стороны архитектуры - значит на то есть явные причины, и последствия, если их игнорировать + +> Методология и архитектура дает сигналы, а то как с этим справляться - зависит от того, какие риски готовы взять на себя и что наиболее подойдет вашей команде) + +## См. также \{#see-also} + +- [(Тред) Про сигналы от архитектуры и дата-флоу](https://t.me/feature_sliced/2070) +- [(Тред) Про фундаментальность архитектуры](https://t.me/feature_sliced/2492) +- [(Тред) Про подсвечивание слабых мест](https://t.me/feature_sliced/3979) +- [(Тред) Как понять, что модель данных разбухла](https://t.me/feature_sliced/4228) From 335d2ba2f4740b9919343fd964cb741ace8bf0dd Mon Sep 17 00:00:00 2001 From: Solant Date: Sat, 7 Feb 2026 01:30:59 +0100 Subject: [PATCH 30/45] add ru/docs/reference articles --- src/content/docs/ru/docs/reference/layers.mdx | 151 +++++++++++++++++ .../docs/ru/docs/reference/public-api.mdx | 158 ++++++++++++++++++ .../ru/docs/reference/slices-segments.mdx | 71 ++++++++ 3 files changed, 380 insertions(+) create mode 100644 src/content/docs/ru/docs/reference/layers.mdx create mode 100644 src/content/docs/ru/docs/reference/public-api.mdx create mode 100644 src/content/docs/ru/docs/reference/slices-segments.mdx diff --git a/src/content/docs/ru/docs/reference/layers.mdx b/src/content/docs/ru/docs/reference/layers.mdx new file mode 100644 index 0000000000..29dd4fb175 --- /dev/null +++ b/src/content/docs/ru/docs/reference/layers.mdx @@ -0,0 +1,151 @@ +--- +title: Слои +sidebar: + order: 1 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +Слои - это первый уровень организационной иерархии в Feature-Sliced Design. Их цель - разделить код на основе того, сколько ответственности ему требуется и от скольких других модулей в приложении он зависит. Каждый слой несет особое семантическое значение, чтобы помочь вам определить, сколько ответственности следует выделить вашему коду. + +Всего существует **7 слоев**, расположенных от наибольшей ответственности и зависимости к наименьшей: + +Дерево файловой системы с одной корневой папкой под названием src и семью подпапками: app, processes, pages, widgets, features, entities, shared. Папка processes слегка выцвечена. +Дерево файловой системы с одной корневой папкой под названием src и семью подпапками: app, processes, pages, widgets, features, entities, shared. Папка processes слегка выцвечена. + +1. App (Эпп) +2. Processes (Процессы, устаревший слой) +2. Pages (Страницы) +3. Widgets (Виджеты) +4. Features (Фичи/функции) +5. Entities (Сущности) +6. Shared (Шэред) + +Вы не обязаны использовать все слои в своем проекте - добавляйте только те, что приносят пользу вашему проекту. Как правило, в большинстве фронтенд-проектов будут как минимум слои Shared, Pages и App. + +На практике слои представляют собой папки с названиями в нижнем регистре (например, `📁 shared`, `📁 pages`, `📁 app`). Добавление новых слоев _не рекомендуется_, так как их семантика стандартизирована. + +## Правило импорта слоев + +Слои состоят из _слайсов_ — высокосвязанных групп модулей. Зависимости между слайсами регулируются **правилом импорта слоев**: + +> _Модуль (файл) в слайсе может импортировать другие слайсы только если они находятся на слоях строго ниже._ + +Например, папка `📁 ~/features/aaa` является слайсом с именем "aaa". Файл внутри нее, `~/features/aaa/api/request.ts`, не может импортировать код из любого файла в `📁 ~/features/bbb`, но может импортировать код из `📁 ~/entities` и `📁 ~/shared`, а также любой код из `📁 ~/features/aaa`, например, `~/features/aaa/lib/cache.ts`. + +Слои App и Shared являются **исключениями** из этого правила — они одновременно являются и слоем, и слайсом. Слайсы разделяют код по бизнес-доменам, и эти два слоя являются исключениями, потому что в Shared нет бизнес-доменов, а App объединяет все бизнес-домены. + +На практике это означает, что слои App и Shared состоят из сегментов, и сегменты могут свободно импортировать друг друга. + +## Определения слоёв + +В этом разделе описывается семантическое значение каждого слоя, чтобы создать интуитивное представление о том, какой код в нём будет лежать. + +### Shared + +Этот слой формирует фундамент для остальной части приложения. Это место для создания связей с внешним миром, например, бэкенды, сторонние библиотеки, среда выполнения приложения (environment). Также это место для ваших собственных библиотек, сконцентрированных на конкретной задаче. + +Этот слой, как и слой App, _не содержит слайсов_. Слайсы предназначены для разделения слоя на предметные области, но предметные области не существуют в Shared. Это означает, что все файлы в Shared могут ссылаться и импортировать друг друга. + +Вот сегменты, которые вы обычно можете найти в этом слое: + +- `📁 api` — API-клиент и, возможно, функции для выполнения запросов к конкретным эндпоинтам бэкенда. +- `📁 ui` — UI-кит приложения. + Компоненты на этом слое не должны содержать бизнес-логику, но могут быть тематически связаны с бизнесом. Например, здесь можно разместить логотип компании и макет страницы. Компоненты с UI-логикой также допустимы (например, автозаполнение или строка поиска). +- `📁 lib` — коллекция внутренних библиотек. + Эта папка не должна рассматриваться как хелперы или утилиты ([прочитайте здесь, почему эти папки часто превращаются в свалку][ext-sova-utility-dump]). Вместо этого каждая библиотека в этой папке должна иметь одну область фокуса, например, даты, цвета, манипуляции с текстом и т.д. Эта область фокуса должна быть задокументирована в файле README. Разработчики в вашей команде должны знать, что можно и что нельзя добавлять в эти библиотеки. +- `📁 config` — переменные окружения, глобальные фиче-флаги и другая глобальная конфигурация для вашего приложения. +- `📁 routes` — константы маршрутов или шаблоны для сопоставления маршрутов. +- `📁 i18n` — код, настраивающий систему переводов, а также глобальные строки перевода. + +Вы можете добавлять ещё сегментов, но убедитесь, что название этих сегментов описывает цель содержимого, а не его суть. Например, `components`, `hooks` и `types` — плохие имена сегментов, поскольку они не очень полезны при поиске кода. + +### Entities + +Слайсы на этом слое представляют концепции из реального мира, с которыми работает проект. Обычно это термины, которые бизнес использует для описания продукта. Например, социальная сеть может работать с бизнес-сущностями, такими как Пользователь, Публикация и Группа. + +Слайс сущности может содержать хранилище данных (`📁 model`), схемы валидации данных (`📁 model`), функции запросов API, связанные с сущностями (`📁 api`), а также визуальное представление этой сущности в интерфейсе (`📁 ui`). Это визуальное представление не обязательно должно создавать полный блок пользовательского интерфейса — оно в первую очередь предназначено для переиспользования одного и того же внешнего вида на нескольких страницах приложения, и к нему может быть присоединена различная бизнес-логика через пропcы или слоты. + +#### Связи между сущностями + +Сущности в FSD являются слайсами, и по умолчанию слайсы не могут знать друг о друге. Однако в реальной жизни сущности часто взаимодействуют друг с другом, и иногда одна сущность владеет или содержит другие сущности. Из-за этого бизнес-логику этих взаимодействий лучше всего хранить на более высоких уровнях, таких как Features или Pages. + +Когда объект данных одной сущности содержит другие объекты данных, обычно хорошей идеей является сделать связь между сущностями явной и обойти изоляцию слайсов, создав API для кросс-ссылок через `@x`-нотацию. Причина в том, что связанные сущности должны рефакториться вместе, поэтому лучше сделать так, чтобы связь было невозможно не заметить. + +Например: + +```ts title="entities/artist/model/artist.ts" +import type { Song } from "entities/song/@x/artist"; + +export interface Artist { + name: string; + songs: Array; +} +``` + +```ts title="entities/song/@x/artist.ts" +export type { Song } from "../model/song.ts"; +``` + +Вы можете узнать больше о `@x`-нотации в разделе [Публичный API для кросс-импортов][public-api-for-cross-imports]. + +### Features + +Этот слой предназначен для основных взаимодействий в вашем приложении, действий, которые важны вашим пользователям. Эти взаимодействия часто затрагивают бизнес-сущности, поскольку сущности — это то, о чём ваше приложение. + +Важный принцип эффективного использования слоя Features: **не все должно быть фичей**. Хорошим показателем того, что что-то должно быть фичей, является тот факт, что оно переиспользуется. Например, если в приложении есть несколько редакторов, и у всех них есть комментарии, то комментарии являются переиспользуемой фичей. Помните, что слайсы — это механизм для быстрого поиска кода, и если фич слишком много, важные фичи теряются. + +В идеале, когда вы приходите в новый проект, вы узнаёте о его функциональности, просматривая страницы и фичи. Принимая решение о том, что должно быть функцией, оптимизируйте опыт новичка в проекте, который/ая хочет быстро обнаружить большие важные области кода. + +Слайс фичи может содержать UI для выполнения действия, например, форму (`📁 ui`), вызовы API, необходимые для выполнения действия (`📁 api`), валидацию и внутреннее состояние (`📁 model`), фиче-флаги (`📁 config`). + +### Widgets + +Слой Widgets предназначен для больших самодостаточных блоков интерфейса. Виджеты наиболее полезны, когда они используются на нескольких страницах или когда страница, к которой они принадлежат, имеет несколько больших независимых блоков, и это один из них. + +Если блок интерфейса составляет бо́льшую часть интересного контента на странице и никогда не используется повторно, он **не должен быть виджетом**, и вместо этого его следует разместить непосредственно на этой странице. + + + +### Pages + +Страницы — это то, из чего состоят веб-сайты и приложения (также называются "экраны" или "активности"). Одна страница обычно соответствует одному слайсу, однако, если есть несколько очень похожих страниц, их можно сгруппировать в один слайс, например, формы регистрации и входа. + +Нет никаких ограничений на количество кода, которое можно разместить в слайсе страницы, по крайней мере, до тех пор, пока вашей команде не станет сложно ориентироваться в ней. Если блок интерфейса со страницы не переиспользуется на других страницах, вполне допустимо оставить его внутри слайса страницы. + +В слайсе страницы вы обычно найдете интерфейс страницы, а также состояния загрузки и границы ошибок (`📁 ui`). Также вы можете найти там запросы на получение и изменение данных (`📁 api`). Обычно у страницы нет выделенной модели данных, и небольшие части состояния могут храниться в самих компонентах. + +### Processes + + + +Выход из ситуаций, когда требуется сложное многостраничное взаимодействие. + +Этот уровень намеренно оставлен не очень определенным. Большинству приложений этот слой не пригодится, логику на уровне роутера и сервера следует оставить на уровне App. Используйте этот слой только тогда, когда слой App вырастет настолько, что станет неподдерживаемым и потребует разгрузки. + +### App + +Всё, что касается приложения целиком, как в техническом смысле (например, провайдеры контекста), так и в бизнес-смысле (например, аналитика). + +Этот слой обычно не содержит слайсов, как и Shared, и вместо этого внутри него сразу находятся сегменты. + +Вот сегменты, которые вы обычно можете найти в этом слое: + +- `📁 routes` — конфигурация роутера +- `📁 store` — глобальная конфигурация хранилища +- `📁 styles` — глобальные стили +- `📁 entrypoint` — точка входа в код приложения, специфичная для вашего фреймворка + +[public-api-for-cross-imports]: /docs/reference/public-api#public-api-for-cross-imports +[ext-remix]: https://remix.run +[ext-sova-utility-dump]: https://sergeysova.com/ru/why-utils-and-helpers-is-a-dump/ diff --git a/src/content/docs/ru/docs/reference/public-api.mdx b/src/content/docs/ru/docs/reference/public-api.mdx new file mode 100644 index 0000000000..825eb3b4b7 --- /dev/null +++ b/src/content/docs/ru/docs/reference/public-api.mdx @@ -0,0 +1,158 @@ +--- +title: Публичный API +sidebar: + order: 3 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +Публичный API — это _контракт_ между группой модулей, например, слайсом, и кодом, который его использует. Он также действует как ворота, разрешая доступ только к определенным объектам и только через этот публичный API. + +На практике это обычно реализуется как индексный файл с реэкспортами: + +```js title="pages/auth/index.js" +export { LoginPage } from "./ui/LoginPage"; +export { RegisterPage } from "./ui/RegisterPage"; +``` + +## Что делает публичный API хорошим? + +Хороший публичный API делает использование и интеграцию слайса в другой код удобным и надежным. Этого можно достичь, поставив три цели: + +1. Остальная часть приложения должна быть защищена от структурных изменений в слайсе, таких как рефакторинг. +2. Значительные изменения в поведении слайса, которые нарушают предыдущие ожидания, должны вызывать изменения в публичном API. +3. Только необходимые части слайса должны быть доступны. + +Последняя цель имеет важные практические последствия. Может возникнуть соблазн создать слепые реэкспорты всего, особенно на ранних этапах разработки слайса, потому что любые новые объекты, которые вы экспортируете из своих файлов, также автоматически экспортируются из слайса: + +```js title="Плохая практика, features/comments/index.js" +// ❌ НИЖЕ ПЛОХОЙ КОД, НЕ ДЕЛАЙТЕ ТАК +export * from "./ui/Comment"; // 👎 не пытайтесь повторить дома +export * from "./model/comments"; // 💩 это плохая практика +``` + +Это приведет к тому, что по индексу слайса нельзя будет понять его интерфейс. Не зная интерфейс, вам придется глубоко погружаться в код слайса, чтобы понять, как с ним работать. Еще одна проблема заключается в том, что вы можете случайно раскрыть внутренние модули, что усложнит рефакторинг, если кто-то начнет от них зависеть. + +## Публичный API для кросс-импортов \{#public-api-for-cross-imports\} + +Кросс-импорты — это ситуация, когда один слайс импортирует из другого слайса на том же слое. Обычно это запрещено [правилом импорта для слоёв][import-rule-on-layers], но часто есть реальные причины, чтобы сделать кросс-импорт. Например, в реальном мире бизнес-сущности часто ссылаются друг на друга, и лучше отразить эти отношения в коде, а не пытаться избавиться от них. + +Для этой цели существует особый вид публичного API, также известный как `@x`-нотация. Если у вас есть сущности A и B, и сущность B должна импортировать из сущности A, то сущность A может объявить отдельный публичный API только для сущности B. + + +- entities + - A + - @x + - B.ts специальный публичный API только для кода внутри `entities/B/` + - index.ts обычный публичный API + + +Затем код внутри `entities/B/` может импортировать из `entities/A/@x/B`: + +```ts +import type { EntityA } from "entities/A/@x/B"; +``` + +Нотацию `A/@x/B` следует читать как "пересечение A и B". + + + +## Проблемы с индексными файлами + +Индексные файлы, такие как `index.js`, также известные как barrel-файлы (файлы-бочки), являются самым распространенным способом определения публичного API. Их легко создать, но они иногда вызывают проблемы с некоторыми сборщиками и фреймворками. + +### Циклические импорты + +Циклический импорт — это когда два или более файла импортируют друг друга по кругу. + +
+ Три файла, импортирующие друг друга по кругу + Три файла, импортирующие друг друга по кругу +
+ На изображении выше: три файла, `fileA.js`, `fileB.js` и `fileC.js`, импортирующие друг друга по кругу. +
+
+ +Эти ситуации часто трудно обрабатывать сборщикам, и в некоторых случаях они могут даже привести к ошибкам во время выполнения кода, которые может быть трудно отладить. + +Циклические импорты могут возникать и без индексных файлов, но наличие индексного файла создает явную возможность случайно создать циклический импорт. Это часто происходит, когда у вас есть два объекта, доступных в публичном API слайса, например, `HomePage` и `loadUserStatistics`, и `HomePage` нужно получить доступ к `loadUserStatistics`, но он делает это следующим образом: + +```jsx title="pages/home/ui/HomePage.jsx" +import { loadUserStatistics } from "../"; // импортируем из pages/home/index.js + +export function HomePage() { /* … */ } +``` + +```js title="pages/home/index.js" +export { HomePage } from "./ui/HomePage"; +export { loadUserStatistics } from "./api/loadUserStatistics"; +``` + +Эта ситуация создает циклический импорт, потому что `index.js` импортирует `ui/HomePage.jsx`, но `ui/HomePage.jsx` тоже импортирует `index.js`. + +Чтобы предотвратить эту проблему, воспользуйтесь этими принципами. Если у вас есть два файла, и один импортирует из другого: +- Если они находятся в одном слайсе, всегда используйте _относительные_ импорты и пишите полный путь импорта +- Если они находятся в разных слайсах, всегда используйте _абсолютные_ импорты, например, через алиас + +### Большие бандлы и неработающий tree-shaking в Shared \{#large-bundles\} + +Некоторым сборщикам может быть трудно выполнять tree-shaking (удаление неимпортированного кода), когда у вас есть индексный файл, который реэкспортирует все. + +Обычно это не проблема для публичных API, потому что содержимое модуля обычно довольно тесно связано, поэтому вам редко нужно импортировать одну вещь, но удалить другую. Однако есть два очень распространенных случая, когда обычные правила публичного API в FSD могут привести к проблемам — `shared/ui` и `shared/lib`. + +Эти две папки являются коллекциями несвязанных вещей, которые часто не нужны все в одном месте. Например, в `shared/ui` могут быть модули для каждого компонента в библиотеке UI: + + +- shared/ + - ui/ + - button/ + - text-field/ + - carousel/ + - accordion/ + + +Эта проблема усугубляется, когда один из этих модулей имеет тяжелую зависимость, такую как подсветка синтаксиса или библиотека drag'n'drop. Вы не хотите подтягивать их на каждую страницу, которая использует что-то из `shared/ui`, например, кнопку. + +Если ваши бандлы нежелательно растут из-за единого публичного API в `shared/ui` или `shared/lib`, рекомендуется вместо этого иметь отдельный индексный файл для каждого компонента или библиотеки: + + +- shared/ + - ui/ + - button + - index.js + - text-field + - index.js + + +Тогда потребители этих компонентов могут импортировать их напрямую, как показано ниже: + +```js title="pages/sign-in/ui/SignInPage.jsx" +import { Button } from '@/shared/ui/button'; +import { TextField } from '@/shared/ui/text-field'; +``` + +### Нет реальной защиты от обхода публичного API + +Когда вы создаете индексный файл для слайса, ничто не мешает другим не использовать его и импортировать напрямую. Это особенно заметно с автоимпортами, потому что существует несколько мест, откуда объект может быть импортирован, поэтому IDE должна решить за вас. Иногда она может выбрать прямой импорт, нарушая правило публичного API для слайсов. + +Чтобы автоматически выявлять эти проблемы, мы рекомендуем использовать [Steiger][ext-steiger], архитектурный линтер с набором правил для Feature-Sliced Design. + +### Худшая производительность сборщиков на больших проектах + +Наличие большого количества индексных файлов в проекте может замедлить работу сервера разработки, как отметил TkDodo в [своей статье "Please Stop Using Barrel Files"][ext-please-stop-using-barrel-files]. + +Есть несколько вещей, которые вы можете сделать, чтобы справиться с этой проблемой: + +1. То же самое, что и в разделе "Большие бандлы и неработающий tree-shaking в Shared" — создайте отдельные индексные файлы для каждого компонента/библиотеки в `shared/ui` и `shared/lib` вместо одного большого +2. Избегайте наличия индексных файлов в сегментах на слоях, которые имеют слайсы. + Например, если у вас есть индекс для фичи "comments", `📄 features/comments/index.js`, нет смысла иметь еще один индекс для `ui` сегмента этой фичи, `📄 features/comments/ui/index.js`. +3. Если у вас очень большой проект, есть большая вероятность, что ваше приложение можно разделить на несколько больших кусков. + Например, в Google Docs задачи, которые решают редактор документов и файловый браузер, сильно отличаются. Вы можете создать монорепозиторий, где каждый пакет является отдельным корнем FSD со своим набором слоев. Некоторые пакеты могут иметь только слои Shared и Entities, другие могут иметь только Pages и App, а некоторые могут включать свой небольшой Shared, но при этом использовать большой Shared из другого пакета. + +[import-rule-on-layers]: /docs/reference/layers#import-rule-on-layers +[ext-steiger]: https://github.com/feature-sliced/steiger +[ext-please-stop-using-barrel-files]: https://tkdodo.eu/blog/please-stop-using-barrel-files diff --git a/src/content/docs/ru/docs/reference/slices-segments.mdx b/src/content/docs/ru/docs/reference/slices-segments.mdx new file mode 100644 index 0000000000..4c94b3227b --- /dev/null +++ b/src/content/docs/ru/docs/reference/slices-segments.mdx @@ -0,0 +1,71 @@ +--- +title: Слайсы и сегменты +sidebar: + order: 2 +--- + +import { FileTree, Aside } from '@astrojs/starlight/components'; + +## Слайсы + +Слайсы — это второй уровень в организационной иерархии Feature-Sliced Design. Их основное назначение — группировать код по его значению для продукта, бизнеса или просто приложения. + +Имена слайсов не стандартизированы, поскольку они напрямую определяются бизнес-областью вашего приложения. Например, фотогалерея может иметь слайсы `photo`, `effects`, `gallery-page`. Для социальной сети потребуются другие слайсы, например, `post`, `comments`, `news-feed`. + +Слои Shared и App не содержат слайсов. Это потому, что Shared не должен содержать никакой бизнес-логики, следовательно, не имеет продуктового значения, а App должен содержать только код, касающийся всего приложения, поэтому разделение не требуется. + +### Нулевая сцепленность и высокая связность \{#zero-coupling-high-cohesion\} + +Слайсы задуманы как независимые и сильно связанные группы файлов кода. Картинка ниже может помочь визуализировать такие сложные концепции как _связность_ (cohesion) и _сцепленность_ (coupling): + +
+ + +
+ Картинка вдохновлена https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/ +
+
+ +Идеальный слайс независим от других слайсов на своем уровне (нулевая сцепленность) и содержит бо́льшую часть кода, связанного с его основной целью (высокая связность). + +Независимость слайсов обеспечивается [правилом импорта для слоёв][layers--import-rule]: + +> _Модуль (файл) в слайсе может импортировать другие слайсы только если они находятся на слоях строго ниже._ + +### Правило публичного API для слайсов + +Внутри слайса код может быть организован как угодно, и это не создаст никаких проблем до тех пор, пока слайс имеет качественный публичный API. В этом суть правила **публичного API для слайсов**: + +> _Каждый слайс (и сегмент на слоях, не имеющих слайсов) должен содержать определение публичного API._ +> +> _Модули вне этого слайса/сегмента могут ссылаться только на публичный API, а не на внутреннюю файловую структуру этого слайса/сегмента._ + +Подробнее о причинах требования публичных API и лучших практиках их создания читайте в [справочнике о публичных API][ref-public-api]. + +### Группы слайсов + +Тесно связанные слайсы могут быть структурно сгруппированы в папку, но они должны соблюдать те же правила изоляции, что и другие слайсы — **никакого совместного использования кода** в этой папке быть не должно. + +![Функции "compose", "like" и "delete" сгруппированы в папку "post". В этой папке также есть файл "some-shared-code.ts", который зачеркнут, чтобы показать, что это запрещено.](/img/graphic-nested-slices.svg) + +## Сегменты + +Сегменты — это третий и последний уровень в организационной иерархии, их цель — группировать код по его техническому назначению. + +Существует несколько стандартизированных названий сегментов: + +- `ui` — все, что связано с отображением UI: UI-компоненты, форматтеры дат, стили и т.д. +- `api` — взаимодействие с бэкендом: функции запросов, типы данных, мапперы и т.д. +- `model` — модель данных: схемы, интерфейсы, хранилища и бизнес-логика. +- `lib` — библиотечный код, необходимый другим модулям в этом слайсе. +- `config` — конфигурационные файлы и фиче-флаги. + +На [странице про Слои][layers--layer-definitions] есть примеры того, как каждый из этих сегментов может использоваться на разных слоях. + +Вы также можете создавать свои собственные сегменты. Наиболее распространенные места для кастомных сегментов — это слои App и Shared, где слайсы не имеют смысла. + +Убедитесь, что название этих сегментов описывает, для чего нужно его содержимое, а не чем оно является. Например, `components`, `hooks` и `types` — плохие названия сегментов, потому что они не так полезны, когда вы ищете код. + +[layers--layer-definitions]: /docs/reference/layers#layer-definitions +[layers--import-rule]: /docs/reference/layers#import-rule-on-layers +[ref-public-api]: /docs/reference/public-api From c6826b3119b10bbdca323d31ecac6b36324335fa Mon Sep 17 00:00:00 2001 From: Solant Date: Sat, 7 Feb 2026 01:31:26 +0100 Subject: [PATCH 31/45] use aside consistently across the files --- src/content/docs/docs/reference/layers.mdx | 2 +- src/content/docs/docs/reference/public-api.mdx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/docs/docs/reference/layers.mdx b/src/content/docs/docs/reference/layers.mdx index 60054eda4f..7e46d65833 100644 --- a/src/content/docs/docs/reference/layers.mdx +++ b/src/content/docs/docs/reference/layers.mdx @@ -107,7 +107,7 @@ The Widgets layer is intended for large self-sufficient blocks of UI. Widgets ar If a block of UI makes up most of the interesting content on a page, and is never reused, it **should not be a widget**, and instead it should be placed directly inside that page. -