mirror of
https://github.com/langgenius/dify.git
synced 2026-09-24 23:22:26 +08:00
feat(web): gate /create and /refine slash commands behind feature preview flag (#37094)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a88c15c906
commit
db1aa683bc
@@ -84,6 +84,10 @@ NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL=true
|
||||
# Default is false for security reasons to prevent conflicts with regular text
|
||||
NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=false
|
||||
|
||||
# Enable preview features still in development (currently the /create and
|
||||
# /refine slash commands in the "Go to Anything" command palette)
|
||||
NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW=false
|
||||
|
||||
# The maximum number of tree node depth for workflow
|
||||
NEXT_PUBLIC_MAX_TREE_DEPTH=50
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const {
|
||||
mockRegister,
|
||||
mockSearch,
|
||||
mockUnregister,
|
||||
featureFlag,
|
||||
} = vi.hoisted(() => ({
|
||||
mockSetTheme: vi.fn(),
|
||||
mockSetLocale: vi.fn(),
|
||||
@@ -16,6 +17,14 @@ const {
|
||||
mockRegister: vi.fn(),
|
||||
mockSearch: vi.fn(),
|
||||
mockUnregister: vi.fn(),
|
||||
// Mutable holder so each test can flip the feature-preview flag before render.
|
||||
featureFlag: { enabled: false },
|
||||
}))
|
||||
|
||||
vi.mock('@/config', () => ({
|
||||
get ENABLE_FEATURE_PREVIEW() {
|
||||
return featureFlag.enabled
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('next-themes', () => ({
|
||||
@@ -92,9 +101,47 @@ describe('slashAction', () => {
|
||||
describe('SlashCommandProvider', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
// Default: feature preview off, so /create and /refine are NOT registered.
|
||||
featureFlag.enabled = false
|
||||
})
|
||||
|
||||
it('should register commands on mount and unregister them on unmount', () => {
|
||||
it('should not register the /create and /refine preview commands when the feature flag is off', () => {
|
||||
const { unmount } = render(<SlashCommandProvider />)
|
||||
|
||||
expect(mockRegister.mock.calls.map(call => call[0].name)).toEqual([
|
||||
'theme',
|
||||
'language',
|
||||
'forum',
|
||||
'docs',
|
||||
'community',
|
||||
'account',
|
||||
'zen',
|
||||
'go',
|
||||
])
|
||||
expect(mockRegister).toHaveBeenCalledWith(expect.objectContaining({ name: 'theme' }), { setTheme: mockSetTheme })
|
||||
expect(mockRegister).toHaveBeenCalledWith(expect.objectContaining({ name: 'language' }), { setLocale: mockSetLocale })
|
||||
|
||||
unmount()
|
||||
|
||||
// Unregister is always called for the preview commands (a no-op when they
|
||||
// were never registered) so toggling the flag off mid-session stays clean.
|
||||
expect(mockUnregister.mock.calls.map(call => call[0])).toEqual([
|
||||
'theme',
|
||||
'language',
|
||||
'forum',
|
||||
'docs',
|
||||
'community',
|
||||
'account',
|
||||
'zen',
|
||||
'go',
|
||||
'create',
|
||||
'refine',
|
||||
])
|
||||
})
|
||||
|
||||
it('should register the /create and /refine preview commands when the feature flag is on', () => {
|
||||
featureFlag.enabled = true
|
||||
|
||||
const { unmount } = render(<SlashCommandProvider />)
|
||||
|
||||
expect(mockRegister.mock.calls.map(call => call[0].name)).toEqual([
|
||||
@@ -109,8 +156,6 @@ describe('SlashCommandProvider', () => {
|
||||
'create',
|
||||
'refine',
|
||||
])
|
||||
expect(mockRegister).toHaveBeenCalledWith(expect.objectContaining({ name: 'theme' }), { setTheme: mockSetTheme })
|
||||
expect(mockRegister).toHaveBeenCalledWith(expect.objectContaining({ name: 'language' }), { setLocale: mockSetLocale })
|
||||
|
||||
unmount()
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ActionItem } from '../types'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useEffect } from 'react'
|
||||
import { getI18n } from 'react-i18next'
|
||||
import { ENABLE_FEATURE_PREVIEW } from '@/config'
|
||||
import { setLocaleOnClient } from '@/i18n-config'
|
||||
import { accountCommand } from './account'
|
||||
import { executeCommand } from './command-bus'
|
||||
@@ -52,8 +53,11 @@ const registerSlashCommands = (deps: Record<string, any>) => {
|
||||
slashCommandRegistry.register(accountCommand, {})
|
||||
slashCommandRegistry.register(zenCommand, {})
|
||||
slashCommandRegistry.register(goCommand, {})
|
||||
slashCommandRegistry.register(createCommand, {})
|
||||
slashCommandRegistry.register(refineCommand, {})
|
||||
// `/create` and `/refine` are preview features, gated behind a flag.
|
||||
if (ENABLE_FEATURE_PREVIEW) {
|
||||
slashCommandRegistry.register(createCommand, {})
|
||||
slashCommandRegistry.register(refineCommand, {})
|
||||
}
|
||||
}
|
||||
|
||||
const unregisterSlashCommands = () => {
|
||||
@@ -66,6 +70,7 @@ const unregisterSlashCommands = () => {
|
||||
slashCommandRegistry.unregister('account')
|
||||
slashCommandRegistry.unregister('zen')
|
||||
slashCommandRegistry.unregister('go')
|
||||
// No-op when the preview flag is off and these were never registered.
|
||||
slashCommandRegistry.unregister('create')
|
||||
slashCommandRegistry.unregister('refine')
|
||||
}
|
||||
|
||||
@@ -314,6 +314,7 @@ export const ENABLE_WEBSITE_JINAREADER = env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREAD
|
||||
export const ENABLE_WEBSITE_FIRECRAWL = env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL
|
||||
export const ENABLE_WEBSITE_WATERCRAWL = env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL
|
||||
export const ENABLE_SINGLE_DOLLAR_LATEX = env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX
|
||||
export const ENABLE_FEATURE_PREVIEW = env.NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW
|
||||
|
||||
export const VALUE_SELECTOR_DELIMITER = '@@@'
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ export NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER=${ENABLE_WEBSITE_JINAREADER:-true}
|
||||
export NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL=${ENABLE_WEBSITE_FIRECRAWL:-true}
|
||||
export NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL=${ENABLE_WEBSITE_WATERCRAWL:-true}
|
||||
export NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=${NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX:-false}
|
||||
export NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW=${NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW:-false}
|
||||
export NEXT_PUBLIC_LOOP_NODE_MAX_COUNT=${LOOP_NODE_MAX_COUNT}
|
||||
export NEXT_PUBLIC_MAX_PARALLEL_LIMIT=${MAX_PARALLEL_LIMIT}
|
||||
export NEXT_PUBLIC_MAX_ITERATIONS_NUM=${MAX_ITERATIONS_NUM}
|
||||
|
||||
@@ -63,6 +63,12 @@ const clientSchema = {
|
||||
* The deployment edition, SELF_HOSTED
|
||||
*/
|
||||
NEXT_PUBLIC_EDITION: z.enum(['SELF_HOSTED', 'CLOUD']).default('SELF_HOSTED'),
|
||||
/**
|
||||
* Enable preview features that are still in development.
|
||||
* Currently gates the `/create` and `/refine` slash commands in the
|
||||
* "Go to Anything" command palette (Cmd/Ctrl+K).
|
||||
*/
|
||||
NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW: coercedBoolean.default(false),
|
||||
|
||||
/**
|
||||
* Cloud-only system-features defaults.
|
||||
@@ -190,6 +196,7 @@ export const env = createEnv({
|
||||
NEXT_PUBLIC_DEPLOY_ENV: isServer ? process.env.NEXT_PUBLIC_DEPLOY_ENV : getRuntimeEnvFromBody('deployEnv'),
|
||||
NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON: isServer ? process.env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON : getRuntimeEnvFromBody('disableUploadImageAsIcon'),
|
||||
NEXT_PUBLIC_EDITION: isServer ? process.env.NEXT_PUBLIC_EDITION : getRuntimeEnvFromBody('edition'),
|
||||
NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW: isServer ? process.env.NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW : getRuntimeEnvFromBody('enableFeaturePreview'),
|
||||
|
||||
/**
|
||||
* Cloud-only system-features defaults.
|
||||
|
||||
Reference in New Issue
Block a user