0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(console,phrases): integrate monaco editor (#5460)

* feat(console,phrases): integrate monaco editor

integrate monaco editor

* chore: update pnpm lock file

update pnpm lock file

* fix(console): remove unused styles

remove unused styles

* feat(console): update font styles

update font styles
This commit is contained in:
simeng-li 2024-03-06 14:08:46 +08:00 committed by GitHub
parent e1def81ed5
commit 2f72f8ffd7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 574 additions and 36 deletions

View file

@ -36,6 +36,7 @@
"@logto/schemas": "workspace:^1.13.1", "@logto/schemas": "workspace:^1.13.1",
"@logto/shared": "workspace:^3.1.0", "@logto/shared": "workspace:^3.1.0",
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"@monaco-editor/react": "^4.6.0",
"@parcel/compressor-brotli": "2.9.3", "@parcel/compressor-brotli": "2.9.3",
"@parcel/compressor-gzip": "2.9.3", "@parcel/compressor-gzip": "2.9.3",
"@parcel/core": "2.9.3", "@parcel/core": "2.9.3",

View file

@ -0,0 +1,24 @@
import { type EditorProps } from '@monaco-editor/react';
import type { IStandaloneThemeData } from './type';
// Logto dark theme extends vs-dark theme
export const logtoDarkTheme: IStandaloneThemeData = {
base: 'vs-dark',
inherit: true,
rules: [],
colors: {
'editor.background': '#181133', // :token/code/code-bg
},
};
// @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html}
export const defaultOptions: EditorProps['options'] = {
minimap: {
enabled: false,
},
wordWrap: 'on',
renderLineHighlight: 'none',
fontFamily: 'Roboto Mono, monospace',
fontSize: 14,
};

View file

@ -0,0 +1,58 @@
@use '@/scss/underscore' as _;
@use '@/scss/code-editor' as codeEditor;
.codeEditor {
@include codeEditor.color;
@include codeEditor.font;
position: relative;
display: flex;
flex-direction: column;
border-radius: 8px;
background-color: var(--color-code-bg);
header {
padding: _.unit(4);
display: flex;
justify-content: space-between;
align-items: center;
.tabList {
display: flex;
gap: _.unit(2);
.tab {
padding: _.unit(1.5) _.unit(3);
font: var(--font-label-2);
font-family: 'Roboto Mono', monospace;
color: var(--color-code-grey);
cursor: pointer;
&.active,
&:hover {
color: var(--color-code-white);
background-color: var(--color-code-tab-active-bg);
border-radius: 8px;
}
}
}
.title {
font: var(--font-label-2);
font-family: 'Roboto Mono', monospace;
color: var(--color-code-white);
}
.actions {
display: flex;
gap: _.unit(2);
align-items: center;
}
}
.editorContainer {
position: relative;
flex-grow: 1;
padding-bottom: _.unit(4);
}
}

View file

@ -0,0 +1,126 @@
import { Editor, type BeforeMount, type OnMount, useMonaco } from '@monaco-editor/react';
import { type Nullable } from '@silverhand/essentials';
import classNames from 'classnames';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import Copy from '@/assets/icons/copy.svg';
import IconButton from '@/ds-components/IconButton';
import { onKeyDownHandler } from '@/utils/a11y';
import { logtoDarkTheme, defaultOptions } from './config.js';
import * as styles from './index.module.scss';
import type { IStandaloneCodeEditor, Model } from './type.js';
type Props = {
className?: string;
actions?: React.ReactNode;
models: Model[];
};
function MonacoCodeEditor({ className, actions, models }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const monaco = useMonaco();
const editorRef = useRef<Nullable<IStandaloneCodeEditor>>(null);
const [activeModelName, setActiveModelName] = useState<string>();
const activeModel = useMemo(
() => models.find((model) => model.name === activeModelName),
[activeModelName, models]
);
// Set the first model as the active model
useEffect(() => {
setActiveModelName(models[0]?.name);
}, [models]);
useEffect(() => {
// Add global declarations
// monaco will be ready after the editor is mounted, useEffect will be called after the monaco is ready
if (!monaco || !activeModel) {
return;
}
// Set the global declarations for the active model
// @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.typescript.LanguageServiceDefaults.html#setExtraLibs}
if (activeModel.globalDeclarations) {
monaco.languages.typescript.typescriptDefaults.setExtraLibs([
{
content: activeModel.globalDeclarations,
filePath: `file:///global.d.ts`,
},
]);
}
}, [activeModel, monaco]);
const handleCodeCopy = useCallback(async () => {
const editor = editorRef.current;
if (editor) {
const code = editor.getValue();
await navigator.clipboard.writeText(code);
toast.success(t('general.copied'));
}
}, [t]);
const handleEditorWillMount = useCallback<BeforeMount>((monaco) => {
// Register the new logto theme
monaco.editor.defineTheme('logto-dark', logtoDarkTheme);
}, []);
const handleEditorDidMount = useCallback<OnMount>((editor) => {
// eslint-disable-next-line @silverhand/fp/no-mutation
editorRef.current = editor;
}, []);
return (
<div className={classNames(className, styles.codeEditor)}>
<header>
{models.length > 1 ? (
<div className={styles.tabList}>
{models.map(({ name }) => (
<div
key={name}
className={classNames(styles.tab, name === activeModelName && styles.active)}
role="button"
tabIndex={0}
onClick={() => {
setActiveModelName(name);
}}
onKeyDown={onKeyDownHandler(() => {
setActiveModelName(name);
})}
>
{name}
</div>
))}
</div>
) : (
<div className={styles.title}>{activeModel?.title}</div>
)}
<div className={styles.actions}>
{actions}
<IconButton size="small" onClick={handleCodeCopy}>
<Copy />
</IconButton>
</div>
</header>
<div className={styles.editorContainer}>
<Editor
height="100%"
language="typescript"
// TODO: need to check on the usage of value and defaultValue
defaultValue={activeModel?.defaultValue}
path={activeModel?.name}
theme="logto-dark"
options={defaultOptions}
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
/>
</div>
</div>
);
}
export default MonacoCodeEditor;

View file

@ -0,0 +1,15 @@
import { type Monaco, type OnMount } from '@monaco-editor/react';
export type IStandaloneThemeData = Parameters<Monaco['editor']['defineTheme']>[1];
export type IStandaloneCodeEditor = Parameters<OnMount>[0];
export type Model = {
/** Used as the unique key for the monaco editor model @see {@link https://github.com/suren-atoyan/monaco-react?tab=readme-ov-file#multi-model-editor} */
name: string;
/** The title of the model */
title: string;
defaultValue: string;
language: string;
globalDeclarations?: string;
};

View file

@ -0,0 +1,118 @@
import type { Model } from './MonacoCodeEditor/type.js';
const userJwtGlobalDeclarations = `
declare global {
export interface CustomJwtClaims extends Record<string, any> {}
/** The user info associated with the token.
*
* @param {string} id - The user id
* @param {string} [primaryEmail] - The user email
* @param {string} [primaryPhone] - The user phone
* @param {string} [username] - The user username
* @param {string} [name] - The user name
* @param {string} [avatar] - The user avatar
*
*/
export type User = {
id: string;
primaryEmail?: string;
primaryPhone?: string;
username?: string;
name?: string;
avatar?: string;
}
/** Logto internal data that can be used to pass additional information
* @param {User} user - The user info associated with the token.
*/
export type Data = {
user: User;
}
export interface Exports {
/**
* This function is called to get custom claims for the JWT token.
*
* @param {string} token -The JWT token.
* @param {Data} data - Logto internal data that can be used to pass additional information
* @param {User} data.user - The user info associated with the token.
*
* @returns The custom claims.
*/
getCustomJwtClaims: (token: string, data: Data) => Promise<CustomJwtClaims>;
}
const exports: Exports;
}
export { exports as default };
`;
const machineToMachineJwtGlobalDeclarations = `
declare global {
export interface CustomJwtClaims extends Record<string, any> {}
export interface Exports {
/**
* This function is called to get custom claims for the JWT token.
*
* @param {string} token -The JWT token.
*
* @returns The custom claims.
*/
getCustomJwtClaims: (token: string) => Promise<CustomJwtClaims>;
}
const exports: Exports;
}
export { exports as default };
`;
const defaultUserJwtClaimsCode = `/**
* This function is called to get custom claims for the JWT token.
*
* @param {string} token -The JWT token.
* @param {Data} data - Logto internal data that can be used to pass additional information
* @param {User} data.user - The user info associated with the token.
*
* @returns The custom claims.
*/
exports.getCustomJwtClaims = async (token, data) => {
return {};
}`;
const defaultMachineToMachineJwtClaimsCode = `/**
* This function is called to get custom claims for the JWT token.
*
* @param {string} token -The JWT token.
*
* @returns The custom claims.
*/
exports.getCustomJwtClaims = async (token) => {
return {};
}`;
export const userJwtFile: Model = {
name: 'user-jwt.ts',
title: 'TypeScript',
language: 'typescript',
defaultValue: defaultUserJwtClaimsCode,
globalDeclarations: userJwtGlobalDeclarations,
};
export const machineToMachineJwtFile: Model = {
name: 'machine-to-machine-jwt.ts',
title: 'TypeScript',
language: 'typescript',
defaultValue: defaultMachineToMachineJwtClaimsCode,
globalDeclarations: machineToMachineJwtGlobalDeclarations,
};
export enum JwtTokenType {
UserAccessToken = 'user-access-token',
MachineToMachineAccessToken = 'm2m-access-token',
}

View file

@ -5,8 +5,39 @@
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
.cardTitle { .header {
flex-shrink: 0; flex-shrink: 0;
margin-bottom: _.unit(4); margin-bottom: _.unit(4);
} }
.tabNav {
margin-bottom: _.unit(4);
}
}
.tabContent {
display: flex;
flex-direction: row;
gap: _.unit(2);
flex-grow: 1;
> * {
flex: 1;
}
}
.codePanel {
margin-bottom: _.unit(6);
position: relative;
display: flex;
flex-direction: column;
.cardTitle {
font: var(--font-title-2);
margin-bottom: _.unit(3);
}
.flexGrow {
flex-grow: 1;
}
} }

