Files
gochat/frontend/tailwind.config.js
T
rogee 1535e6cd55 Decouple frontend from Rails, establish pnpm workspace dev workflow
Strip the Chatwoot frontend down to a standalone Vite + Vue 3 SPA that talks
directly to the GoChat Go backend — no Ruby/Rails required.

Deleted from frontend/ (all Rails-only):
- Ruby: Gemfile, Gemfile.lock, config.ru, Capfile, Rakefile, bin/ (binstubs),
  config/ (application/boot/environment/routes/initializers/environments/locales),
  app/helpers/ (*.rb), app/views/ (ERB/jbuilder), app/assets/ (administrate scss)
- Enterprise overlay (enterprise/ — pure Ruby/jbuilder, zero JS)
- Infra: Procfile*, docker-compose*, docker/, deployment/, swagger/, .circleci/,
  .devcontainer/, .qlty/, .codegraph/, .windsurf/, .github/, .vscode/, .husky/
- Lint/config: .rubocop.yml, .scss-lint.yml, .rspec, rubocop/, .bundler-audit.yml,
  .annotaterb.yml, .all-contributorsrc, crowdin.yml, histoire.config.ts, etc.
- Stale frontend/pnpm-lock.yaml (replaced by root workspace lockfile)

Kept (frontend build essentials):
- app/javascript/ (all Vue SPA source — dashboard/widget/sdk/portal/superadmin/survey/v3)
- package.json, vite.config.ts, tailwind.config.js, postcss.config.js
- theme/, vitest.setup.js, .prettierrc

Key decoupling changes:
- vite.config.ts: removed vite-plugin-ruby, added explicit rollupOptions.input
  for all 7 entrypoints + dev-server proxy (/api,/platform,/cable,/health → GoChat)
- index.html: created static SPA entry that injects window.chatwootConfig /
  window.globalConfig (previously done by Rails ERB vueapp.html.erb)
- package.json: renamed @gochat/frontend, removed vite-plugin-ruby/histoire/husky,
  scripts now use plain vite dev/build
- tailwind.config.js: removed enterprise ERB content paths
- markdownEmbeds.js: relocated markdown_embeds.yml into app/javascript/dashboard/config/

Root pnpm workspace:
- package.json: dev:backend (air), dev:frontend (vite), dev (concurrently)
- pnpm-workspace.yaml: frontend as workspace package
- .gitignore: node_modules/, frontend/dist/, frontend build artifacts

Verified: pnpm install + vite build succeeds (4446 modules, all 7 entrypoints
produce JS+CSS chunks in dist/).
2026-07-07 15:37:24 +08:00

277 lines
7.5 KiB
JavaScript

