From 2d669fa4b71d2e641c3c064ac772ba806abf990b Mon Sep 17 00:00:00 2001 From: isultanov99 Date: Sat, 15 Aug 2026 17:38:29 +0200 Subject: [PATCH] feat(sub): add template variables to subscription metadata (#6163) --- .../components/form/RemarkTemplateField.tsx | 77 ++++++---- .../src/components/form/RemarkVarPicker.tsx | 8 +- frontend/src/lib/remark/remarkVariables.ts | 19 ++- .../pages/settings/SubscriptionGeneralTab.tsx | 31 +++- .../src/test/remark-template-field.test.tsx | 29 ++++ internal/sub/controller.go | 46 +++--- internal/sub/placeholders.go | 118 ++++++++++++++++ internal/sub/placeholders_test.go | 132 ++++++++++++++++++ internal/sub/service.go | 1 + internal/web/translation/ar-EG.json | 8 +- internal/web/translation/en-US.json | 8 +- internal/web/translation/es-ES.json | 8 +- internal/web/translation/fa-IR.json | 8 +- internal/web/translation/id-ID.json | 8 +- internal/web/translation/ja-JP.json | 8 +- internal/web/translation/pt-BR.json | 8 +- internal/web/translation/ru-RU.json | 8 +- internal/web/translation/tr-TR.json | 8 +- internal/web/translation/uk-UA.json | 8 +- internal/web/translation/vi-VN.json | 8 +- internal/web/translation/zh-CN.json | 8 +- internal/web/translation/zh-TW.json | 8 +- 22 files changed, 457 insertions(+), 108 deletions(-) create mode 100644 internal/sub/placeholders.go create mode 100644 internal/sub/placeholders_test.go diff --git a/frontend/src/components/form/RemarkTemplateField.tsx b/frontend/src/components/form/RemarkTemplateField.tsx index 1b9c3fe4c..731eed711 100644 --- a/frontend/src/components/form/RemarkTemplateField.tsx +++ b/frontend/src/components/form/RemarkTemplateField.tsx @@ -1,10 +1,11 @@ import { useRef } from 'react'; import { Button, Input, Popover, Tooltip } from 'antd'; import type { InputRef } from 'antd'; +import type { TextAreaRef } from 'antd/es/input/TextArea'; import { CodeOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; -import { hasRemarkTokens, previewRemark, wrapToken } from '@/lib/remark/remarkVariables'; +import { hasRemarkTokens, previewRemark, SUBSCRIPTION_METADATA_VARIABLES, wrapToken } from '@/lib/remark/remarkVariables'; import RemarkVarPicker from './RemarkVarPicker'; interface RemarkTemplateFieldProps { @@ -13,19 +14,31 @@ interface RemarkTemplateFieldProps { onChange?: (value: string) => void; maxLength?: number; placeholder?: string; + multiline?: boolean; + rows?: number; + metadataOnly?: boolean; } /** * RemarkTemplateField is a text input augmented with a {{VAR}} template picker * (insert-at-caret) and a live, sample-based preview of the expanded result. - * Used for the global subscription Remark Template. + * Used for subscription text fields that support Remark Template variables. */ -export default function RemarkTemplateField({ value = '', onChange, maxLength, placeholder }: RemarkTemplateFieldProps) { +export default function RemarkTemplateField({ value = '', onChange, maxLength, placeholder, multiline = false, rows, metadataOnly = false }: RemarkTemplateFieldProps) { const { t } = useTranslation(); const inputRef = useRef(null); + const textAreaRef = useRef(null); + const variables = metadataOnly ? SUBSCRIPTION_METADATA_VARIABLES : undefined; + + function getTextElement() { + if (multiline) { + return textAreaRef.current?.resizableTextArea?.textArea ?? null; + } + return inputRef.current?.input ?? null; + } function insertToken(token: string) { - const el = inputRef.current?.input; + const el = getTextElement(); const start = el?.selectionStart ?? value.length; const end = el?.selectionEnd ?? value.length; const insert = wrapToken(token); @@ -39,31 +52,47 @@ export default function RemarkTemplateField({ value = '', onChange, maxLength, p }); } + const pickerButton = ( + } + trigger="click" + placement="bottomRight" + title={t('pages.hosts.remarkVars.title')} + > + +