View file

@ -1,44 +1,73 @@
import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact'; import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact';
import classNames from 'classnames';
import type { TFuncKey } from 'i18next';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Card from '@/ds-components/Card';
import CardTitle from '@/ds-components/CardTitle'; import CardTitle from '@/ds-components/CardTitle';
import TabNav, { TabNavItem } from '@/ds-components/TabNav'; import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import MonacoCodeEditor from './MonacoCodeEditor';
import { type Model } from './MonacoCodeEditor/type';
import { userJwtFile, machineToMachineJwtFile, JwtTokenType } from './config';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
import { JwtTokenType } from './type';
export * from './type'; export { JwtTokenType } from './config';
type Props = { type Props = {
tab: JwtTokenType; tab: JwtTokenType;
}; };
const phrases = Object.freeze({
tab: {
[JwtTokenType.UserAccessToken]: 'user_jwt_tab',
[JwtTokenType.MachineToMachineAccessToken]: 'machine_to_machine_jwt_tab',
},
token: {
[JwtTokenType.UserAccessToken]: 'user_jwt',
[JwtTokenType.MachineToMachineAccessToken]: 'machine_to_machine_jwt',
},
} satisfies Record<
string,
Record<JwtTokenType, TFuncKey<'translation', 'admin_console.jwt_claims'>>
>);
const getPath = (tab: JwtTokenType) => `/jwt-claims/${tab}`; const getPath = (tab: JwtTokenType) => `/jwt-claims/${tab}`;
function JwtClaims({ tab }: Props) { function JwtClaims({ tab }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
// TODO: API integration, read/write the custom claims code value
const activeModel = useMemo<Model>(() => {
return tab === JwtTokenType.UserAccessToken ? userJwtFile : machineToMachineJwtFile;
}, [tab]);
return ( return (
<div className={styles.container}> <div className={styles.container}>
<CardTitle <CardTitle
title="jwt_claims.title" title="jwt_claims.title"
subtitle="jwt_claims.description" subtitle="jwt_claims.description"
className={styles.cardTitle} className={styles.header}
/> />
<TabNav> <TabNav className={styles.tabNav}>
<TabNavItem {Object.values(JwtTokenType).map((tokenType) => (
href={getPath(JwtTokenType.UserAccessToken)} <TabNavItem key={tokenType} href={getPath(tokenType)} isActive={tokenType === tab}>
isActive={tab === JwtTokenType.UserAccessToken} {t(`jwt_claims.${phrases.tab[tokenType]}`)}
> </TabNavItem>
{t('jwt_claims.user_jwt_tab')} ))}
</TabNavItem>
<TabNavItem
href={getPath(JwtTokenType.MachineToMachineAccessToken)}
isActive={tab === JwtTokenType.MachineToMachineAccessToken}
>
{t('jwt_claims.machine_to_machine_jwt_tab')}
</TabNavItem>
</TabNav> </TabNav>
<form className={classNames(styles.tabContent)}>
<Card className={styles.codePanel}>
<div className={styles.cardTitle}>
{t('jwt_claims.code_editor_title', {
token: t(`jwt_claims.${phrases.token[tab]}`),
})}
</div>
<MonacoCodeEditor className={styles.flexGrow} models={[activeModel]} />
</Card>
<div>Form Panel</div>
</form>
</div> </div>
); );
} }

