This commit is contained in:
2026-03-12 11:03:47 +08:00
commit ab7e9b597a
21 changed files with 19393 additions and 0 deletions

10
src/main/env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/// <reference types="electron-vite/node" />
interface ImportMetaEnv {
readonly MAIN_VITE_API_URL: string
readonly MAIN_VITE_DEBUG: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

79
src/main/index.ts Normal file
View File

@@ -0,0 +1,79 @@
import { app, shell, BrowserWindow, ipcMain } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
function createWindow(): void {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 900,
minHeight: 600,
show: false,
autoHideMenuBar: true,
titleBarStyle: 'hiddenInset',
trafficLightPosition: { x: 15, y: 10 },
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
contextIsolation: true,
nodeIntegration: false
}
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.kintone')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
// IPC test
ipcMain.on('ping', () => console.log('pong'))
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

11
src/preload/index.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
import { ElectronAPI } from '@electron-toolkit/preload'
declare global {
interface Window {
electron: ElectronAPI
api: {
ping: () => void
platform: NodeJS.Platform
}
}
}

27
src/preload/index.ts Normal file
View File

@@ -0,0 +1,27 @@
import { contextBridge, ipcRenderer } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {
ping: () => ipcRenderer.send('ping'),
// Platform detection
platform: process.platform,
// Store APIs will be added here
}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
} else {
// @ts-ignore (define in dts)
window.electron = electronAPI
// @ts-ignore (define in dts)
window.api = api
}

13
src/renderer/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" />
<title>Kintone Customize Manager</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./src/main.tsx"></script>
</body>
</html>

94
src/renderer/src/App.tsx Normal file
View File

@@ -0,0 +1,94 @@
import { useState } from 'react'
import { Layout, Typography, theme } from 'antd'
import {
GithubOutlined,
SettingOutlined,
CloudServerOutlined,
} from '@ant-design/icons'
const { Header, Content, Sider } = Layout
const { Title, Text } = Typography
function App() {
const [collapsed, setCollapsed] = useState(false)
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken()
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider
collapsible
collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0,
top: 0,
bottom: 0,
}}
>
<div
style={{
height: 32,
margin: 16,
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: borderRadiusLG,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<CloudServerOutlined style={{ fontSize: 20, color: '#fff' }} />
{!collapsed && (
<Text style={{ color: '#fff', marginLeft: 8, fontSize: 14 }}>
Kintone
</Text>
)}
</div>
</Sider>
<Layout style={{ marginLeft: collapsed ? 80 : 200, transition: 'all 0.2s' }}>
<Header
style={{
padding: '0 24px',
background: colorBgContainer,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
borderBottom: '1px solid #f0f0f0',
}}
>
<Title level={4} style={{ margin: 0 }}>
Kintone Customize Manager
</Title>
<div style={{ display: 'flex', gap: 16 }}>
<SettingOutlined style={{ fontSize: 18, cursor: 'pointer' }} />
<GithubOutlined style={{ fontSize: 18, cursor: 'pointer' }} />
</div>
</Header>
<Content style={{ margin: '24px 16px 0', overflow: 'initial' }}>
<div
style={{
padding: 24,
textAlign: 'center',
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
<Title level={3}>使 Kintone Customize Manager</Title>
<Text type="secondary">
Kintone JavaScriptCSSPlugin
</Text>
<div style={{ marginTop: 24 }}>
<Text> Domain 使</Text>
</div>
</div>
</Content>
</Layout>
</Layout>
)
}
export default App

10
src/renderer/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly MAIN_VITE_API_URL: string
readonly MAIN_VITE_DEBUG: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

View File

@@ -0,0 +1,50 @@
html,
body,
#root {
margin: 0;
padding: 0;
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
}
/* macOS style window controls area */
.titlebar {
-webkit-app-region: drag;
user-select: none;
}
.titlebar button {
-webkit-app-region: no-drag;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #d9d9d9;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #bfbfbf;
}
/* Dark mode scrollbar */
@media (prefers-color-scheme: dark) {
::-webkit-scrollbar-thumb {
background: #424242;
}
::-webkit-scrollbar-thumb:hover {
background: #5a5a5a;
}
}

27
src/renderer/src/main.tsx Normal file
View File

@@ -0,0 +1,27 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { ConfigProvider, App as AntdApp } from 'antd'
import zhCN from 'antd/locale/zh_CN'
import { ThemeProvider } from '@lobehub/ui'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ConfigProvider
locale={zhCN}
theme={{
token: {
colorPrimary: '#1677ff',
borderRadius: 6,
},
}}
>
<ThemeProvider>
<AntdApp>
<App />
</AntdApp>
</ThemeProvider>
</ConfigProvider>
</React.StrictMode>,
)