generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new console module page (#2723)
Screencast: https://sq-tbd.slack.com/archives/C04PEQERFM0/p1726706223996289 --------- Co-authored-by: Wes <[email protected]>
- Loading branch information
1 parent
3585981
commit 246395e
Showing
7 changed files
with
279 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,20 @@ | ||
import { useMemo } from 'react' | ||
import { useParams } from 'react-router-dom' | ||
import { DeploymentPage } from '../deployments/DeploymentPage' | ||
import { useModules } from '../../api/modules/use-modules' | ||
import { Schema } from './schema/Schema' | ||
|
||
export const ModulePanel = () => { | ||
const { moduleName } = useParams() | ||
const modules = useModules() | ||
|
||
return <DeploymentPage moduleName={moduleName || ''} /> | ||
const module = useMemo(() => { | ||
if (!modules.isSuccess || modules.data.modules.length === 0) { | ||
return | ||
} | ||
return modules.data.modules.find((module) => module.name === moduleName) | ||
}, [modules?.data, moduleName]) | ||
|
||
if (!module) return | ||
|
||
return <Schema schema={module.schema} /> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
frontend/console/src/features/modules/schema/LinkTokens.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { useParams } from 'react-router-dom' | ||
import { DeclLink } from '../decls/DeclLink' | ||
import { UnderlyingType } from './UnderlyingType' | ||
|
||
export const LinkToken = ({ token }: { token: string }) => { | ||
const { moduleName } = useParams() | ||
if (token.match(/^\w+$/)) { | ||
return ( | ||
<span className='font-bold'> | ||
<DeclLink slim moduleName={moduleName} declName={token} /> | ||
</span> | ||
) | ||
} | ||
return token | ||
} | ||
|
||
export const LinkVerbNameToken = ({ token }: { token: string }) => { | ||
const splitToken = token.split('(') | ||
return ( | ||
<span> | ||
<LinkToken token={splitToken[0]} /> | ||
(<UnderlyingType token={splitToken[1]} /> | ||
</span> | ||
) | ||
} |
121 changes: 121 additions & 0 deletions
121
frontend/console/src/features/modules/schema/Schema.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { useParams } from 'react-router-dom' | ||
import { classNames } from '../../../utils' | ||
import { DeclLink } from '../decls/DeclLink' | ||
import { LinkToken, LinkVerbNameToken } from './LinkTokens' | ||
import { UnderlyingType } from './UnderlyingType' | ||
import { commentPrefix, declTypes, isFirstLineOfBlock, specialChars, staticKeywords } from './schema.utils' | ||
|
||
function maybeRenderDeclName(token: string, declType: string, tokens: string[], i: number) { | ||
const offset = declType === 'database' ? 4 : 2 | ||
if (i - offset < 0 || declType !== tokens[i - offset]) { | ||
return | ||
} | ||
if (declType === 'enum') { | ||
return [<LinkToken key='l' token={token.slice(0, token.length - 1)} />, token.slice(-1)] | ||
} | ||
if (declType === 'verb') { | ||
return <LinkVerbNameToken token={token} /> | ||
} | ||
return <LinkToken token={token} /> | ||
} | ||
|
||
function maybeRenderUnderlyingType(token: string, declType: string, tokens: string[], i: number, moduleName: string) { | ||
if (declType === 'database') { | ||
return | ||
} | ||
|
||
// Parse type(s) out of the headline signature | ||
const offset = 4 | ||
if (i - offset >= 0 && tokens.slice(0, i - offset + 1).includes(declType)) { | ||
return <UnderlyingType token={token} /> | ||
} | ||
|
||
// Parse type(s) out of nested lines | ||
if (tokens.length > 4 && tokens.slice(0, 4).filter((t) => t !== ' ').length === 0) { | ||
if (i === 6 && tokens[4] === '+calls') { | ||
return <UnderlyingType token={token} /> | ||
} | ||
if (i === 6 && tokens[4] === '+subscribe') { | ||
return <DeclLink moduleName={moduleName} declName={token} textColors='font-bold text-green-700 dark:text-green-400' /> | ||
} | ||
const plusIndex = tokens.findIndex((t) => t.startsWith('+')) | ||
if (i >= 6 && (i < plusIndex || plusIndex === -1)) { | ||
return <UnderlyingType token={token} /> | ||
} | ||
} | ||
} | ||
|
||
const SchemaLine = ({ line }: { line: string }) => { | ||
const { moduleName } = useParams() | ||
if (line.startsWith(commentPrefix)) { | ||
return <span className='text-gray-500 dark:text-gray-400'>{line}</span> | ||
} | ||
const tokens = line.split(/( )/).filter((l) => l !== '') | ||
let declType: string | ||
return tokens.map((token, i) => { | ||
if (token.trim() === '') { | ||
return <span key={i}>{token}</span> | ||
} | ||
if (specialChars.includes(token)) { | ||
return <span key={i}>{token}</span> | ||
} | ||
if (staticKeywords.includes(token)) { | ||
return ( | ||
<span key={i} className='text-fuchsia-700 dark:text-fuchsia-400'> | ||
{token} | ||
</span> | ||
) | ||
} | ||
if (declTypes.includes(token) && tokens.length > 2 && tokens[2] !== ' ') { | ||
declType = token | ||
return ( | ||
<span key={i} className='text-fuchsia-700 dark:text-fuchsia-400'> | ||
{token} | ||
</span> | ||
) | ||
} | ||
if (token[0] === '+' && token.slice(1).match(/^\w+$/)) { | ||
return ( | ||
<span key={i} className='text-fuchsia-700 dark:text-fuchsia-400'> | ||
{token} | ||
</span> | ||
) | ||
} | ||
|
||
const numQuotesBefore = (tokens.slice(0, i).join('').match(/"/g) || []).length + (token.match(/^".+/) ? 1 : 0) | ||
const numQuotesAfter = | ||
( | ||
tokens | ||
.slice(i + 1, tokens.length) | ||
.join('') | ||
.match(/"/g) || [] | ||
).length + (token.match(/.+"$/) ? 1 : 0) | ||
if (numQuotesBefore % 2 === 1 && numQuotesAfter % 2 === 1) { | ||
return ( | ||
<span key={i} className='text-rose-700 dark:text-rose-300'> | ||
{token} | ||
</span> | ||
) | ||
} | ||
|
||
const maybeDeclName = maybeRenderDeclName(token, declType, tokens, i) | ||
if (maybeDeclName) { | ||
return <span key={i}>{maybeDeclName}</span> | ||
} | ||
const maybeUnderlyingType = maybeRenderUnderlyingType(token, declType, tokens, i, moduleName || '') | ||
if (maybeUnderlyingType) { | ||
return <span key={i}>{maybeUnderlyingType}</span> | ||
} | ||
return <span key={i}>{token}</span> | ||
}) | ||
} | ||
|
||
export const Schema = ({ schema }: { schema: string }) => { | ||
const ll = schema.split('\n') | ||
const lines = ll.map((l, i) => ( | ||
<div key={i} className={classNames('mb-1', isFirstLineOfBlock(ll, i) ? 'mt-4' : '')}> | ||
<SchemaLine line={l} /> | ||
</div> | ||
)) | ||
return <div className='mt-4 mx-4 whitespace-pre font-mono text-xs'>{lines}</div> | ||
} |
75 changes: 75 additions & 0 deletions
75
frontend/console/src/features/modules/schema/UnderlyingType.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { DeclLink } from '../decls/DeclLink' | ||
|
||
export const UnderlyingType = ({ token }: { token: string }) => { | ||
if (token.match(/^\[.+\]$/)) { | ||
// Handles lists: [elementType] | ||
return ( | ||
<span className='text-green-700 dark:text-green-400'> | ||
[<UnderlyingType token={token.slice(1, token.length - 1)} />] | ||
</span> | ||
) | ||
} | ||
|
||
if (token.match(/^{.+:$/)) { | ||
// Handles first token of map: {KeyType: ValueType} | ||
return ( | ||
<span className='text-green-700 dark:text-green-400'> | ||
{'{'} | ||
<UnderlyingType token={token.slice(1, token.length - 1)} />: | ||
</span> | ||
) | ||
} | ||
|
||
if (token.match(/.+}$/)) { | ||
// Handles last token of map: {KeyType: ValueType} | ||
return ( | ||
<span className='text-green-700 dark:text-green-400'> | ||
<UnderlyingType token={token.slice(0, token.length - 1)} /> | ||
{'}'} | ||
</span> | ||
) | ||
} | ||
|
||
if (token.match(/^.+\?$/)) { | ||
// Handles optional: elementType? | ||
return ( | ||
<span className='text-green-700 dark:text-green-400'> | ||
<UnderlyingType token={token.slice(0, token.length - 1)} />? | ||
</span> | ||
) | ||
} | ||
|
||
if (token.match(/^.+\)$/)) { | ||
// Handles closing parens in param list of verb signature: verb echo(inputType) outputType | ||
return ( | ||
<span> | ||
<UnderlyingType token={token.slice(0, token.length - 1)} />) | ||
</span> | ||
) | ||
} | ||
|
||
const maybeSplitRef = token.split('.') | ||
if (maybeSplitRef.length < 2) { | ||
// Not linkable because it's not a ref | ||
return <span className='text-green-700 dark:text-green-400'>{token}</span> | ||
} | ||
const moduleName = maybeSplitRef[0] | ||
const declName = maybeSplitRef[1].split('<')[0] | ||
const primaryTypeEl = ( | ||
<span className='text-green-700 dark:text-green-400'> | ||
<DeclLink moduleName={moduleName} declName={declName.split(/[,>]/)[0]} textColors='font-bold text-green-700 dark:text-green-400' /> | ||
{[',', '>'].includes(declName.slice(-1)) ? declName.slice(-1) : ''} | ||
</span> | ||
) | ||
const hasTypeParams = maybeSplitRef[1].includes('<') | ||
if (!hasTypeParams) { | ||
return primaryTypeEl | ||
} | ||
return ( | ||
<span className='text-green-700 dark:text-green-400'> | ||
{primaryTypeEl} | ||
{'<'} | ||
<UnderlyingType token={maybeSplitRef.length === 2 ? maybeSplitRef[1].split('<')[1] : `${maybeSplitRef[1].split('<')[1]}.${maybeSplitRef.slice(2)}`} /> | ||
</span> | ||
) | ||
} |
30 changes: 30 additions & 0 deletions
30
frontend/console/src/features/modules/schema/schema.utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
export const commentPrefix = ' //' | ||
|
||
export const staticKeywords = ['module', 'export'] | ||
|
||
export const declTypes = ['config', 'data', 'database', 'enum', 'fsm', 'topic', 'typealias', 'secret', 'subscription', 'verb'] | ||
|
||
export const specialChars = ['{', '}', '='] | ||
|
||
export function isFirstLineOfBlock(ll: string[], i: number): boolean { | ||
if (i === 0) { | ||
// Never add space for the first block | ||
return false | ||
} | ||
if (ll[i].startsWith(' ')) { | ||
// Never add space for nested lines | ||
return false | ||
} | ||
if (ll[i - 1].startsWith(commentPrefix)) { | ||
// Prior line is a comment | ||
return false | ||
} | ||
if (ll[i].startsWith(commentPrefix)) { | ||
return true | ||
} | ||
const tokens = ll[i].trim().split(' ') | ||
if (!tokens || tokens.length === 0) { | ||
return false | ||
} | ||
return staticKeywords.includes(tokens[0]) || declTypes.includes(tokens[0]) | ||
} |