mirror of https://github.com/langgenius/dify.git
296 lines
11 KiB
TypeScript
296 lines
11 KiB
TypeScript
import type { IconifyJSON } from '@iconify/types'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { getIconCollections, iconsPlugin } from '@egoist/tailwindcss-icons'
|
|
import { cleanupSVG, deOptimisePaths, importDirectorySync, isEmptyColor, parseColors, runSVGO } from '@iconify/tools'
|
|
import { compareColors, stringToColor } from '@iconify/utils/lib/colors'
|
|
import tailwindTypography from '@tailwindcss/typography'
|
|
// @ts-expect-error workaround for turbopack issue
|
|
import tailwindThemeVarDefine from './themes/tailwind-theme-var-define.ts'
|
|
import typography from './typography.js'
|
|
|
|
const _dirname = typeof __dirname !== 'undefined'
|
|
? __dirname
|
|
: path.dirname(fileURLToPath(import.meta.url))
|
|
|
|
// https://iconify.design/docs/articles/cleaning-up-icons/
|
|
function getIconSetFromDir(dir: string, prefix: string) {
|
|
// Import icons
|
|
const iconSet = importDirectorySync(dir, {
|
|
prefix,
|
|
ignoreImportErrors: 'warn',
|
|
})
|
|
|
|
// Validate, clean up, fix palette and optimise
|
|
iconSet.forEachSync((name, type) => {
|
|
if (type !== 'icon')
|
|
return
|
|
|
|
const svg = iconSet.toSVG(name)
|
|
if (!svg) {
|
|
// Invalid icon
|
|
iconSet.remove(name)
|
|
return
|
|
}
|
|
|
|
// Clean up and optimise icons
|
|
try {
|
|
// Clean up icon code
|
|
cleanupSVG(svg)
|
|
|
|
// Change color to `currentColor`
|
|
// Skip this step if icon has hardcoded palette
|
|
const blackColor = stringToColor('black')!
|
|
const whiteColor = stringToColor('white')!
|
|
parseColors(svg, {
|
|
defaultColor: 'currentColor',
|
|
callback: (attr, colorStr, color) => {
|
|
if (!color) {
|
|
// Color cannot be parsed!
|
|
throw new Error(`Invalid color: "${colorStr}" in attribute ${attr}`)
|
|
}
|
|
|
|
if (isEmptyColor(color)) {
|
|
// Color is empty: 'none' or 'transparent'. Return as is
|
|
return color
|
|
}
|
|
|
|
// Change black to 'currentColor'
|
|
if (compareColors(color, blackColor))
|
|
return 'currentColor'
|
|
|
|
// Remove shapes with white color
|
|
if (compareColors(color, whiteColor))
|
|
return 'remove'
|
|
|
|
// Icon is not monotone
|
|
return color
|
|
},
|
|
})
|
|
|
|
// Optimise
|
|
runSVGO(svg)
|
|
|
|
// Update paths for compatibility with old software
|
|
deOptimisePaths(svg)
|
|
}
|
|
catch (err) {
|
|
// Invalid icon
|
|
console.error(`Error parsing ${name}:`, err)
|
|
iconSet.remove(name)
|
|
return
|
|
}
|
|
|
|
// Update icon
|
|
iconSet.fromSVG(name, svg)
|
|
})
|
|
|
|
// Export
|
|
return iconSet.export()
|
|
}
|
|
|
|
function getCollectionsFromSubDirs(baseDir: string, prefixBase: string): Record<string, IconifyJSON> {
|
|
const collections: Record<string, IconifyJSON> = {}
|
|
|
|
function processDir(dir: string, prefix: string): void {
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
const subDirs = entries.filter(e => e.isDirectory())
|
|
const svgFiles = entries.filter(e => e.isFile() && e.name.endsWith('.svg'))
|
|
|
|
// Process SVG files in current directory if any
|
|
if (svgFiles.length > 0) {
|
|
collections[prefix] = getIconSetFromDir(dir, prefix)
|
|
}
|
|
|
|
// Recurse into subdirectories if any
|
|
if (subDirs.length > 0) {
|
|
for (const subDir of subDirs) {
|
|
const subDirPath = path.join(dir, subDir.name)
|
|
const subPrefix = `${prefix}-${subDir.name}`
|
|
processDir(subDirPath, subPrefix)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read top-level subdirectories and process each
|
|
const entries = fs.readdirSync(baseDir, { withFileTypes: true })
|
|
for (const entry of entries) {
|
|
if (entry.isDirectory()) {
|
|
const subDirPath = path.join(baseDir, entry.name)
|
|
const prefix = `${prefixBase}-${entry.name}`
|
|
processDir(subDirPath, prefix)
|
|
}
|
|
}
|
|
|
|
return collections
|
|
}
|
|
|
|
const config = {
|
|
theme: {
|
|
typography,
|
|
extend: {
|
|
colors: {
|
|
gray: {
|
|
25: '#fcfcfd',
|
|
50: '#f9fafb',
|
|
100: '#f2f4f7',
|
|
200: '#eaecf0',
|
|
300: '#d0d5dd',
|
|
400: '#98a2b3',
|
|
500: '#667085',
|
|
700: '#475467',
|
|
600: '#344054',
|
|
800: '#1d2939',
|
|
900: '#101828',
|
|
},
|
|
primary: {
|
|
25: '#f5f8ff',
|
|
50: '#eff4ff',
|
|
100: '#d1e0ff',
|
|
200: '#b2ccff',
|
|
300: '#84adff',
|
|
400: '#528bff',
|
|
500: '#2970ff',
|
|
600: '#155eef',
|
|
700: '#004eeb',
|
|
800: '#0040c1',
|
|
900: '#00359e',
|
|
},
|
|
blue: {
|
|
500: '#E1EFFE',
|
|
},
|
|
green: {
|
|
50: '#F3FAF7',
|
|
100: '#DEF7EC',
|
|
800: '#03543F',
|
|
|
|
},
|
|
yellow: {
|
|
100: '#FDF6B2',
|
|
800: '#723B13',
|
|
},
|
|
purple: {
|
|
50: '#F6F5FF',
|
|
200: '#DCD7FE',
|
|
},
|
|
indigo: {
|
|
25: '#F5F8FF',
|
|
50: '#EEF4FF',
|
|
100: '#E0EAFF',
|
|
300: '#A4BCFD',
|
|
400: '#8098F9',
|
|
600: '#444CE7',
|
|
800: '#2D31A6',
|
|
},
|
|
...tailwindThemeVarDefine,
|
|
},
|
|
screens: {
|
|
'mobile': '100px',
|
|
// => @media (min-width: 100px) { ... }
|
|
'tablet': '640px', // 391
|
|
// => @media (min-width: 600px) { ... }
|
|
'pc': '769px',
|
|
// => @media (min-width: 769px) { ... }
|
|
'2k': '2560px',
|
|
},
|
|
boxShadow: {
|
|
'xs': '0px 1px 2px 0px rgba(16, 24, 40, 0.05)',
|
|
'sm': '0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.10)',
|
|
'sm-no-bottom': '0px -1px 2px 0px rgba(16, 24, 40, 0.06), 0px -1px 3px 0px rgba(16, 24, 40, 0.10)',
|
|
'md': '0px 2px 4px -2px rgba(16, 24, 40, 0.06), 0px 4px 8px -2px rgba(16, 24, 40, 0.10)',
|
|
'lg': '0px 4px 6px -2px rgba(16, 24, 40, 0.03), 0px 12px 16px -4px rgba(16, 24, 40, 0.08)',
|
|
'xl': '0px 8px 8px -4px rgba(16, 24, 40, 0.03), 0px 20px 24px -4px rgba(16, 24, 40, 0.08)',
|
|
'2xl': '0px 24px 48px -12px rgba(16, 24, 40, 0.18)',
|
|
'3xl': '0px 32px 64px -12px rgba(16, 24, 40, 0.14)',
|
|
'status-indicator-green-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-success-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
|
|
'status-indicator-warning-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-warning-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
|
|
'status-indicator-red-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-error-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
|
|
'status-indicator-blue-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-normal-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
|
|
'status-indicator-gray-shadow': '0px 1px 2px 0px var(--color-components-badge-status-light-disabled-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
|
|
},
|
|
opacity: {
|
|
2: '0.02',
|
|
8: '0.08',
|
|
},
|
|
fontFamily: {
|
|
instrument: ['var(--font-instrument-serif)', 'serif'],
|
|
},
|
|
fontSize: {
|
|
'2xs': '0.625rem',
|
|
},
|
|
backgroundColor: {
|
|
'background-gradient-bg-fill-chat-bubble-bg-3': 'var(--color-background-gradient-bg-fill-chat-bubble-bg-3)',
|
|
},
|
|
backgroundImage: {
|
|
'chatbot-bg': 'var(--color-chatbot-bg)',
|
|
'chat-bubble-bg': 'var(--color-chat-bubble-bg)',
|
|
'chat-input-mask': 'var(--color-chat-input-mask)',
|
|
'workflow-process-bg': 'var(--color-workflow-process-bg)',
|
|
'workflow-process-paused-bg': 'var(--color-workflow-process-paused-bg)',
|
|
'workflow-run-failed-bg': 'var(--color-workflow-run-failed-bg)',
|
|
'workflow-batch-failed-bg': 'var(--color-workflow-batch-failed-bg)',
|
|
'mask-top2bottom-gray-50-to-transparent': 'var(--mask-top2bottom-gray-50-to-transparent)',
|
|
'marketplace-divider-bg': 'var(--color-marketplace-divider-bg)',
|
|
'marketplace-plugin-empty': 'var(--color-marketplace-plugin-empty)',
|
|
'toast-success-bg': 'var(--color-toast-success-bg)',
|
|
'toast-warning-bg': 'var(--color-toast-warning-bg)',
|
|
'toast-error-bg': 'var(--color-toast-error-bg)',
|
|
'toast-info-bg': 'var(--color-toast-info-bg)',
|
|
'app-detail-bg': 'var(--color-app-detail-bg)',
|
|
'app-detail-overlay-bg': 'var(--color-app-detail-overlay-bg)',
|
|
'dataset-chunk-process-success-bg': 'var(--color-dataset-chunk-process-success-bg)',
|
|
'dataset-chunk-process-error-bg': 'var(--color-dataset-chunk-process-error-bg)',
|
|
'dataset-chunk-detail-card-hover-bg': 'var(--color-dataset-chunk-detail-card-hover-bg)',
|
|
'dataset-child-chunk-expand-btn-bg': 'var(--color-dataset-child-chunk-expand-btn-bg)',
|
|
'dataset-option-card-blue-gradient': 'var(--color-dataset-option-card-blue-gradient)',
|
|
'dataset-option-card-purple-gradient': 'var(--color-dataset-option-card-purple-gradient)',
|
|
'dataset-option-card-orange-gradient': 'var(--color-dataset-option-card-orange-gradient)',
|
|
'dataset-chunk-list-mask-bg': 'var(--color-dataset-chunk-list-mask-bg)',
|
|
'line-divider-bg': 'var(--color-line-divider-bg)',
|
|
'dataset-warning-message-bg': 'var(--color-dataset-warning-message-bg)',
|
|
'price-premium-badge-background': 'var(--color-premium-badge-background)',
|
|
'premium-yearly-tip-text-background': 'var(--color-premium-yearly-tip-text-background)',
|
|
'price-premium-text-background': 'var(--color-premium-text-background)',
|
|
'price-enterprise-background': 'var(--color-price-enterprise-background)',
|
|
'grid-mask-background': 'var(--color-grid-mask-background)',
|
|
'node-data-source-bg': 'var(--color-node-data-source-bg)',
|
|
'tag-selector-mask-bg': 'var(--color-tag-selector-mask-bg)',
|
|
'tag-selector-mask-hover-bg': 'var(--color-tag-selector-mask-hover-bg)',
|
|
'pipeline-template-card-hover-bg': 'var(--color-pipeline-template-card-hover-bg)',
|
|
'pipeline-add-documents-title-bg': 'var(--color-pipeline-add-documents-title-bg)',
|
|
'billing-plan-title-bg': 'var(--color-billing-plan-title-bg)',
|
|
'billing-plan-card-premium-bg': 'var(--color-billing-plan-card-premium-bg)',
|
|
'billing-plan-card-enterprise-bg': 'var(--color-billing-plan-card-enterprise-bg)',
|
|
'knowledge-pipeline-creation-footer-bg': 'var(--color-knowledge-pipeline-creation-footer-bg)',
|
|
'progress-bar-indeterminate-stripe': 'var(--color-progress-bar-indeterminate-stripe)',
|
|
'chat-answer-human-input-form-divider-bg': 'var(--color-chat-answer-human-input-form-divider-bg)',
|
|
},
|
|
animation: {
|
|
'spin-slow': 'spin 2s linear infinite',
|
|
},
|
|
},
|
|
},
|
|
plugins: [
|
|
tailwindTypography,
|
|
iconsPlugin({
|
|
collections: {
|
|
...getCollectionsFromSubDirs(path.resolve(_dirname, 'app/components/base/icons/assets/public'), 'custom-public'),
|
|
...getCollectionsFromSubDirs(path.resolve(_dirname, 'app/components/base/icons/assets/vender'), 'custom-vender'),
|
|
...getIconCollections(['heroicons', 'ri']),
|
|
},
|
|
extraProperties: {
|
|
width: '1rem',
|
|
height: '1rem',
|
|
display: 'block',
|
|
},
|
|
}),
|
|
],
|
|
// https://github.com/tailwindlabs/tailwindcss/discussions/5969
|
|
corePlugins: {
|
|
preflight: false,
|
|
},
|
|
}
|
|
|
|
export default config
|