const { slateDark } = require('@radix-ui/colors');
import { colors } from './theme/colors';
import { icons } from './theme/icons';
const defaultTheme = require('tailwindcss/defaultTheme');
const {
iconsPlugin,
getIconCollections,
} = require('@egoist/tailwindcss-icons');
const defaultSansFonts = [
'-apple-system',
'system-ui',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Tahoma',
'Arial',
'sans-serif !important',
];
const tailwindConfig = {
darkMode: 'class',
content: [
'./app/javascript/widget/**/*.vue',
'./app/javascript/v3/**/*.vue',
'./app/javascript/dashboard/**/*.vue',
'./app/javascript/portal/**/*.vue',
'./app/javascript/shared/**/*.vue',
'./app/javascript/survey/**/*.vue',
'./app/javascript/dashboard/components-next/**/*.vue',
'./app/javascript/dashboard/helper/**/*.js',
'./app/javascript/dashboard/composables/**/*.js',
'./app/javascript/dashboard/components-next/**/*.js',
'./app/javascript/dashboard/routes/dashboard/**/**/*.js',
'./app/javascript/**/*.html',
],
theme: {
extend: {
fontFamily: {
sans: defaultSansFonts,
inter: ['Inter', ...defaultSansFonts],
interDisplay: ['InterDisplay', ...defaultSansFonts],
},
fontWeight: {
420: '420',
440: '440',
460: '460',
520: '520',
620: '620',
},
typography: {
bubble: {
css: {
color: 'rgb(var(--slate-12))',
lineHeight: '1.6',
fontSize: '14px',
'*': {
'&:first-child': {
marginTop: '0',
},
},
overflowWrap: 'anywhere',
strong: {
color: 'rgb(var(--slate-12))',
fontWeight: '700',
},
b: {
color: 'rgb(var(--slate-12))',
fontWeight: '700',
},
h1: {
color: 'rgb(var(--slate-12))',
fontWeight: '700',
fontSize: '1.25rem',
'&:first-child': {
marginTop: '0',
},
},
h2: {
color: 'rgb(var(--slate-12))',
fontWeight: '700',
fontSize: '1rem',
'&:first-child': {
marginTop: '0',
},
},
h3: {
color: 'rgb(var(--slate-12))',
fontWeight: '700',
fontSize: '1rem',
'&:first-child': {
marginTop: '0',
},
},
hr: {
marginTop: '1.5em',
marginBottom: '1.5em',
},
a: {
color: 'rgb(var(--slate-12))',
textDecoration: 'underline',
},
ul: {
paddingInlineStart: '0.625em',
},
ol: {
paddingInlineStart: '0.625em',
},
'ul li': {
margin: '0 0 0.5em 1em',
listStyleType: 'disc',
'[dir="rtl"] &': {
margin: '0 1em 0.5em 0',
},
},
'ol li': {
margin: '0 0 0.5em 1em',
listStyleType: 'decimal',
'[dir="rtl"] &': {
margin: '0 1em 0.5em 0',
},
},
blockquote: {
color: 'rgb(var(--slate-11))',
borderLeft: `4px solid rgb(var(--black-alpha-1))`,
paddingLeft: '1em',
'[dir="rtl"] &': {
borderLeft: 'none',
paddingLeft: '0',
borderRight: `4px solid rgb(var(--black-alpha-1))`,
paddingRight: '1em',
},
'[dir="ltr"] &': {
borderRight: 'none',
paddingRight: '0',
},
},
code: {
backgroundColor: 'rgb(var(--alpha-3))',
color: 'rgb(var(--slate-11))',
padding: '0.2em 0.4em',
borderRadius: '4px',
fontSize: '0.95em',
'&::before': {
content: `none`,
},
'&::after': {
content: `none`,
},
},
pre: {
backgroundColor: 'rgb(var(--alpha-3))',
padding: '1em',
borderRadius: '6px',
overflowX: 'auto',
},
table: {
width: '100%',
borderCollapse: 'collapse',
},
th: {
padding: '0.75em',
color: 'rgb(var(--slate-12))',
border: `none`,
textAlign: 'start',
fontWeight: '600',
},
tr: {
border: `none`,
},
td: {
padding: '0.75em',
border: `none`,
},
img: {
maxWidth: '100%',
height: 'auto',
marginTop: 'unset',
marginBottom: 'unset',
},
},
},
},
},
screens: {
xs: '480px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
'3xl': '1900px',
},
fontSize: {
...defaultTheme.fontSize,
xxs: '0.625rem',
},
colors: {
transparent: 'transparent',
white: '#fff',
'modal-backdrop-light': 'rgba(0, 0, 0, 0.4)',
'modal-backdrop-dark': 'rgba(0, 0, 0, 0.6)',
current: 'currentColor',
...colors,
body: slateDark.slate7,
},
keyframes: {
...defaultTheme.keyframes,
wiggle: {
'0%': { transform: 'translateX(0)' },
'15%': { transform: 'translateX(0.375rem)' },
'30%': { transform: 'translateX(-0.375rem)' },
'45%': { transform: 'translateX(0.375rem)' },
'60%': { transform: 'translateX(-0.375rem)' },
'75%': { transform: 'translateX(0.375rem)' },
'90%': { transform: 'translateX(-0.375rem)' },
'100%': { transform: 'translateX(0)' },
},
'fade-in-up': {
'0%': { opacity: 0, transform: 'translateY(0.5rem)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
},
'loader-pulse': {
'0%': { opacity: 0.4 },
'50%': { opacity: 1 },
'100%': { opacity: 0.4 },
},
'card-select': {
'0%, 100%': {
transform: 'translateX(0)',
},
'50%': {
transform: 'translateX(1px)',
},
},
shake: {
'0%, 100%': { transform: 'translateX(0)' },
'25%': { transform: 'translateX(0.234375rem)' },
'50%': { transform: 'translateX(-0.234375rem)' },
'75%': { transform: 'translateX(0.234375rem)' },
},
},
animation: {
...defaultTheme.animation,
wiggle: 'wiggle 0.5s ease-in-out',
'fade-in-up': 'fade-in-up 0.3s ease-out',
'loader-pulse': 'loader-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'card-select': 'card-select 0.25s ease-in-out',
shake: 'shake 0.3s ease-in-out 0s 2',
},
},
plugins: [
// eslint-disable-next-line
require('@tailwindcss/typography'),
iconsPlugin({
collections: {
woot: { icons },
...getIconCollections([
'lucide',
'logos',
'ri',
'ph',
'material-symbols',
'teenyicons',
'fluent',
]),
},
}),
],
};
module.exports = tailwindConfig;