diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 405ce77400..943d38878e 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -4270,11 +4270,6 @@ "count": 1 } }, - "web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx": { "no-restricted-imports": { "count": 1 @@ -4293,16 +4288,6 @@ "count": 1 } }, - "web/app/components/workflow/nodes/_base/components/form-input-type-switch.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "web/app/components/workflow/nodes/_base/components/help-link.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx": { "no-restricted-imports": { "count": 1 @@ -4502,22 +4487,6 @@ "count": 1 } }, - "web/app/components/workflow/nodes/agent/components/model-bar.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "ts/no-empty-object-type": { - "count": 1 - } - }, - "web/app/components/workflow/nodes/agent/components/tool-icon.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react/unsupported-syntax": { - "count": 1 - } - }, "web/app/components/workflow/nodes/agent/default.ts": { "ts/no-explicit-any": { "count": 3 @@ -4859,11 +4828,6 @@ "count": 1 } }, - "web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts": { "ts/no-explicit-any": { "count": 2 @@ -4966,14 +4930,6 @@ "count": 1 } }, - "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "ts/no-explicit-any": { - "count": 4 - } - }, "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx": { "no-restricted-imports": { "count": 1 @@ -5009,11 +4965,6 @@ "count": 2 } }, - "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx": { "react/set-state-in-effect": { "count": 1 @@ -5235,11 +5186,6 @@ "count": 5 } }, - "web/app/components/workflow/nodes/tool/components/copy-id.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/nodes/tool/components/input-var-list.tsx": { "ts/no-explicit-any": { "count": 7 @@ -5405,11 +5351,6 @@ "count": 1 } }, - "web/app/components/workflow/note-node/note-editor/toolbar/command.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/note-node/note-editor/utils.ts": { "regexp/no-useless-quantifier": { "count": 1 diff --git a/web/app/components/base/infotip/index.tsx b/web/app/components/base/infotip/index.tsx index b97b499af3..ce818fe030 100644 --- a/web/app/components/base/infotip/index.tsx +++ b/web/app/components/base/infotip/index.tsx @@ -73,7 +73,7 @@ export function Infotip({ /> {children} diff --git a/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx b/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx index 7786dbec17..2e95473bb2 100644 --- a/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx @@ -225,7 +225,7 @@ describe('FormInputItem branches', () => { }) expect(screen.getByText('alpha')).toBeInTheDocument() - fireEvent.click(screen.getByRole('button')) + fireEvent.click(screen.getByText('alpha').closest('button') as HTMLButtonElement) fireEvent.click(screen.getByText('beta')) expect(onChange).toHaveBeenCalledWith({ @@ -320,9 +320,9 @@ describe('FormInputItem branches', () => { }) await waitFor(() => { - expect(screen.getByRole('button')).not.toBeDisabled() + expect(screen.getByText('Select options').closest('button')).not.toBeDisabled() }) - fireEvent.click(screen.getByRole('button')) + fireEvent.click(screen.getByText('Select options').closest('button') as HTMLButtonElement) fireEvent.click(screen.getByText('trigger-option')) expect(onChange).toHaveBeenCalledWith({ diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx index 10a167c504..a00e8e1adc 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx @@ -5,7 +5,7 @@ import type { } from '@/app/components/workflow/types' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' +import { Infotip } from '@/app/components/base/infotip' import Collapse from '../collapse' import DefaultValue from './default-value' import ErrorHandleTypeSelector from './error-handle-type-selector' @@ -57,7 +57,9 @@ const ErrorHandle = ({
{t('nodes.common.errorHandle.title', { ns: 'workflow' })}
- + + {t('nodes.common.errorHandle.tip', { ns: 'workflow' })} + {collapseIcon} = ({ onChange, }) => { const { t } = useTranslation() + const variableLabel = t('nodes.common.typeSwitch.variable', { ns: 'workflow' }) + const inputLabel = t('nodes.common.typeSwitch.input', { ns: 'workflow' }) + return (
- -
onChange(VarType.variable)} - > - -
-
- -
onChange(VarType.constant)} - > - -
-
+ {value === VarType.variable + ? ( + + ) + : ( + + onChange(VarType.variable)} + > + + + )} + /> + {variableLabel} + + )} + {value === VarType.constant + ? ( + + ) + : ( + + onChange(VarType.constant)} + > + + + )} + /> + {inputLabel} + + )}
) } diff --git a/web/app/components/workflow/nodes/_base/components/help-link.tsx b/web/app/components/workflow/nodes/_base/components/help-link.tsx index 30f95a12be..298f50738f 100644 --- a/web/app/components/workflow/nodes/_base/components/help-link.tsx +++ b/web/app/components/workflow/nodes/_base/components/help-link.tsx @@ -1,8 +1,7 @@ import type { BlockEnum } from '@/app/components/workflow/types' -import { RiBookOpenLine } from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import TooltipPlus from '@/app/components/base/tooltip' import { useNodeHelpLink } from '../hooks/use-node-help-link' type HelpLinkProps = { @@ -17,19 +16,25 @@ const HelpLink = ({ if (!link) return null - return ( - - - - - + const label = t('userProfile.helpCenter', { ns: 'common' }) + return ( + + + + + )} + /> + {label} + ) } diff --git a/web/app/components/workflow/nodes/agent/components/__tests__/model-bar.spec.tsx b/web/app/components/workflow/nodes/agent/components/__tests__/model-bar.spec.tsx index d85f54ed19..2127b48dca 100644 --- a/web/app/components/workflow/nodes/agent/components/__tests__/model-bar.spec.tsx +++ b/web/app/components/workflow/nodes/agent/components/__tests__/model-bar.spec.tsx @@ -1,5 +1,5 @@ import type { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { fireEvent, render, screen } from '@testing-library/react' +import { render, screen } from '@testing-library/react' import { ModelBar } from '../model-bar' type ModelProviderItem = { @@ -52,11 +52,9 @@ describe('agent/model-bar', () => { const emptySelector = screen.getByText((_, element) => element?.textContent === 'no-model:0') - fireEvent.mouseEnter(emptySelector) - expect(emptySelector).toBeInTheDocument() expect(screen.getByText('indicator:red')).toBeInTheDocument() - expect(screen.getByText('workflow.nodes.agent.modelNotSelected')).toBeInTheDocument() + expect(screen.getByLabelText('workflow.nodes.agent.modelNotSelected')).toBeInTheDocument() }) it('should render the selected model without warning when it is installed', () => { @@ -69,10 +67,8 @@ describe('agent/model-bar', () => { it('should show a warning tooltip when the selected model is not installed', () => { render() - fireEvent.mouseEnter(screen.getByText('openai/gpt-4.1:1')) - expect(screen.getByText('openai/gpt-4.1:1')).toBeInTheDocument() expect(screen.getByText('indicator:red')).toBeInTheDocument() - expect(screen.getByText('workflow.nodes.agent.modelNotInstallTooltip')).toBeInTheDocument() + expect(screen.getByLabelText('workflow.nodes.agent.modelNotInstallTooltip')).toBeInTheDocument() }) }) diff --git a/web/app/components/workflow/nodes/agent/components/__tests__/tool-icon.spec.tsx b/web/app/components/workflow/nodes/agent/components/__tests__/tool-icon.spec.tsx index 30a12bb528..af61b43367 100644 --- a/web/app/components/workflow/nodes/agent/components/__tests__/tool-icon.spec.tsx +++ b/web/app/components/workflow/nodes/agent/components/__tests__/tool-icon.spec.tsx @@ -87,19 +87,17 @@ describe('agent/tool-icon', () => { const { rerender } = render() - fireEvent.mouseEnter(screen.getByText('app-icon:#fff:B')) expect(screen.getByText('indicator:yellow')).toBeInTheDocument() - expect(screen.getByText('workflow.nodes.agent.toolNotAuthorizedTooltip:{"tool":"tool-b"}')).toBeInTheDocument() + expect(screen.getByLabelText('workflow.nodes.agent.toolNotAuthorizedTooltip:{"tool":"tool-b"}')).toBeInTheDocument() mockWorkflowTools = [] mockMarketplaceIcon = 'https://example.com/market-tool.png' rerender() const marketplaceIcon = screen.getByRole('img', { name: 'tool icon' }) - fireEvent.mouseEnter(marketplaceIcon) expect(marketplaceIcon).toHaveAttribute('src', 'https://example.com/market-tool.png') expect(screen.getByText('indicator:red')).toBeInTheDocument() - expect(screen.getByText('workflow.nodes.agent.toolNotInstallTooltip:{"tool":"tool-c"}')).toBeInTheDocument() + expect(screen.getByLabelText('workflow.nodes.agent.toolNotInstallTooltip:{"tool":"tool-c"}')).toBeInTheDocument() }) it('should fall back to the group icon while tool data is still loading', () => { diff --git a/web/app/components/workflow/nodes/agent/components/model-bar.tsx b/web/app/components/workflow/nodes/agent/components/model-bar.tsx index 8e2f19d726..0ec0b943ef 100644 --- a/web/app/components/workflow/nodes/agent/components/model-bar.tsx +++ b/web/app/components/workflow/nodes/agent/components/model-bar.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' @@ -10,7 +10,10 @@ import Indicator from '@/app/components/header/indicator' type ModelBarProps = { provider: string model: string -} | {} +} | { + provider?: never + model?: never +} const useAllModel = () => { const { data: textGeneration } = useModelList(ModelTypeEnum.textGeneration) @@ -35,23 +38,27 @@ const useAllModel = () => { export const ModelBar: FC = (props) => { const { t } = useTranslation() const modelList = useAllModel() - if (!('provider' in props)) { + if (props.provider === undefined) { + const tooltip = t('nodes.agent.modelNotSelected', { ns: 'workflow' }) + return ( - -
- - -
+ + + + + + )} + /> + {tooltip} ) } @@ -59,23 +66,34 @@ export const ModelBar: FC = (props) => { provider => provider.provider === props.provider && provider.models.some(model => model.model === props.model), ) const showWarn = modelList && !modelInstalled - return modelList && ( - -
- - {showWarn && } -
+ if (!modelList) + return null + + const modelNotInstalledTooltip = t('nodes.agent.modelNotInstallTooltip', { ns: 'workflow' }) + const modelSelector = ( +
+ + {showWarn && } +
+ ) + + if (modelInstalled) + return modelSelector + + return ( + + + {modelNotInstalledTooltip} ) } diff --git a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx index b545c2f370..3986dcf6a4 100644 --- a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx +++ b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx @@ -1,9 +1,10 @@ +import type { ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { memo, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import { Group } from '@/app/components/base/icons/src/vender/other' -import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools } from '@/service/use-tools' import { getIconFromMarketPlace } from '@/utils/get-icon' @@ -62,44 +63,50 @@ export const ToolIcon = memo(({ providerName }: ToolIconProps) => { throw new Error('Unknown status') }, [name, notSuccess, status, t]) const [iconFetchError, setIconFetchError] = useState(false) - return ( - + + if (!iconFetchError && icon) { + if (typeof icon === 'string') { + iconContent = ( + tool icon setIconFetchError(true)} + /> + ) + } + else if (typeof icon === 'object') { + iconContent = ( + + ) + } + } + + const iconNode = ( +
-
-
- {(() => { - if (iconFetchError || !icon) - return - if (typeof icon === 'string') { - return ( - tool icon setIconFetchError(true)} - /> - ) - } - if (typeof icon === 'object') { - return ( - - ) - } - return - })()} -
- {indicator && } +
+ {iconContent}
+ {indicator && } +
+ ) + + if (!notSuccess || !tooltip) + return iconNode + + return ( + + + {tooltip} ) }) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx index 814b3cea6d..b81032242f 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx @@ -9,7 +9,7 @@ import { import { Switch } from '@langgenius/dify-ui/switch' import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' +import { Infotip } from '@/app/components/base/infotip' import { env } from '@/env' export type TopKAndScoreThresholdProps = { @@ -59,10 +59,13 @@ const TopKAndScoreThreshold = ({
{t('datasetConfig.top_k', { ns: 'appDebug' })} - + + {t('datasetConfig.top_kTip', { ns: 'appDebug' })} +
{t('datasetConfig.score_threshold', { ns: 'appDebug' })}
- + + {t('datasetConfig.score_thresholdTip', { ns: 'appDebug' })} +
+type EditorOnMount = NonNullable['onMount']> +type MonacoEditor = Parameters[0] +type Monaco = Parameters[1] + const CodeEditor: FC = ({ value, onUpdate, @@ -36,8 +39,8 @@ const CodeEditor: FC = ({ }) => { const { t } = useTranslation() const { theme } = useTheme() - const monacoRef = useRef(null) - const editorRef = useRef(null) + const monacoRef = useRef(null) + const editorRef = useRef(null) const [isMounted, setIsMounted] = React.useState(false) const containerRef = useRef(null) @@ -50,7 +53,7 @@ const CodeEditor: FC = ({ } }, [theme]) - const handleEditorDidMount = useCallback((editor: any, monaco: any) => { + const handleEditorDidMount = useCallback((editor, monaco) => { editorRef.current = editor monacoRef.current = monaco @@ -83,7 +86,7 @@ const CodeEditor: FC = ({ }) monaco.editor.setTheme('light-theme') setIsMounted(true) - }, []) + }, [onBlur, onFocus]) const formatJsonContent = useCallback(() => { if (editorRef.current) @@ -122,24 +125,36 @@ const CodeEditor: FC = ({
{showFormatButton && ( - - + + + + + )} + /> + {t('operation.format', { ns: 'common' })} )} - - + + copy(value)} + > + + + )} + /> + {t('operation.copy', { ns: 'common' })}
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx index 0afedab3d2..2f3b70aefc 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx @@ -1,8 +1,7 @@ import type { FC } from 'react' -import { RiAddCircleLine, RiDeleteBinLine, RiEditLine } from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import * as React from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' type ActionsProps = { disableAddBtn: boolean @@ -18,36 +17,59 @@ const Actions: FC = ({ onDelete, }) => { const { t } = useTranslation() + const addChildFieldLabel = t('nodes.llm.jsonSchema.addChildField', { ns: 'workflow' }) + const editLabel = t('operation.edit', { ns: 'common' }) + const removeLabel = t('operation.remove', { ns: 'common' }) return (
- - + + + + + )} + /> + {addChildFieldLabel} - - + + + + + )} + /> + {editLabel} - - + + + + + )} + /> + {removeLabel}
) diff --git a/web/app/components/workflow/nodes/tool/components/__tests__/copy-id.spec.tsx b/web/app/components/workflow/nodes/tool/components/__tests__/copy-id.spec.tsx index ee6791ca03..2fb7e66e24 100644 --- a/web/app/components/workflow/nodes/tool/components/__tests__/copy-id.spec.tsx +++ b/web/app/components/workflow/nodes/tool/components/__tests__/copy-id.spec.tsx @@ -20,27 +20,21 @@ describe('tool/copy-id', () => { it('should copy content and reset copied state when mouse leaves', () => { const { container } = render() - const trigger = screen.getByText('tool-123').parentElement as HTMLElement + const trigger = screen.getByRole('button', { name: 'appOverview.overview.appInfo.embedded.copy' }) const wrapper = container.querySelector('.inline-flex') as HTMLElement - act(() => { - fireEvent.mouseEnter(trigger) - }) - expect(screen.getByText('appOverview.overview.appInfo.embedded.copy')).toBeInTheDocument() - act(() => { fireEvent.click(trigger) vi.advanceTimersByTime(100) }) expect(copy).toHaveBeenCalledWith('tool-123') - expect(screen.getByText('appOverview.overview.appInfo.embedded.copied')).toBeInTheDocument() + expect(trigger).toHaveAccessibleName('appOverview.overview.appInfo.embedded.copied') act(() => { fireEvent.mouseLeave(wrapper) vi.advanceTimersByTime(100) - fireEvent.mouseEnter(trigger) }) - expect(screen.getByText('appOverview.overview.appInfo.embedded.copy')).toBeInTheDocument() + expect(trigger).toHaveAccessibleName('appOverview.overview.appInfo.embedded.copy') }) it('should stop click propagation from the outer wrapper', () => { diff --git a/web/app/components/workflow/nodes/tool/components/copy-id.tsx b/web/app/components/workflow/nodes/tool/components/copy-id.tsx index eaf3d1bec5..18a510caaf 100644 --- a/web/app/components/workflow/nodes/tool/components/copy-id.tsx +++ b/web/app/components/workflow/nodes/tool/components/copy-id.tsx @@ -1,11 +1,10 @@ 'use client' -import { RiFileCopyLine } from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import copy from 'copy-to-clipboard' import { debounce } from 'es-toolkit/compat' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' type Props = { content: string @@ -25,27 +24,33 @@ const CopyFeedbackNew = ({ content }: Props) => { const onMouseLeave = debounce(() => { setIsCopied(false) }, 100) + const tooltip = (isCopied + ? t(`${prefixEmbedded}.copied`, { ns: 'appOverview' }) + : t(`${prefixEmbedded}.copy`, { ns: 'appOverview' })) || '' return (
e.stopPropagation()} onMouseLeave={onMouseLeave}> - -
-
- {content} -
- -
+ + + + {content} + + + + )} + /> + + {tooltip} +
) diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx index ab4bf8c7bb..9f9c02de33 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx @@ -1,17 +1,10 @@ import { cn } from '@langgenius/dify-ui/cn' -import { - RiBold, - RiItalic, - RiLink, - RiListUnordered, - RiStrikethrough, -} from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { memo, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' import { useStore } from '../store' import { useCommand } from './hooks' @@ -32,15 +25,15 @@ const Command = ({ const icon = useMemo(() => { switch (type) { case 'bold': - return + return case 'italic': - return + return case 'strikethrough': - return + return case 'link': - return + return case 'bullet': - return + return } }, [type, selectedIsBold, selectedIsItalic, selectedIsStrikeThrough, selectedIsLink, selectedIsBullet]) @@ -60,22 +53,27 @@ const Command = ({ }, [type, t]) return ( - -
+ handleCommand(type)} + > + {icon} + )} - onClick={() => handleCommand(type)} - > - {icon} -
+ /> + {tip}
) } diff --git a/web/docs/overlay-migration.md b/web/docs/overlay-migration.md index 73c0f02d9d..cb020f9ab6 100644 --- a/web/docs/overlay-migration.md +++ b/web/docs/overlay-migration.md @@ -44,12 +44,6 @@ This document tracks the Dify-web migration away from legacy overlay APIs. ## Allowlist maintenance -- After each migration batch, run: - -```sh -pnpm -C web lint:fix --prune-suppressions -``` - - If a migrated file was in the allowlist, remove it from `web/eslint.constants.mjs` in the same PR. - Never increase allowlist scope to bypass new code.