View file

@ -1,4 +0,0 @@
export enum JwtTokenType {
UserAccessToken = 'user-access-token',
MachineToMachineAccessToken = 'm2m-access-token',
}

View file

@ -0,0 +1,10 @@
@mixin color {
--color-code-bg: #181133;
--color-code-white: var(--color-base);
--color-code-grey: #adaab4;
--color-code-tab-active-bg: rgba(255, 255, 255, 24%);
}
@mixin font {
--font-code: 500 14px/20px 'Roboto Mono', monospace;
}

View file

@ -9,6 +9,7 @@ body {
color: var(--color-text); color: var(--color-text);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: auto; -moz-osx-font-smoothing: auto;
min-height: 100vh;
} }
* { * {

View file

@ -39,6 +39,14 @@ export default function koaSecurityHeaders<StateT, ContextT, ResponseBodyT>(
const developmentOrigins = conditionalArray(!isProduction && 'ws:'); const developmentOrigins = conditionalArray(!isProduction && 'ws:');
const appInsightsOrigins = ['https://*.applicationinsights.azure.com']; const appInsightsOrigins = ['https://*.applicationinsights.azure.com'];
// We use react-monaco-editor for code editing in the admin console. It loads the monaco editor asynchronously from a CDN.
// Allow the CDN src in the CSP.
// Allow blob: for monaco editor to load worker scripts
const monacoEditorCDNSource = [
'https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/vs/',
'blob:',
];
/** /**
* Default Applied rules: * Default Applied rules:
* - crossOriginOpenerPolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#cross-origin-opener-policy-coop * - crossOriginOpenerPolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#cross-origin-opener-policy-coop
@ -107,6 +115,7 @@ export default function koaSecurityHeaders<StateT, ContextT, ResponseBodyT>(
scriptSrc: [ scriptSrc: [
"'self'", "'self'",
...conditionalArray(!isProduction && ["'unsafe-eval'", "'unsafe-inline'"]), ...conditionalArray(!isProduction && ["'unsafe-eval'", "'unsafe-inline'"]),
...monacoEditorCDNSource,
], ],
connectSrc: [ connectSrc: [
"'self'", "'self'",

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -3,7 +3,10 @@ const jwt_claims = {
description: description:
'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.', 'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.',
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
user_jwt: 'user JWT',
machine_to_machine_jwt: 'machine-to-machine JWT',
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -7,7 +7,13 @@ const jwt_claims = {
/** UNTRANSLATED */ /** UNTRANSLATED */
user_jwt_tab: 'User JWT', user_jwt_tab: 'User JWT',
/** UNTRANSLATED */ /** UNTRANSLATED */
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT', machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
/** UNTRANSLATED */
user_jwt: 'user JWT',
/** UNTRANSLATED */
machine_to_machine_jwt: 'machine-to-machine JWT',
/** UNTRANSLATED */
code_editor_title: 'Customize the {{token}} claims',
}; };
export default Object.freeze(jwt_claims); export default Object.freeze(jwt_claims);

View file

@ -2948,6 +2948,9 @@ importers:
'@mdx-js/react': '@mdx-js/react':
specifier: ^1.6.22 specifier: ^1.6.22
version: 1.6.22(react@18.2.0) version: 1.6.22(react@18.2.0)
'@monaco-editor/react':
specifier: ^4.6.0
version: 4.6.0(monaco-editor@0.46.0)(react-dom@18.2.0)(react@18.2.0)
'@parcel/compressor-brotli': '@parcel/compressor-brotli':
specifier: 2.9.3 specifier: 2.9.3
version: 2.9.3(@parcel/core@2.9.3) version: 2.9.3(@parcel/core@2.9.3)
@ -7933,6 +7936,28 @@ packages:
json5: 2.2.1 json5: 2.2.1
dev: true dev: true
/@monaco-editor/loader@1.4.0(monaco-editor@0.46.0):
resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==}
peerDependencies:
monaco-editor: '>= 0.21.0 < 1'
dependencies:
monaco-editor: 0.46.0
state-local: 1.0.7
dev: true
/@monaco-editor/react@4.6.0(monaco-editor@0.46.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==}
peerDependencies:
monaco-editor: '>= 0.25.0 < 1'
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@monaco-editor/loader': 1.4.0(monaco-editor@0.46.0)
monaco-editor: 0.46.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
/@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2:
resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
cpu: [arm64] cpu: [arm64]
@ -17200,6 +17225,10 @@ packages:
resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
dev: false dev: false
/monaco-editor@0.46.0:
resolution: {integrity: sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==}
dev: true
/ms@2.1.2: /ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
@ -20081,6 +20110,10 @@ packages:
escape-string-regexp: 2.0.0 escape-string-regexp: 2.0.0
dev: true dev: true
/state-local@1.0.7:
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
dev: true
/state-toggle@1.0.3: /state-toggle@1.0.3:
resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==} resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==}
dev: true dev: true