🐛 fix(editor): Add Go template editor component and update related schemas
This commit is contained in:
parent
e7844d077b
commit
4a907257c6
@ -46,6 +46,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@workspace/ui/components/tooltip';
|
} from '@workspace/ui/components/tooltip';
|
||||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||||
|
import { GoTemplateEditor } from '@workspace/ui/custom-components/editor';
|
||||||
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
|
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
|
||||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||||
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
|
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
|
||||||
@ -63,7 +64,7 @@ const createClientFormSchema = (t: any) =>
|
|||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
icon: z.string().optional(),
|
icon: z.string().optional(),
|
||||||
user_agent: z.string().min(1, `User-Agent ${t('form.validation.userAgentRequiredSuffix')}`),
|
user_agent: z.string().min(1, `User-Agent ${t('form.validation.userAgentRequiredSuffix')}`),
|
||||||
proxy_template: z.string().default(''),
|
schema: z.string().default(''),
|
||||||
template: z.string().default(''),
|
template: z.string().default(''),
|
||||||
output_format: z.string().default(''),
|
output_format: z.string().default(''),
|
||||||
download_link: z.object({
|
download_link: z.object({
|
||||||
@ -94,7 +95,7 @@ export function ProtocolForm() {
|
|||||||
description: '',
|
description: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
user_agent: '',
|
user_agent: '',
|
||||||
proxy_template: '',
|
schema: '',
|
||||||
template: '',
|
template: '',
|
||||||
output_format: '',
|
output_format: '',
|
||||||
download_link: {
|
download_link: {
|
||||||
@ -230,7 +231,7 @@ export function ProtocolForm() {
|
|||||||
description: '',
|
description: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
user_agent: '',
|
user_agent: '',
|
||||||
proxy_template: '',
|
schema: '',
|
||||||
template: '',
|
template: '',
|
||||||
output_format: '',
|
output_format: '',
|
||||||
download_link: {
|
download_link: {
|
||||||
@ -252,7 +253,7 @@ export function ProtocolForm() {
|
|||||||
description: client.description || '',
|
description: client.description || '',
|
||||||
icon: client.icon || '',
|
icon: client.icon || '',
|
||||||
user_agent: client.user_agent,
|
user_agent: client.user_agent,
|
||||||
proxy_template: client.proxy_template || '',
|
schema: client.proxy_template || '',
|
||||||
template: client.template || '',
|
template: client.template || '',
|
||||||
output_format: client.output_format || '',
|
output_format: client.output_format || '',
|
||||||
download_link: {
|
download_link: {
|
||||||
@ -301,6 +302,7 @@ export function ProtocolForm() {
|
|||||||
if (editingClient) {
|
if (editingClient) {
|
||||||
await updateSubscribeApplication({
|
await updateSubscribeApplication({
|
||||||
...data,
|
...data,
|
||||||
|
proxy_template: data.schema || '',
|
||||||
is_default: editingClient.is_default,
|
is_default: editingClient.is_default,
|
||||||
id: editingClient.id,
|
id: editingClient.id,
|
||||||
});
|
});
|
||||||
@ -308,6 +310,7 @@ export function ProtocolForm() {
|
|||||||
} else {
|
} else {
|
||||||
await createSubscribeApplication({
|
await createSubscribeApplication({
|
||||||
...data,
|
...data,
|
||||||
|
proxy_template: data.schema || '',
|
||||||
is_default: false,
|
is_default: false,
|
||||||
});
|
});
|
||||||
toast.success(t('actions.createSuccess'));
|
toast.success(t('actions.createSuccess'));
|
||||||
@ -491,20 +494,17 @@ export function ProtocolForm() {
|
|||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name='proxy_template'
|
name='schema'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('form.fields.proxyTemplate')}</FormLabel>
|
<FormLabel>{t('form.fields.schema')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder='clash://install-config?url={url}&name={name}'
|
placeholder='clash://install-config?url={url}&name={name}'
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>{t('form.descriptions.schema')}</FormDescription>
|
||||||
{t('form.descriptions.proxyTemplate')}
|
|
||||||
{`{url} {name}`}
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@ -517,10 +517,10 @@ export function ProtocolForm() {
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('form.fields.template')}</FormLabel>
|
<FormLabel>{t('form.fields.template')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<GoTemplateEditor
|
||||||
placeholder='proxies:\n - name: {name}\n type: {type}\n server: {server}\n port: {port}'
|
enableSprig
|
||||||
className='min-h-[120px] font-mono text-sm'
|
value={field.value || ''}
|
||||||
{...field}
|
onChange={(value) => field.onChange(value)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>{t('form.descriptions.template')}</FormDescription>
|
<FormDescription>{t('form.descriptions.template')}</FormDescription>
|
||||||
|
|||||||
@ -52,8 +52,8 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"outputFormat": "Output Format",
|
"outputFormat": "Output Format",
|
||||||
"proxyTemplate": "Subscription Link",
|
"schema": "URL Schema",
|
||||||
"template": "Config Template"
|
"template": "Subscription File Template"
|
||||||
},
|
},
|
||||||
"descriptions": {
|
"descriptions": {
|
||||||
"icon": "Icon URL or base64 encoding",
|
"icon": "Icon URL or base64 encoding",
|
||||||
@ -61,8 +61,8 @@
|
|||||||
"userAgentPrefix": "Client identifier for distinguishing different clients",
|
"userAgentPrefix": "Client identifier for distinguishing different clients",
|
||||||
"description": "Detailed client description",
|
"description": "Detailed client description",
|
||||||
"outputFormat": "Subscription configuration file format",
|
"outputFormat": "Subscription configuration file format",
|
||||||
"proxyTemplate": "Subscription link template, supports variables: ",
|
"schema": "URL Schema template, supports variables: '{url}' subscription URL, '{name}' site name",
|
||||||
"template": "Subscription configuration template, supports various variables",
|
"template": "Subscription file template, supports various variables",
|
||||||
"downloadLink": "platform download URL"
|
"downloadLink": "platform download URL"
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
|
|||||||
@ -47,8 +47,8 @@
|
|||||||
"icon": "图标 URL 或 base64 编码",
|
"icon": "图标 URL 或 base64 编码",
|
||||||
"name": "客户端显示名称",
|
"name": "客户端显示名称",
|
||||||
"outputFormat": "订阅配置文件格式",
|
"outputFormat": "订阅配置文件格式",
|
||||||
"proxyTemplate": "订阅链接模板,支持变量:",
|
"schema": "URL Schema 模板,支持变量:'{url}' 订阅地址,'{name}' 站点名称",
|
||||||
"template": "订阅配置模板,支持各种变量",
|
"template": "订阅文件模板,支持各种变量",
|
||||||
"userAgentPrefix": "用于区分不同客户端的客户端标识符"
|
"userAgentPrefix": "用于区分不同客户端的客户端标识符"
|
||||||
},
|
},
|
||||||
"editTitle": "编辑客户端",
|
"editTitle": "编辑客户端",
|
||||||
@ -57,8 +57,8 @@
|
|||||||
"icon": "图标",
|
"icon": "图标",
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
"outputFormat": "输出格式",
|
"outputFormat": "输出格式",
|
||||||
"proxyTemplate": "订阅链接",
|
"schema": "URL Schema",
|
||||||
"template": "配置模板"
|
"template": "订阅文件模板"
|
||||||
},
|
},
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"basic": "基本信息",
|
"basic": "基本信息",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
import * as ads from './ads';
|
import * as ads from './ads';
|
||||||
|
|||||||
3
apps/admin/services/admin/typings.d.ts
vendored
3
apps/admin/services/admin/typings.d.ts
vendored
@ -339,6 +339,7 @@ declare namespace API {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
scheme?: string;
|
||||||
user_agent: string;
|
user_agent: string;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
proxy_template: string;
|
proxy_template: string;
|
||||||
@ -1501,6 +1502,7 @@ declare namespace API {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
scheme?: string;
|
||||||
user_agent: string;
|
user_agent: string;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
proxy_template: string;
|
proxy_template: string;
|
||||||
@ -1779,6 +1781,7 @@ declare namespace API {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
scheme?: string;
|
||||||
user_agent: string;
|
user_agent: string;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
proxy_template: string;
|
proxy_template: string;
|
||||||
|
|||||||
@ -65,6 +65,7 @@
|
|||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"lucide-react": "^0.473.0",
|
"lucide-react": "^0.473.0",
|
||||||
"mathjs": "^14.0.1",
|
"mathjs": "^14.0.1",
|
||||||
|
"monaco-themes": "^0.4.6",
|
||||||
"motion": "^11.18.1",
|
"motion": "^11.18.1",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"react-day-picker": "8.10.1",
|
"react-day-picker": "8.10.1",
|
||||||
|
|||||||
402
packages/ui/src/custom-components/editor/go-template.tsx
Normal file
402
packages/ui/src/custom-components/editor/go-template.tsx
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { type Monaco } from '@monaco-editor/react';
|
||||||
|
import {
|
||||||
|
MonacoEditor,
|
||||||
|
MonacoEditorProps,
|
||||||
|
} from '@workspace/ui/custom-components/editor/monaco-editor';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
// Import Dracula theme
|
||||||
|
import DraculaTheme from 'monaco-themes/themes/Dracula.json' with { type: 'json' };
|
||||||
|
|
||||||
|
export interface GoTemplateEditorProps extends Omit<MonacoEditorProps, 'language'> {
|
||||||
|
schema?: Record<string, unknown>;
|
||||||
|
enableSprig?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go template syntax keywords
|
||||||
|
const GO_TEMPLATE_KEYWORDS = [
|
||||||
|
'if',
|
||||||
|
'else',
|
||||||
|
'end',
|
||||||
|
'with',
|
||||||
|
'range',
|
||||||
|
'template',
|
||||||
|
'define',
|
||||||
|
'block',
|
||||||
|
'and',
|
||||||
|
'or',
|
||||||
|
'not',
|
||||||
|
'eq',
|
||||||
|
'ne',
|
||||||
|
'lt',
|
||||||
|
'le',
|
||||||
|
'gt',
|
||||||
|
'ge',
|
||||||
|
'print',
|
||||||
|
'printf',
|
||||||
|
'println',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sprig function list (commonly used)
|
||||||
|
const SPRIG_FUNCTIONS = [
|
||||||
|
// String functions
|
||||||
|
'trim',
|
||||||
|
'trimAll',
|
||||||
|
'trimSuffix',
|
||||||
|
'trimPrefix',
|
||||||
|
'upper',
|
||||||
|
'lower',
|
||||||
|
'title',
|
||||||
|
'untitle',
|
||||||
|
'repeat',
|
||||||
|
'substr',
|
||||||
|
'nospace',
|
||||||
|
'trunc',
|
||||||
|
'abbrev',
|
||||||
|
'abbrevboth',
|
||||||
|
'initials',
|
||||||
|
'randAlphaNum',
|
||||||
|
'randAlpha',
|
||||||
|
'randNumeric',
|
||||||
|
'wrap',
|
||||||
|
'wrapWith',
|
||||||
|
'contains',
|
||||||
|
'hasPrefix',
|
||||||
|
'hasSuffix',
|
||||||
|
'quote',
|
||||||
|
'squote',
|
||||||
|
'cat',
|
||||||
|
'indent',
|
||||||
|
'nindent',
|
||||||
|
'replace',
|
||||||
|
'plural',
|
||||||
|
'sha1sum',
|
||||||
|
'sha256sum',
|
||||||
|
'adler32sum',
|
||||||
|
'htmlEscape',
|
||||||
|
'htmlUnescape',
|
||||||
|
'urlEscape',
|
||||||
|
'urlUnescape',
|
||||||
|
|
||||||
|
// Math functions
|
||||||
|
'add',
|
||||||
|
'sub',
|
||||||
|
'mul',
|
||||||
|
'div',
|
||||||
|
'mod',
|
||||||
|
'max',
|
||||||
|
'min',
|
||||||
|
'ceil',
|
||||||
|
'floor',
|
||||||
|
'round',
|
||||||
|
|
||||||
|
// Date functions
|
||||||
|
'now',
|
||||||
|
'date',
|
||||||
|
'dateInZone',
|
||||||
|
'duration',
|
||||||
|
'durationRound',
|
||||||
|
'unixEpoch',
|
||||||
|
'dateModify',
|
||||||
|
'mustDateModify',
|
||||||
|
'htmlDate',
|
||||||
|
'htmlDateInZone',
|
||||||
|
'toDate',
|
||||||
|
'mustToDate',
|
||||||
|
|
||||||
|
// Lists and Dict functions
|
||||||
|
'list',
|
||||||
|
'first',
|
||||||
|
'rest',
|
||||||
|
'last',
|
||||||
|
'initial',
|
||||||
|
'reverse',
|
||||||
|
'uniq',
|
||||||
|
'compact',
|
||||||
|
'slice',
|
||||||
|
'has',
|
||||||
|
'set',
|
||||||
|
'unset',
|
||||||
|
'hasKey',
|
||||||
|
'pluck',
|
||||||
|
'keys',
|
||||||
|
'pick',
|
||||||
|
'omit',
|
||||||
|
'merge',
|
||||||
|
'mergeOverwrite',
|
||||||
|
'values',
|
||||||
|
'append',
|
||||||
|
'prepend',
|
||||||
|
'concat',
|
||||||
|
'dict',
|
||||||
|
'get',
|
||||||
|
'index',
|
||||||
|
'dig',
|
||||||
|
|
||||||
|
// Type functions
|
||||||
|
'typeOf',
|
||||||
|
'typeIs',
|
||||||
|
'typeIsLike',
|
||||||
|
'kindOf',
|
||||||
|
'kindIs',
|
||||||
|
'deepEqual',
|
||||||
|
|
||||||
|
// Default functions
|
||||||
|
'default',
|
||||||
|
'empty',
|
||||||
|
'coalesce',
|
||||||
|
'fromJson',
|
||||||
|
'toJson',
|
||||||
|
'toPrettyJson',
|
||||||
|
'toRawJson',
|
||||||
|
'mustFromJson',
|
||||||
|
'mustToJson',
|
||||||
|
'mustToPrettyJson',
|
||||||
|
|
||||||
|
// Encoding functions
|
||||||
|
'b64enc',
|
||||||
|
'b64dec',
|
||||||
|
'b32enc',
|
||||||
|
'b32dec',
|
||||||
|
|
||||||
|
// Flow control
|
||||||
|
'fail',
|
||||||
|
'required',
|
||||||
|
'tpl',
|
||||||
|
|
||||||
|
// UUID functions
|
||||||
|
'uuidv4',
|
||||||
|
|
||||||
|
// OS functions
|
||||||
|
'env',
|
||||||
|
'expandenv',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function GoTemplateEditor({ schema, enableSprig = true, ...props }: GoTemplateEditorProps) {
|
||||||
|
const completionItems = useMemo(() => {
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
// Add Go template keywords
|
||||||
|
items.push(
|
||||||
|
...GO_TEMPLATE_KEYWORDS.map((keyword) => ({
|
||||||
|
label: keyword,
|
||||||
|
kind: 14,
|
||||||
|
insertText: keyword,
|
||||||
|
documentation: `Go template keyword: ${keyword}`,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add Sprig functions if enabled
|
||||||
|
if (enableSprig) {
|
||||||
|
items.push(
|
||||||
|
...SPRIG_FUNCTIONS.map((func) => ({
|
||||||
|
label: func,
|
||||||
|
kind: 3, // Function
|
||||||
|
insertText: `${func} `,
|
||||||
|
documentation: `Sprig function: ${func}`,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add schema field completion
|
||||||
|
if (schema && typeof schema === 'object') {
|
||||||
|
const addSchemaFields = (schemaObj: Record<string, unknown>, prefix = '.') => {
|
||||||
|
// Handle JSON Schema properties
|
||||||
|
if (schemaObj.properties && typeof schemaObj.properties === 'object') {
|
||||||
|
const properties = schemaObj.properties as Record<string, unknown>;
|
||||||
|
Object.keys(properties).forEach((key) => {
|
||||||
|
const property = properties[key] as Record<string, unknown>;
|
||||||
|
const fullPath = prefix === '.' ? `.${key}` : `${prefix}.${key}`;
|
||||||
|
const type = property.type || 'unknown';
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
label: fullPath,
|
||||||
|
kind: 10, // Property
|
||||||
|
insertText: fullPath,
|
||||||
|
documentation: `Schema field: ${fullPath} (${type})${property.description ? ` - ${property.description}` : ''}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Recursively add nested object properties
|
||||||
|
if (property.type === 'object' && property.properties) {
|
||||||
|
addSchemaFields(property, fullPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Handle direct object structure (non-JSON Schema format)
|
||||||
|
else {
|
||||||
|
Object.keys(schemaObj).forEach((key) => {
|
||||||
|
if (key === 'properties' || key === 'type' || key === 'description') return;
|
||||||
|
|
||||||
|
const value = schemaObj[key];
|
||||||
|
const fullPath = prefix === '.' ? `.${key}` : `${prefix}.${key}`;
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
label: fullPath,
|
||||||
|
kind: 10, // Property
|
||||||
|
insertText: fullPath,
|
||||||
|
documentation: `Schema field: ${fullPath} (${typeof value})`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||||
|
addSchemaFields(value as Record<string, unknown>, fullPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addSchemaFields(schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}, [schema, enableSprig]);
|
||||||
|
|
||||||
|
const handleEditorMount = (editor: unknown, monaco: Monaco) => {
|
||||||
|
// Register custom Go template language
|
||||||
|
monaco.languages.register({ id: 'gotemplate' });
|
||||||
|
|
||||||
|
// Set syntax highlighting
|
||||||
|
monaco.languages.setMonarchTokensProvider('gotemplate', {
|
||||||
|
tokenizer: {
|
||||||
|
root: [
|
||||||
|
// Comments - match {{/*...*/}} first
|
||||||
|
[/\{\{\/\*[\s\S]*?\*\/\}\}/, 'comment'],
|
||||||
|
// Template tags - enter template state
|
||||||
|
[/\{\{-?/, { token: 'template-tag', next: '@template' }],
|
||||||
|
// Regular text
|
||||||
|
[/[^{]+/, ''],
|
||||||
|
[/[{]/, ''],
|
||||||
|
],
|
||||||
|
template: [
|
||||||
|
// Exit template
|
||||||
|
[/-?\}\}/, { token: 'template-tag', next: '@pop' }],
|
||||||
|
// Strings in template
|
||||||
|
[/"([^"\\]|\\.)*"/, 'string'],
|
||||||
|
[/'([^'\\]|\\.)*'/, 'string'],
|
||||||
|
// Go template keywords - exact word match
|
||||||
|
[new RegExp(`\\b(${GO_TEMPLATE_KEYWORDS.join('|')})\\b`), 'keyword'],
|
||||||
|
// Sprig functions - exact word match
|
||||||
|
[new RegExp(`\\b(${SPRIG_FUNCTIONS.join('|')})\\b`), 'function'],
|
||||||
|
// Variables starting with $
|
||||||
|
[/\$\w+/, 'variable'],
|
||||||
|
// Properties starting with .
|
||||||
|
[/\.\w+/, 'property'],
|
||||||
|
// Numbers
|
||||||
|
[/\d+(\.\d+)?/, 'number'],
|
||||||
|
// Operators and other tokens
|
||||||
|
[/[|:]/, 'operator'],
|
||||||
|
// Whitespace
|
||||||
|
[/\s+/, ''],
|
||||||
|
// Any other characters in template
|
||||||
|
[/./, ''],
|
||||||
|
],
|
||||||
|
string: [
|
||||||
|
[/[^\\"]+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/"/, 'string', '@pop'],
|
||||||
|
],
|
||||||
|
string_single: [
|
||||||
|
[/[^\\']+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/'/, 'string', '@pop'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Override the Dracula theme with Go template colors
|
||||||
|
monaco.editor.defineTheme('transparentTheme', {
|
||||||
|
base: DraculaTheme.base as 'vs' | 'vs-dark' | 'hc-black',
|
||||||
|
inherit: DraculaTheme.inherit,
|
||||||
|
rules: [
|
||||||
|
...DraculaTheme.rules,
|
||||||
|
{ token: 'template-tag', foreground: 'FFB86C', fontStyle: 'bold' }, // Dracula orange for template tags
|
||||||
|
{ token: 'keyword', foreground: 'FF79C6' }, // Dracula pink for keywords
|
||||||
|
{ token: 'function', foreground: '50FA7B' }, // Dracula green for functions
|
||||||
|
{ token: 'variable', foreground: 'F1FA8C' }, // Dracula yellow for variables
|
||||||
|
{ token: 'property', foreground: '8BE9FD' }, // Dracula cyan for properties
|
||||||
|
{ token: 'operator', foreground: 'FF79C6' }, // Dracula pink for operators
|
||||||
|
],
|
||||||
|
colors: {
|
||||||
|
'editor.background': '#00000000',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Force theme refresh
|
||||||
|
const editorInstance = editor as { updateOptions?: (options: unknown) => void };
|
||||||
|
if (editorInstance && editorInstance.updateOptions) {
|
||||||
|
editorInstance.updateOptions({ theme: 'transparentTheme' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register auto-completion
|
||||||
|
monaco.languages.registerCompletionItemProvider('gotemplate', {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
provideCompletionItems: (model: any, position: any) => {
|
||||||
|
const range = {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
startColumn: position.column,
|
||||||
|
endColumn: position.column,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
suggestions: completionItems.map((item) => ({
|
||||||
|
...item,
|
||||||
|
range,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register hover provider
|
||||||
|
monaco.languages.registerHoverProvider('gotemplate', {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
provideHover: (model: any, position: any) => {
|
||||||
|
const word = model.getWordAtPosition(position);
|
||||||
|
if (word) {
|
||||||
|
const wordText = word.word;
|
||||||
|
|
||||||
|
if (GO_TEMPLATE_KEYWORDS.includes(wordText)) {
|
||||||
|
return {
|
||||||
|
range: new monaco.Range(
|
||||||
|
position.lineNumber,
|
||||||
|
word.startColumn,
|
||||||
|
position.lineNumber,
|
||||||
|
word.endColumn,
|
||||||
|
),
|
||||||
|
contents: [{ value: `**Go Template Keyword**: ${wordText}` }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SPRIG_FUNCTIONS.includes(wordText)) {
|
||||||
|
return {
|
||||||
|
range: new monaco.Range(
|
||||||
|
position.lineNumber,
|
||||||
|
word.startColumn,
|
||||||
|
position.lineNumber,
|
||||||
|
word.endColumn,
|
||||||
|
),
|
||||||
|
contents: [{ value: `**Sprig Function**: ${wordText}` }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (props.onMount) {
|
||||||
|
props.onMount(editor as Parameters<NonNullable<typeof props.onMount>>[0], monaco);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MonacoEditor
|
||||||
|
title='Go Template Editor'
|
||||||
|
description={`Go text/template syntax${enableSprig ? ' with Sprig functions' : ''}`}
|
||||||
|
{...props}
|
||||||
|
language='gotemplate'
|
||||||
|
placeholder='Enter your Go template here...'
|
||||||
|
onMount={handleEditorMount}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
export { GoTemplateEditor } from '@workspace/ui/custom-components/editor/go-template';
|
||||||
export { HTMLEditor } from '@workspace/ui/custom-components/editor/html';
|
export { HTMLEditor } from '@workspace/ui/custom-components/editor/html';
|
||||||
export { JSONEditor } from '@workspace/ui/custom-components/editor/json';
|
export { JSONEditor } from '@workspace/ui/custom-components/editor/json';
|
||||||
export { MarkdownEditor } from '@workspace/ui/custom-components/editor/markdown';
|
export { MarkdownEditor } from '@workspace/ui/custom-components/editor/markdown';
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { Button } from '@workspace/ui/components/button';
|
|||||||
import { cn } from '@workspace/ui/lib/utils';
|
import { cn } from '@workspace/ui/lib/utils';
|
||||||
import { useSize } from 'ahooks';
|
import { useSize } from 'ahooks';
|
||||||
import { EyeIcon, EyeOff, FullscreenIcon, MinimizeIcon } from 'lucide-react';
|
import { EyeIcon, EyeOff, FullscreenIcon, MinimizeIcon } from 'lucide-react';
|
||||||
|
import DraculaTheme from 'monaco-themes/themes/Dracula.json' with { type: 'json' };
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export interface MonacoEditorProps {
|
export interface MonacoEditorProps {
|
||||||
@ -145,10 +146,11 @@ export function MonacoEditor({
|
|||||||
theme='transparentTheme'
|
theme='transparentTheme'
|
||||||
beforeMount={(monaco: Monaco) => {
|
beforeMount={(monaco: Monaco) => {
|
||||||
monaco.editor.defineTheme('transparentTheme', {
|
monaco.editor.defineTheme('transparentTheme', {
|
||||||
base: 'vs-dark',
|
base: DraculaTheme.base as 'vs' | 'vs-dark' | 'hc-black',
|
||||||
inherit: true,
|
inherit: DraculaTheme.inherit,
|
||||||
rules: [],
|
rules: DraculaTheme.rules,
|
||||||
colors: {
|
colors: {
|
||||||
|
...DraculaTheme.colors,
|
||||||
'editor.background': '#00000000',
|
'editor.background': '#00000000',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user