mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
feat: 迁移到pnpm-workspace
This commit is contained in:
1 parent
f181c70402
commit
0f9f21db83
49 files changed
+220
-322
No files matched your search
+13
-1
@@ -7,8 +7,12 @@ export default defineConfig(() => {
|
||||
return {
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@openchatlab': resolve('packages'),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
// 使用系统环境变量
|
||||
'process.env.APTABASE_APP_KEY': JSON.stringify(process.env.APTABASE_APP_KEY || ''),
|
||||
},
|
||||
build: {
|
||||
@@ -22,6 +26,11 @@ export default defineConfig(() => {
|
||||
},
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@openchatlab': resolve('packages'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
@@ -38,6 +47,9 @@ export default defineConfig(() => {
|
||||
'@openchatlab': resolve('packages'),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
__IS_ELECTRON__: JSON.stringify(true),
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
|
||||
@@ -245,9 +245,9 @@ export class Agent {
|
||||
ctx.api = lastMsg.api
|
||||
}
|
||||
// 从 model.baseUrl 和 api 类型构造完整请求 URL
|
||||
const baseUrl = (this.piModel as Record<string, unknown>).baseUrl as string | undefined
|
||||
const baseUrl = (this.piModel as unknown as Record<string, unknown>).baseUrl as string | undefined
|
||||
if (baseUrl) {
|
||||
const apiType = lastMsg?.api || (this.piModel as Record<string, unknown>).api
|
||||
const apiType = lastMsg?.api || (this.piModel as unknown as Record<string, unknown>).api
|
||||
const pathMap: Record<string, string> = {
|
||||
'openai-completions': '/chat/completions',
|
||||
'openai-responses': '/responses',
|
||||
|
||||
@@ -266,7 +266,7 @@ export function loadConfigStore(): AIConfigStore {
|
||||
}
|
||||
|
||||
// Schema v2 → v3 迁移
|
||||
if (store.schemaVersion < 3) {
|
||||
if ((store.schemaVersion ?? 0) < 3) {
|
||||
store = { ...migrateToSchemaV3(store), schemaVersion: CURRENT_SCHEMA_VERSION } as typeof store
|
||||
needsSchemaSave = true
|
||||
}
|
||||
|
||||
@@ -51,14 +51,14 @@ export function createTool(context: ToolContext): AgentTool<typeof schema> {
|
||||
posFilterMode: 'meaningful',
|
||||
enableStopwords: true,
|
||||
}
|
||||
const freqMap = batchSegmentWithFrequency(texts, segLocale, segOptions)
|
||||
const freqResult = batchSegmentWithFrequency(texts, segLocale, segOptions)
|
||||
|
||||
if (freqMap.size === 0) {
|
||||
if (freqResult.words.size === 0) {
|
||||
const text = isZh ? '分词后没有有意义的高频词' : 'No meaningful high-frequency words found after segmentation'
|
||||
return { content: [{ type: 'text', text }], details: null }
|
||||
}
|
||||
|
||||
const ranking = [...freqMap.entries()].map(([word, count], i) => ({
|
||||
const ranking = [...freqResult.words.entries()].map(([word, count], i) => ({
|
||||
rank: i + 1,
|
||||
word,
|
||||
count,
|
||||
|
||||
@@ -13,6 +13,7 @@ export enum ApiErrorCode {
|
||||
BODY_TOO_LARGE = 'BODY_TOO_LARGE',
|
||||
IMPORT_IN_PROGRESS = 'IMPORT_IN_PROGRESS',
|
||||
IDEMPOTENCY_CONFLICT = 'IDEMPOTENCY_CONFLICT',
|
||||
IDEMPOTENCY_PENDING = 'IDEMPOTENCY_PENDING',
|
||||
IMPORT_FAILED = 'IMPORT_FAILED',
|
||||
SERVER_ERROR = 'SERVER_ERROR',
|
||||
}
|
||||
@@ -28,6 +29,7 @@ const HTTP_STATUS: Record<ApiErrorCode, number> = {
|
||||
[ApiErrorCode.BODY_TOO_LARGE]: 413,
|
||||
[ApiErrorCode.IMPORT_IN_PROGRESS]: 409,
|
||||
[ApiErrorCode.IDEMPOTENCY_CONFLICT]: 409,
|
||||
[ApiErrorCode.IDEMPOTENCY_PENDING]: 409,
|
||||
[ApiErrorCode.IMPORT_FAILED]: 500,
|
||||
[ApiErrorCode.SERVER_ERROR]: 500,
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import { pipeline } from 'stream/promises'
|
||||
import { getTempDir } from '../../paths'
|
||||
import * as worker from '../../worker/workerManager'
|
||||
import {
|
||||
ApiError,
|
||||
ApiErrorCode,
|
||||
successResponse,
|
||||
importInProgress,
|
||||
importFailed,
|
||||
@@ -180,7 +182,7 @@ async function handleUnifiedImport(request: FastifyRequest, reply: FastifyReply,
|
||||
reply.code(err.statusCode).send(errorResponse(err))
|
||||
return
|
||||
}
|
||||
tempFile = writeResult.tempFile
|
||||
tempFile = writeResult.tempFile!
|
||||
|
||||
// Idempotency-Key check (after tempFile is written so we can hash JSONL content)
|
||||
if (cacheKey) {
|
||||
@@ -193,13 +195,11 @@ async function handleUnifiedImport(request: FastifyRequest, reply: FastifyReply,
|
||||
return
|
||||
}
|
||||
if (cached.status === 'pending') {
|
||||
reply.code(409).send(
|
||||
errorResponse({
|
||||
statusCode: 409,
|
||||
code: 'IDEMPOTENCY_PENDING',
|
||||
message: 'A request with this Idempotency-Key is still in progress. Please retry later.',
|
||||
})
|
||||
const pendingErr = new ApiError(
|
||||
ApiErrorCode.IDEMPOTENCY_PENDING,
|
||||
'A request with this Idempotency-Key is still in progress. Please retry later.'
|
||||
)
|
||||
reply.code(pendingErr.statusCode).send(errorResponse(pendingErr))
|
||||
return
|
||||
}
|
||||
reply.send(cached.response)
|
||||
@@ -368,7 +368,7 @@ async function handleLegacyImport(request: FastifyRequest, reply: FastifyReply,
|
||||
reply.code(err.statusCode).send(errorResponse(err))
|
||||
return
|
||||
}
|
||||
tempFile = writeResult.tempFile
|
||||
tempFile = writeResult.tempFile!
|
||||
|
||||
if (sessionId) {
|
||||
const session = await worker.getSession(sessionId)
|
||||
|
||||
@@ -28,13 +28,8 @@ import type {
|
||||
} from '../../../src/types/analysis'
|
||||
import { openDatabase } from './core'
|
||||
|
||||
/**
|
||||
* 时间过滤参数
|
||||
*/
|
||||
export interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
export type { TimeFilter } from '@openchatlab/shared-types'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
/**
|
||||
* 构建时间过滤 WHERE 子句
|
||||
|
||||
@@ -86,11 +86,9 @@ export function getCacheDir(): string {
|
||||
|
||||
// ==================== 时间过滤工具 ====================
|
||||
|
||||
export interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null // 成员筛选,null 表示全部成员
|
||||
}
|
||||
// Re-export from shared-types
|
||||
export type { TimeFilter } from '@openchatlab/shared-types'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
/**
|
||||
* 构建时间过滤 WHERE 子句
|
||||
|
||||
@@ -196,7 +196,6 @@ export async function incrementalImport(
|
||||
id: number
|
||||
platform_id: string
|
||||
}>
|
||||
const initialMemberCount = existingMembers.length
|
||||
for (const m of existingMembers) {
|
||||
memberIdMap.set(m.platform_id, m.id)
|
||||
}
|
||||
|
||||
@@ -269,8 +269,8 @@ export function getLanguagePreferenceAnalysis(params: LanguagePreferenceParams):
|
||||
const b = memberProfiles[1]
|
||||
|
||||
// 共同高频词
|
||||
const wordsA = new Map(a.topWords.map((w: any) => [w.word, w.count]))
|
||||
const wordsB = new Map(b.topWords.map((w: any) => [w.word, w.count]))
|
||||
const wordsA = new Map<string, number>(a.topWords.map((w: any) => [w.word, w.count]))
|
||||
const wordsB = new Map<string, number>(b.topWords.map((w: any) => [w.word, w.count]))
|
||||
const shared: Array<{ word: string; countA: number; countB: number }> = []
|
||||
for (const [word, countA] of wordsA) {
|
||||
const countB = wordsB.get(word)
|
||||
|
||||
Vendored
+1
-5
@@ -20,11 +20,7 @@ import type { FileParseInfo, ConflictCheckResult, MergeParams, MergeResult } fro
|
||||
import type { LanguagePreferenceResult } from '../../src/types/quotes/languagePreference'
|
||||
import type { TableSchema, SQLResult } from '../../src/components/analysis/SQLLab/types'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null // 成员筛选,null 表示全部成员
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
// @ 互动关系图数据
|
||||
interface MentionGraphData {
|
||||
|
||||
@@ -13,15 +13,10 @@ import { CanvasRenderer } from 'echarts/renderers'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import { loadClusterGraph } from './queries'
|
||||
import type { ClusterGraphData, ClusterGraphOptions } from './types'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
echarts.use([HeatmapChart, TooltipComponent, GridComponent, VisualMapComponent, CanvasRenderer])
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -8,12 +8,7 @@
|
||||
*/
|
||||
|
||||
import type { MemberRow, MessageRow, ClusterGraphData, ClusterGraphOptions, BuildClusterInput } from './types'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const DEFAULT_OPTIONS: Required<ClusterGraphOptions> = {
|
||||
lookAhead: 3,
|
||||
|
||||
@@ -8,12 +8,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { EChartGraph } from '@/components/charts'
|
||||
import type { EChartGraphData } from '@/components/charts'
|
||||
import { loadMentionGraph } from './queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
|
||||
@@ -7,12 +7,7 @@
|
||||
*/
|
||||
|
||||
import type { MemberInfo, NameHistory, MentionMessage, MentionGraphData, BuildGraphInput } from './types'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
/**
|
||||
* 构建时间和成员过滤条件
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ThemeCard } from '@/components/UI'
|
||||
import { MessageType, getMessageTypeName } from './types'
|
||||
import type { MessageTypeCount, HourlyActivity, WeekdayActivity, DailyActivity, TextStats } from './types'
|
||||
import { queryLongMessageCount } from './queries'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
echarts.use([PieChart, BarChart, TooltipComponent, GridComponent, CanvasRenderer])
|
||||
@@ -17,12 +18,6 @@ echarts.use([PieChart, BarChart, TooltipComponent, GridComponent, CanvasRenderer
|
||||
const { t } = useI18n()
|
||||
const isDark = useDark()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
sessionName: string
|
||||
|
||||
@@ -25,12 +25,7 @@ import type {
|
||||
TextStats,
|
||||
} from './types'
|
||||
import MessageProfileCard from './MessageProfileCard.vue'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
|
||||
@@ -13,12 +13,7 @@ import type {
|
||||
LengthDistribution,
|
||||
TextStats,
|
||||
} from './types'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
/**
|
||||
* 构建时间和成员过滤条件
|
||||
|
||||
@@ -7,12 +7,7 @@ import { PageAnchorsNav, TopNSelect } from '@/components/UI'
|
||||
import { usePageAnchors } from '@/composables'
|
||||
import type { MemberActivity } from './types'
|
||||
import { ActivityRank, CheckInRank, MemeBattleRank, RepeatSection, DivingRank, NightOwlRank } from './sections'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
|
||||
@@ -14,12 +14,7 @@ import type {
|
||||
MemeBattleAnalysis,
|
||||
RepeatAnalysis,
|
||||
} from './types'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
function buildFilter(filter?: TimeFilter): { conditions: string; params: any[] } {
|
||||
const parts: string[] = []
|
||||
|
||||
@@ -5,11 +5,7 @@ import { EChartRank } from '@/components/charts'
|
||||
import type { RankItem } from '@/components/charts'
|
||||
import { SectionCard, LoadingState, Tabs, TopNSelect } from '@/components/UI'
|
||||
import { queryDragonKingAnalysis, queryCheckInAnalysis } from '../queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -4,11 +4,7 @@ import type { CheckInAnalysis } from '../types'
|
||||
import { EChartStreakRank } from '../charts'
|
||||
import { SectionCard, LoadingState, EmptyState, Tabs, TopNSelect } from '@/components/UI'
|
||||
import { queryCheckInAnalysis } from '../queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -4,11 +4,7 @@ import type { DivingAnalysis } from '../types'
|
||||
import { EChartDivingRank } from '../charts'
|
||||
import { LoadingState } from '@/components/UI'
|
||||
import { queryDivingAnalysis } from '../queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
|
||||
@@ -6,11 +6,7 @@ import type { RankItem } from '@/components/charts'
|
||||
import { LoadingState, Tabs, SectionCard, TopNSelect } from '@/components/UI'
|
||||
import { EChartBattleRank } from '../charts'
|
||||
import { queryMemeBattleAnalysis } from '../queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -5,11 +5,7 @@ import { EChartRank } from '@/components/charts'
|
||||
import { SectionCard, Tabs, TopNSelect, LoadingState } from '@/components/UI'
|
||||
import { EChartConsecutiveRank, EChartNightOwlRank } from '../charts'
|
||||
import { queryNightOwlAnalysis } from '../queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -6,11 +6,7 @@ import type { RankItem, EChartBarData } from '@/components/charts'
|
||||
import { SectionCard, EmptyState, LoadingState, Tabs, TopNSelect } from '@/components/UI'
|
||||
import { EChartTimeRank } from '../charts'
|
||||
import { queryRepeatAnalysis } from '../queries'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @openchatlab/shared-types
|
||||
* 平台无关的共享类型定义,三端(Electron / Node 服务 / Web)统一使用
|
||||
*/
|
||||
|
||||
// ==================== 时间筛选 ====================
|
||||
|
||||
export interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
|
||||
// ==================== 枚举与平台 ====================
|
||||
|
||||
/**
|
||||
* 消息类型枚举
|
||||
*
|
||||
* 分类说明:
|
||||
* - 基础消息 (0-19): 常见的内容类型
|
||||
* - 交互消息 (20-39): 涉及互动的消息类型
|
||||
* - 系统消息 (80-89): 系统相关消息
|
||||
* - 其他 (99): 未知或无法分类的消息
|
||||
*/
|
||||
export enum MessageType {
|
||||
// ========== 基础消息类型 (0-19) ==========
|
||||
TEXT = 0,
|
||||
IMAGE = 1,
|
||||
VOICE = 2,
|
||||
VIDEO = 3,
|
||||
FILE = 4,
|
||||
EMOJI = 5,
|
||||
LINK = 7,
|
||||
LOCATION = 8,
|
||||
|
||||
// ========== 交互消息类型 (20-39) ==========
|
||||
RED_PACKET = 20,
|
||||
TRANSFER = 21,
|
||||
POKE = 22,
|
||||
CALL = 23,
|
||||
SHARE = 24,
|
||||
REPLY = 25,
|
||||
FORWARD = 26,
|
||||
CONTACT = 27,
|
||||
|
||||
// ========== 系统消息类型 (80-89) ==========
|
||||
SYSTEM = 80,
|
||||
RECALL = 81,
|
||||
|
||||
// ========== 其他 (99) ==========
|
||||
OTHER = 99,
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天平台类型(字符串,允许任意值)
|
||||
* 常见平台示例:qq, weixin, discord, whatsapp 等
|
||||
* 合并多平台记录时使用 'mixed'
|
||||
*/
|
||||
export type ChatPlatform = string
|
||||
|
||||
export const KNOWN_PLATFORMS = {
|
||||
QQ: 'qq',
|
||||
WECHAT: 'weixin',
|
||||
DISCORD: 'discord',
|
||||
WHATSAPP: 'whatsapp',
|
||||
TELEGRAM: 'telegram',
|
||||
INSTAGRAM: 'instagram',
|
||||
LINE: 'line',
|
||||
UNKNOWN: 'unknown',
|
||||
} as const
|
||||
|
||||
/**
|
||||
* 聊天类型枚举
|
||||
*/
|
||||
export enum ChatType {
|
||||
GROUP = 'group',
|
||||
PRIVATE = 'private',
|
||||
}
|
||||
|
||||
// ==================== 成员角色 ====================
|
||||
|
||||
export interface MemberRole {
|
||||
id: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export const STANDARD_ROLE_IDS = {
|
||||
OWNER: 'owner',
|
||||
ADMIN: 'admin',
|
||||
} as const
|
||||
|
||||
// ==================== 标准协议(Parser 输出) ====================
|
||||
|
||||
export interface ParsedMember {
|
||||
platformId: string
|
||||
accountName: string
|
||||
groupNickname?: string
|
||||
avatar?: string
|
||||
roles?: MemberRole[]
|
||||
}
|
||||
|
||||
export interface ParsedMessage {
|
||||
platformMessageId?: string
|
||||
senderPlatformId: string
|
||||
senderAccountName: string
|
||||
senderGroupNickname?: string
|
||||
timestamp: number
|
||||
type: MessageType
|
||||
content: string | null
|
||||
replyToMessageId?: string
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@openchatlab/shared-types",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "ChatLab 跨平台共享类型定义",
|
||||
"main": "./index.ts",
|
||||
"types": "./index.ts"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- 'packages/**'
|
||||
@@ -6,15 +6,11 @@ const EChartWordcloud = defineAsyncComponent(() => import('@/components/charts/E
|
||||
import type { EChartWordcloudData } from '@/components/charts'
|
||||
import { LoadingState, EmptyState, UITabs } from '@/components/UI'
|
||||
import { useLayoutStore } from '@/stores/layout'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const layoutStore = useLayoutStore()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -7,14 +7,10 @@ import { ListPro } from '@/components/charts'
|
||||
import { LoadingState, EmptyState, SectionCard } from '@/components/UI'
|
||||
import { formatDate, getRankBadgeClass } from '@/utils'
|
||||
import { useLayoutStore } from '@/stores/layout'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -7,14 +7,10 @@ import type { RankItem } from '@/components/charts'
|
||||
import { LoadingState } from '@/components/UI'
|
||||
import { getRankBadgeClass } from '@/utils'
|
||||
import { usePromptStore } from '@/stores/prompt'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
// 扩展基础模板类型,添加组件内使用的字段
|
||||
interface KeywordTemplate extends BaseKeywordTemplate {
|
||||
description?: string
|
||||
|
||||
@@ -9,15 +9,11 @@ import CatchphrasePKSection from './sections/CatchphrasePKSection.vue'
|
||||
import PosPortraitSection from './sections/PosPortraitSection.vue'
|
||||
import ModalParticleSection from './sections/ModalParticleSection.vue'
|
||||
import PunctuationSection from './sections/PunctuationSection.vue'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const layoutStore = useLayoutStore()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -5,15 +5,11 @@ import { ThemeCard } from '@/components/UI'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
import type { SharedWord } from '@/types/quotes/languagePreference'
|
||||
import type { MemberWithStats } from '@/types/analysis'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
type DictType = 'default' | 'zh-CN' | 'zh-TW'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -4,16 +4,12 @@ import { useI18n } from 'vue-i18n'
|
||||
import dayjs from 'dayjs'
|
||||
import { ThemeCard } from '@/components/UI'
|
||||
import type { WordFrequencyItem, PosTagStat } from './topicProfileTypes'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const EChartWordcloud = defineAsyncComponent(() => import('@/components/charts/EChartWordcloud.vue'))
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
totalMessages: number
|
||||
totalWords: number
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useSettingsStore } from '@/stores/settings'
|
||||
import { useLayoutStore } from '@/stores/layout'
|
||||
import { useWordFilterStore } from '@/stores/wordFilter'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const settingsStore = useSettingsStore()
|
||||
@@ -20,11 +21,6 @@ const layoutStore = useLayoutStore()
|
||||
const wordFilterStore = useWordFilterStore()
|
||||
const toast = useToast()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
interface PosTagInfo {
|
||||
tag: string
|
||||
name: string
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { ref, watch, type Ref, type ComputedRef } from 'vue'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
/**
|
||||
* 异步数据加载 Composable
|
||||
* 统一处理数据加载、状态管理和错误处理
|
||||
*/
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
interface UseAsyncDataOptions<T> {
|
||||
/** 是否立即加载数据(默认 true) */
|
||||
immediate?: boolean
|
||||
|
||||
Vendored
+2
@@ -1,5 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare const __IS_ELECTRON__: boolean | undefined
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
// 使用 Record<string, never> 避免 {} 被 ESLint 判定为过宽类型。
|
||||
|
||||
@@ -3,14 +3,10 @@ import { ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { SubTabs } from '@/components/UI'
|
||||
import { CatchphraseTab, HotRepeatTab } from '@/components/analysis/quotes'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -8,14 +8,10 @@ import RankingView from '@openchatlab/chart-ranking/RankingView.vue'
|
||||
import GroupRelationships from './view/GroupRelationships.vue'
|
||||
import { WordcloudTab, CatchphraseTab, HotRepeatTab } from '@/components/analysis/quotes'
|
||||
import { isFeatureSupported, type LocaleType } from '@/i18n'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
sessionName?: string
|
||||
|
||||
@@ -9,15 +9,10 @@ import { SubTabs } from '@/components/UI'
|
||||
import InteractionView from '@openchatlab/chart-interaction/InteractionView.vue'
|
||||
import Relationships from './Relationships.vue'
|
||||
import ClusterView from '@openchatlab/chart-cluster/ClusterView.vue'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
memberId?: number | null
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -5,14 +5,10 @@ import type { MentionAnalysis, MemberMentionDetail } from '@/types/analysis'
|
||||
import { RankListPro } from '@/components/charts'
|
||||
import type { RankItem } from '@/components/charts'
|
||||
import { SectionCard, EmptyState, LoadingState } from '@/components/UI'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { KeywordAnalysis } from '@/components/analysis/quotes'
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
|
||||
@@ -6,14 +6,10 @@ import UserSelect from '@/components/common/UserSelect.vue'
|
||||
import MessageView from '@openchatlab/chart-message/MessageView.vue'
|
||||
import RelationshipView from './view/RelationshipView.vue'
|
||||
import { WordcloudTab, LanguagePreferenceTab } from '@/components/analysis/quotes'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
sessionName?: string
|
||||
|
||||
@@ -6,14 +6,10 @@ import { ThemeCard, SectionCard, EmptyState, LoadingState } from '@/components/U
|
||||
import { EChart } from '@/components/charts'
|
||||
import RelationshipMetricCard from './RelationshipMetricCard.vue'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { TimeFilter } from '@openchatlab/shared-types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
// Props
|
||||
defineProps<{
|
||||
sessionId: string
|
||||
|
||||
+10
-120
@@ -1,53 +1,23 @@
|
||||
/**
|
||||
* ChatLab 基础类型定义
|
||||
* 包含:枚举、数据库模型、Parser 解析结果
|
||||
*/
|
||||
|
||||
// ==================== 枚举定义 ====================
|
||||
|
||||
/**
|
||||
* 消息类型枚举
|
||||
*
|
||||
* 分类说明:
|
||||
* - 基础消息 (0-19): 常见的内容类型
|
||||
* - 交互消息 (20-39): 涉及互动的消息类型
|
||||
* - 系统消息 (80-89): 系统相关消息
|
||||
* - 其他 (99): 未知或无法分类的消息
|
||||
* 核心类型已迁移到 @openchatlab/shared-types,此处 re-export 保持兼容
|
||||
*/
|
||||
export enum MessageType {
|
||||
// ========== 基础消息类型 (0-19) ==========
|
||||
TEXT = 0, // 文本消息
|
||||
IMAGE = 1, // 图片
|
||||
VOICE = 2, // 语音
|
||||
VIDEO = 3, // 视频
|
||||
FILE = 4, // 文件
|
||||
EMOJI = 5, // 表情包/贴纸
|
||||
LINK = 7, // 链接/卡片(分享的网页、文章等)
|
||||
LOCATION = 8, // 位置/地理位置
|
||||
|
||||
// ========== 交互消息类型 (20-39) ==========
|
||||
RED_PACKET = 20, // 红包
|
||||
TRANSFER = 21, // 转账
|
||||
POKE = 22, // 拍一拍/戳一戳
|
||||
CALL = 23, // 语音/视频通话
|
||||
SHARE = 24, // 分享(音乐、小程序等)
|
||||
REPLY = 25, // 引用回复
|
||||
FORWARD = 26, // 转发消息
|
||||
CONTACT = 27, // 名片消息
|
||||
import { MessageType, ChatType, KNOWN_PLATFORMS, STANDARD_ROLE_IDS } from '@openchatlab/shared-types'
|
||||
|
||||
// ========== 系统消息类型 (80-89) ==========
|
||||
SYSTEM = 80, // 系统消息(入群/退群/群公告等)
|
||||
RECALL = 81, // 撤回消息
|
||||
import type { TimeFilter, ChatPlatform, MemberRole, ParsedMember, ParsedMessage } from '@openchatlab/shared-types'
|
||||
|
||||
// ========== 其他 (99) ==========
|
||||
OTHER = 99, // 其他/未知
|
||||
}
|
||||
// Re-export all shared types for backward compatibility
|
||||
export { MessageType, ChatType, KNOWN_PLATFORMS, STANDARD_ROLE_IDS }
|
||||
|
||||
export type { TimeFilter, ChatPlatform, MemberRole, ParsedMember, ParsedMessage }
|
||||
|
||||
/**
|
||||
* 消息类型 i18n key 映射
|
||||
*/
|
||||
const MESSAGE_TYPE_KEYS: Record<number, string> = {
|
||||
// 基础消息类型
|
||||
[MessageType.TEXT]: 'text',
|
||||
[MessageType.IMAGE]: 'image',
|
||||
[MessageType.VOICE]: 'voice',
|
||||
@@ -56,7 +26,6 @@ const MESSAGE_TYPE_KEYS: Record<number, string> = {
|
||||
[MessageType.EMOJI]: 'emoji',
|
||||
[MessageType.LINK]: 'link',
|
||||
[MessageType.LOCATION]: 'location',
|
||||
// 交互消息类型
|
||||
[MessageType.RED_PACKET]: 'redPacket',
|
||||
[MessageType.TRANSFER]: 'transfer',
|
||||
[MessageType.POKE]: 'poke',
|
||||
@@ -65,10 +34,8 @@ const MESSAGE_TYPE_KEYS: Record<number, string> = {
|
||||
[MessageType.REPLY]: 'reply',
|
||||
[MessageType.FORWARD]: 'forward',
|
||||
[MessageType.CONTACT]: 'contact',
|
||||
// 系统消息类型
|
||||
[MessageType.SYSTEM]: 'system',
|
||||
[MessageType.RECALL]: 'recall',
|
||||
// 其他
|
||||
[MessageType.OTHER]: 'other',
|
||||
}
|
||||
|
||||
@@ -83,58 +50,6 @@ export function getMessageTypeName(type: MessageType | number, t?: (key: string)
|
||||
return t ? t('common.messageType.unknown') : '未知'
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天平台类型(字符串,允许任意值)
|
||||
* 常见平台示例:qq, weixin, discord, whatsapp 等
|
||||
* 合并多平台记录时使用 'mixed'
|
||||
*/
|
||||
export type ChatPlatform = string
|
||||
|
||||
/**
|
||||
* 预定义的常用平台值
|
||||
*/
|
||||
export const KNOWN_PLATFORMS = {
|
||||
QQ: 'qq',
|
||||
WECHAT: 'weixin',
|
||||
DISCORD: 'discord',
|
||||
WHATSAPP: 'whatsapp',
|
||||
TELEGRAM: 'telegram',
|
||||
INSTAGRAM: 'instagram',
|
||||
LINE: 'line',
|
||||
UNKNOWN: 'unknown',
|
||||
} as const
|
||||
|
||||
/**
|
||||
* 聊天类型枚举
|
||||
*/
|
||||
export enum ChatType {
|
||||
GROUP = 'group', // 群聊
|
||||
PRIVATE = 'private', // 私聊
|
||||
}
|
||||
|
||||
// ==================== 成员角色 ====================
|
||||
|
||||
/**
|
||||
* 成员角色
|
||||
* 一个成员可以有多个角色(如 Discord 的多角色系统)
|
||||
*/
|
||||
export interface MemberRole {
|
||||
/** 角色标识:标准角色使用 "owner" | "admin",自定义角色使用任意字符串 */
|
||||
id: string
|
||||
/** 角色显示名称(自定义角色需要,标准角色可省略) */
|
||||
name?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准角色 ID
|
||||
*/
|
||||
export const STANDARD_ROLE_IDS = {
|
||||
/** 群主/创建者 */
|
||||
OWNER: 'owner',
|
||||
/** 管理员 */
|
||||
ADMIN: 'admin',
|
||||
} as const
|
||||
|
||||
// ==================== 数据库模型 ====================
|
||||
|
||||
/**
|
||||
@@ -180,31 +95,6 @@ export interface DbMessage {
|
||||
|
||||
// ==================== Parser 解析结果 ====================
|
||||
|
||||
/**
|
||||
* 解析后的成员信息
|
||||
*/
|
||||
export interface ParsedMember {
|
||||
platformId: string // 平台标识
|
||||
accountName: string // 账号名称(QQ原始昵称 sendNickName)
|
||||
groupNickname?: string // 群昵称(sendMemberName,可为空)
|
||||
avatar?: string // 头像(base64 Data URL,可为空)
|
||||
roles?: MemberRole[] // 成员角色(可为空)
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析后的消息
|
||||
*/
|
||||
export interface ParsedMessage {
|
||||
platformMessageId?: string // 消息的平台原始 ID(用于回复关联查询)
|
||||
senderPlatformId: string // 发送者平台ID
|
||||
senderAccountName: string // 发送时的账号名称
|
||||
senderGroupNickname?: string // 发送时的群昵称(可为空)
|
||||
timestamp: number // 时间戳(秒)
|
||||
type: MessageType // 消息类型
|
||||
content: string | null // 内容
|
||||
replyToMessageId?: string // 回复的目标消息 ID(平台原始 ID,可为空)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser 解析结果
|
||||
*/
|
||||
@@ -213,9 +103,9 @@ export interface ParseResult {
|
||||
name: string
|
||||
platform: ChatPlatform
|
||||
type: ChatType
|
||||
groupId?: string // 群ID(群聊类型有值)
|
||||
groupAvatar?: string // 群头像(base64 Data URL)
|
||||
ownerId?: string // 所有者/导出者的 platformId
|
||||
groupId?: string
|
||||
groupAvatar?: string
|
||||
ownerId?: string
|
||||
}
|
||||
members: ParsedMember[]
|
||||
messages: ParsedMessage[]
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 平台检测工具
|
||||
*
|
||||
* 通过编译时注入的 __IS_ELECTRON__ 常量区分运行环境。
|
||||
* Electron renderer 构建时注入 true,未来 Web 版构建时不定义或为 false。
|
||||
*/
|
||||
|
||||
declare const __IS_ELECTRON__: boolean | undefined
|
||||
|
||||
export const IS_ELECTRON = typeof __IS_ELECTRON__ !== 'undefined' && __IS_ELECTRON__
|
||||
+9
-2
@@ -6,11 +6,18 @@
|
||||
"electron/main/**/*",
|
||||
"electron/preload/**/*",
|
||||
"electron/shared/**/*",
|
||||
"src/types/**/*"
|
||||
"src/types/**/*",
|
||||
"packages/shared-types/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["electron-vite/node"],
|
||||
"moduleResolution": "bundler"
|
||||
"moduleResolution": "bundler",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@electron/*": ["electron/*"],
|
||||
"@openchatlab/*": ["packages/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user