|
@@ -0,0 +1,259 @@
|
|
|
|
|
+import { promptAction } from '@kit.ArkUI';
|
|
|
|
|
+import { KirinCodeClient, Message, Session } from '../service/KirinCodeClient';
|
|
|
|
|
+
|
|
|
|
|
+const client = new KirinCodeClient();
|
|
|
|
|
+
|
|
|
|
|
+const MODES = ['build', 'plan', 'debug', 'solo'] as const;
|
|
|
|
|
+type Mode = typeof MODES[number];
|
|
|
|
|
+
|
|
|
|
|
+@Entry
|
|
|
|
|
+@Component
|
|
|
|
|
+struct Index {
|
|
|
|
|
+ @State messages: Message[] = [];
|
|
|
|
|
+ @State inputText: string = '';
|
|
|
|
|
+ @State currentSession: Session | null = null;
|
|
|
|
|
+ @State connected: boolean = false;
|
|
|
|
|
+ @State connecting: boolean = true;
|
|
|
|
|
+ @State selectedMode: Mode = 'build';
|
|
|
|
|
+ @State sessions: Session[] = [];
|
|
|
|
|
+ @State showSidebar: boolean = true;
|
|
|
|
|
+
|
|
|
|
|
+ async aboutToAppear(): Promise<void> {
|
|
|
|
|
+ await this.checkConnection();
|
|
|
|
|
+ if (this.connected) {
|
|
|
|
|
+ await this.loadSessions();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async checkConnection(): Promise<void> {
|
|
|
|
|
+ this.connecting = true;
|
|
|
|
|
+ this.connected = await client.healthCheck();
|
|
|
|
|
+ this.connecting = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async loadSessions(): Promise<void> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.sessions = await client.listSessions();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.sessions = [];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async createNewSession(): Promise<void> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const session = await client.createSession(`New session - ${new Date().toLocaleTimeString()}`);
|
|
|
|
|
+ this.currentSession = session;
|
|
|
|
|
+ this.messages = [];
|
|
|
|
|
+ await this.loadSessions();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ promptAction.showToast({ message: 'Failed to create session' });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async selectSession(session: Session): Promise<void> {
|
|
|
|
|
+ this.currentSession = session;
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.messages = await client.getMessages(session.id);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.messages = [];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async sendMessage(): Promise<void> {
|
|
|
|
|
+ const text = this.inputText.trim();
|
|
|
|
|
+ if (!text || !this.currentSession) return;
|
|
|
|
|
+
|
|
|
|
|
+ const userMsg: Message = {
|
|
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
|
|
+ role: 'user',
|
|
|
|
|
+ content: text,
|
|
|
|
|
+ timestamp: Date.now()
|
|
|
|
|
+ };
|
|
|
|
|
+ this.messages = [...this.messages, userMsg];
|
|
|
|
|
+ this.inputText = '';
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ await client.sendPrompt(this.currentSession.id, text, this.selectedMode);
|
|
|
|
|
+ const updated = await client.getMessages(this.currentSession.id);
|
|
|
|
|
+ this.messages = updated;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ promptAction.showToast({ message: 'Failed to send message' });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ build() {
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ // Sidebar
|
|
|
|
|
+ if (this.showSidebar) {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // Logo
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Text('🦄')
|
|
|
|
|
+ .fontSize(24)
|
|
|
|
|
+ .margin({ right: 8 })
|
|
|
|
|
+ Text('KirinCode')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .fontColor('#e74c3c')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .border({ width: { bottom: 1 }, color: '#2c3e50' })
|
|
|
|
|
+
|
|
|
|
|
+ // New Session Button
|
|
|
|
|
+ Button('+ New Session')
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .margin({ left: 8, right: 8, top: 8, bottom: 8 })
|
|
|
|
|
+ .onClick(() => this.createNewSession())
|
|
|
|
|
+
|
|
|
|
|
+ // Session List
|
|
|
|
|
+ List({ space: 4 }) {
|
|
|
|
|
+ ForEach(this.sessions, (session: Session) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Text(session.title)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+ .fontColor(this.currentSession?.id === session.id ? '#f39c12' : '#ecf0f1')
|
|
|
|
|
+ .padding(10)
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+ .onClick(() => this.selectSession(session))
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .scrollBar(BarState.Off)
|
|
|
|
|
+
|
|
|
|
|
+ // Connection Status
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Text(this.connecting ? 'Connecting...' : (this.connected ? '🟢 Connected' : '🔴 Disconnected'))
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .fontColor('#7f8c8d')
|
|
|
|
|
+ .padding(12)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .border({ width: { top: 1 }, color: '#2c3e50' })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(250)
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .backgroundColor('#1a1a2e')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Main Chat Area
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // Toolbar
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ // Mode Tabs
|
|
|
|
|
+ Row({ space: 0 }) {
|
|
|
|
|
+ ForEach(MODES, (mode: Mode) => {
|
|
|
|
|
+ Text(mode.charAt(0).toUpperCase() + mode.slice(1))
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor(this.selectedMode === mode ? '#ffffff' : '#7f8c8d')
|
|
|
|
|
+ .padding({ left: 14, right: 14, top: 8, bottom: 8 })
|
|
|
|
|
+ .borderRadius(6)
|
|
|
|
|
+ .backgroundColor(this.selectedMode === mode ? '#e74c3c' : 'transparent')
|
|
|
|
|
+ .margin({ right: 4 })
|
|
|
|
|
+ .onClick(() => this.selectedMode = mode)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Blank()
|
|
|
|
|
+
|
|
|
|
|
+ // Toggle sidebar
|
|
|
|
|
+ Button(this.showSidebar ? '◀' : '▶')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .backgroundColor('transparent')
|
|
|
|
|
+ .onClick(() => this.showSidebar = !this.showSidebar)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding({ left: 16, right: 16, top: 10, bottom: 10 })
|
|
|
|
|
+ .border({ width: { bottom: 1 }, color: '#2c3e50' })
|
|
|
|
|
+ .backgroundColor('#16213e')
|
|
|
|
|
+
|
|
|
|
|
+ // Messages
|
|
|
|
|
+ if (!this.currentSession) {
|
|
|
|
|
+ // Empty state
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Text('🦄')
|
|
|
|
|
+ .fontSize(64)
|
|
|
|
|
+ .opacity(0.4)
|
|
|
|
|
+ Text('Welcome to KirinCode')
|
|
|
|
|
+ .fontSize(24)
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .fontColor('#ecf0f1')
|
|
|
|
|
+ .margin({ top: 12 })
|
|
|
|
|
+ Text('Create a new session or select one to start')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor('#7f8c8d')
|
|
|
|
|
+ .margin({ top: 8 })
|
|
|
|
|
+ Button('New Session')
|
|
|
|
|
+ .margin({ top: 24 })
|
|
|
|
|
+ .onClick(() => this.createNewSession())
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Message list
|
|
|
|
|
+ List({ space: 8 }) {
|
|
|
|
|
+ ForEach(this.messages, (msg: Message) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Text(msg.role === 'user' ? '👤' : '🤖')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .margin({ right: 8 })
|
|
|
|
|
+ Text(msg.content)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(msg.role === 'user' ? '#ecf0f1' : '#bdc3c7')
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .textAlign(msg.role === 'user' ? TextAlign.End : TextAlign.Start)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .alignItems(VerticalAlign.Top)
|
|
|
|
|
+ }
|
|
|
|
|
+ .padding(12)
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .backgroundColor(msg.role === 'user' ? '#2c3e5022' : '#16213e')
|
|
|
|
|
+ .width('90%')
|
|
|
|
|
+ .alignSelf(msg.role === 'user' ? ItemAlign.End : ItemAlign.Start)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .scrollBar(BarState.Auto)
|
|
|
|
|
+ .padding(8)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Input area
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ TextInput({ placeholder: 'Enter your prompt...', text: this.inputText })
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .backgroundColor('#1a1a2e')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .border({ width: 1, color: '#2c3e50' })
|
|
|
|
|
+ .fontColor('#ecf0f1')
|
|
|
|
|
+ .placeholderColor('#7f8c8d')
|
|
|
|
|
+ .onChange((value: string) => this.inputText = value)
|
|
|
|
|
+ .onSubmit(() => this.sendMessage())
|
|
|
|
|
+
|
|
|
|
|
+ Button('Send')
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .margin({ left: 8 })
|
|
|
|
|
+ .backgroundColor('#e74c3c')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => this.sendMessage())
|
|
|
|
|
+ }
|
|
|
|
|
+ .padding({ left: 16, right: 16, top: 10, bottom: 12 })
|
|
|
|
|
+ .border({ width: { top: 1 }, color: '#2c3e50' })
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .backgroundColor('#0f172a')
|
|
|
|
|
+ }
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .backgroundColor('#0f172a')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|