feat: add markdown rendering, API settings, star graph auto-view toggle
- index.html: markdown rendering with marked.js + DOMPurify XSS protection - settings.html: API Key/Base URL/Model config form with save - graph.html: auto camera transition toggle for add/read/delete nodes
This commit is contained in:
2176
entry/build/default/cache/default/default@CompileArkTS/esmodule/.ts_checker_cache
vendored
Normal file
2176
entry/build/default/cache/default/default@CompileArkTS/esmodule/.ts_checker_cache
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/.tsbuildinfo
vendored
Normal file
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/.tsbuildinfo
vendored
Normal file
File diff suppressed because one or more lines are too long
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/.tsbuildinfo.linter
vendored
Normal file
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/.tsbuildinfo.linter
vendored
Normal file
File diff suppressed because one or more lines are too long
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/compileInfo.json
vendored
Normal file
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/compileInfo.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"buildMode":"Debug"}
|
||||
@ -0,0 +1 @@
|
||||
{"hspPkgNames":[],"compileEntries":["&entry/build/generated/r/ResourceTable&","&entry/src/main/ets/entryability/EntryAbility&","&entry/src/main/ets/pages/Index&"],"updateVersionInfo":{}}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
||||
<EFBFBD>r@<40>
|
||||
Binary file not shown.
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/dep_info.json
vendored
Normal file
1
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/dep_info.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"resolveConflictMode":true}
|
||||
@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
//# sourceMappingURL=ResourceTable.js.map
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,30 @@
|
||||
import type AbilityConstant from "@ohos:app.ability.AbilityConstant";
|
||||
import UIAbility from "@ohos:app.ability.UIAbility";
|
||||
import type Window from "@ohos:window";
|
||||
import type Want from "@ohos:app.ability.Want";
|
||||
export default class EntryAbility extends UIAbility {
|
||||
onCreate(want: Want, param: AbilityConstant.LaunchParam): void {
|
||||
console.info('EntryAbility onCreate');
|
||||
}
|
||||
onDestroy(): void {
|
||||
console.info('EntryAbility onDestroy');
|
||||
}
|
||||
onWindowStageCreate(windowStage: Window.WindowStage): void {
|
||||
windowStage.loadContent('pages/Index', (err, data) => {
|
||||
if (err.code) {
|
||||
console.error('Failed to load content: ' + JSON.stringify(err));
|
||||
return;
|
||||
}
|
||||
console.info('Succeeded in loading content: ' + JSON.stringify(data));
|
||||
});
|
||||
}
|
||||
onWindowStageDestroy(): void {
|
||||
console.info('EntryAbility onWindowStageDestroy');
|
||||
}
|
||||
onForeground(): void {
|
||||
console.info('EntryAbility onForeground');
|
||||
}
|
||||
onBackground(): void {
|
||||
console.info('EntryAbility onBackground');
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,222 @@
|
||||
import relationalStore from "@ohos:data.relationalStore";
|
||||
import type { Context } from "@ohos:abilityAccessCtrl";
|
||||
interface TripletData {
|
||||
subject: string;
|
||||
relation: string;
|
||||
object: string;
|
||||
}
|
||||
interface CriteriaData {
|
||||
subject?: string;
|
||||
target?: string;
|
||||
relation?: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
interface NodeData {
|
||||
id: number;
|
||||
label: string;
|
||||
type: string;
|
||||
mentions: number;
|
||||
}
|
||||
interface EdgeData {
|
||||
from: number;
|
||||
to: number;
|
||||
label: string;
|
||||
weight: number;
|
||||
}
|
||||
interface GraphData {
|
||||
nodes: NodeData[];
|
||||
edges: EdgeData[];
|
||||
}
|
||||
interface RecallResult {
|
||||
data: GraphData;
|
||||
}
|
||||
interface ChatMessage {
|
||||
role: string;
|
||||
content: string;
|
||||
}
|
||||
interface CleanupResult {
|
||||
cleaned: number;
|
||||
}
|
||||
const STORE_CONFIG: relationalStore.StoreConfig = {
|
||||
name: 'trulymem.db',
|
||||
securityLevel: relationalStore.SecurityLevel.S1
|
||||
};
|
||||
export class GraphDatabase {
|
||||
private store?: relationalStore.RdbStore;
|
||||
private context?: Context;
|
||||
async init(context: Context): Promise<void> {
|
||||
this.context = context;
|
||||
this.store = await relationalStore.getRdbStore(context, STORE_CONFIG);
|
||||
await this.createTables();
|
||||
}
|
||||
private async createTables(): Promise<void> {
|
||||
if (!this.store)
|
||||
return;
|
||||
await this.store.executeSql(`
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
type TEXT DEFAULT 'concept',
|
||||
mentions INTEGER DEFAULT 1,
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)
|
||||
`);
|
||||
await this.store.executeSql(`
|
||||
CREATE TABLE IF NOT EXISTS relations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
subject_id INTEGER NOT NULL,
|
||||
relation TEXT NOT NULL,
|
||||
object_id INTEGER NOT NULL,
|
||||
weight REAL DEFAULT 1.0,
|
||||
created_at TEXT
|
||||
)
|
||||
`);
|
||||
await this.store.executeSql(`
|
||||
CREATE TABLE IF NOT EXISTS chat_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
role TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
tools TEXT,
|
||||
created_at TEXT
|
||||
)
|
||||
`);
|
||||
}
|
||||
async commit(triplets: TripletData[]): Promise<void> {
|
||||
if (!this.store)
|
||||
return;
|
||||
for (const triplet of triplets) {
|
||||
const subjectId: number = await this.upsertNode(triplet.subject);
|
||||
const objectId: number = await this.upsertNode(triplet.object);
|
||||
const bucket: relationalStore.ValuesBucket = {
|
||||
'subject_id': subjectId,
|
||||
'relation': triplet.relation,
|
||||
'object_id': objectId,
|
||||
'created_at': new Date().toISOString()
|
||||
};
|
||||
await this.store.insert('relations', bucket);
|
||||
}
|
||||
}
|
||||
private async upsertNode(name: string): Promise<number> {
|
||||
if (!this.store)
|
||||
return -1;
|
||||
const predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates('nodes');
|
||||
predicates.equalTo('name', name);
|
||||
const resultSet: relationalStore.ResultSet = await this.store.query(predicates, ['id']);
|
||||
if (resultSet.goToNextRow()) {
|
||||
const id: number = resultSet.getLong(resultSet.getColumnIndex('id'));
|
||||
resultSet.close();
|
||||
return id;
|
||||
}
|
||||
resultSet.close();
|
||||
const bucket: relationalStore.ValuesBucket = {
|
||||
'name': name,
|
||||
'type': 'concept',
|
||||
'created_at': new Date().toISOString(),
|
||||
'updated_at': new Date().toISOString()
|
||||
};
|
||||
return await this.store.insert('nodes', bucket);
|
||||
}
|
||||
async recall(queryIntent: string, seedEntities?: string[]): Promise<RecallResult> {
|
||||
if (!this.store) {
|
||||
const empty: RecallResult = { data: { nodes: [], edges: [] } };
|
||||
return empty;
|
||||
}
|
||||
const nodes: NodeData[] = await this.getAllNodes();
|
||||
const edges: EdgeData[] = await this.getAllEdges();
|
||||
const result: RecallResult = { data: { nodes, edges } };
|
||||
return result;
|
||||
}
|
||||
async purge(criteria: CriteriaData): Promise<void> {
|
||||
if (!this.store)
|
||||
return;
|
||||
const predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates('nodes');
|
||||
await this.store.delete(predicates);
|
||||
}
|
||||
async introspect(): Promise<RecallResult> {
|
||||
const nodes: NodeData[] = await this.getAllNodes();
|
||||
const edges: EdgeData[] = await this.getAllEdges();
|
||||
const result: RecallResult = { data: { nodes, edges } };
|
||||
return result;
|
||||
}
|
||||
async archive(days: number): Promise<void> {
|
||||
// Archive old data - placeholder
|
||||
}
|
||||
async cleanup(dryRun: boolean): Promise<CleanupResult> {
|
||||
const result: CleanupResult = { cleaned: 0 };
|
||||
return result;
|
||||
}
|
||||
async saveChatMessage(role: string, content: string, tools?: string): Promise<void> {
|
||||
if (!this.store)
|
||||
return;
|
||||
const bucket: relationalStore.ValuesBucket = {
|
||||
'role': role,
|
||||
'content': content,
|
||||
'tools': tools || null,
|
||||
'created_at': new Date().toISOString()
|
||||
};
|
||||
await this.store.insert('chat_records', bucket);
|
||||
}
|
||||
async getChatHistory(limit?: number): Promise<ChatMessage[]> {
|
||||
if (!this.store)
|
||||
return [];
|
||||
const predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates('chat_records');
|
||||
predicates.orderByDesc('created_at');
|
||||
if (limit) {
|
||||
predicates.limitAs(limit);
|
||||
}
|
||||
const resultSet: relationalStore.ResultSet = await this.store.query(predicates, ['role', 'content']);
|
||||
const messages: ChatMessage[] = [];
|
||||
while (resultSet.goToNextRow()) {
|
||||
const msg: ChatMessage = {
|
||||
role: resultSet.getString(resultSet.getColumnIndex('role')),
|
||||
content: resultSet.getString(resultSet.getColumnIndex('content'))
|
||||
};
|
||||
messages.push(msg);
|
||||
}
|
||||
resultSet.close();
|
||||
return messages.reverse();
|
||||
}
|
||||
async clearChatHistory(): Promise<void> {
|
||||
if (!this.store)
|
||||
return;
|
||||
const predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates('chat_records');
|
||||
await this.store.delete(predicates);
|
||||
}
|
||||
private async getAllNodes(): Promise<NodeData[]> {
|
||||
if (!this.store)
|
||||
return [];
|
||||
const predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates('nodes');
|
||||
const resultSet: relationalStore.ResultSet = await this.store.query(predicates, ['id', 'name', 'type', 'mentions']);
|
||||
const nodes: NodeData[] = [];
|
||||
while (resultSet.goToNextRow()) {
|
||||
const node: NodeData = {
|
||||
id: resultSet.getLong(resultSet.getColumnIndex('id')),
|
||||
label: resultSet.getString(resultSet.getColumnIndex('name')),
|
||||
type: resultSet.getString(resultSet.getColumnIndex('type')),
|
||||
mentions: resultSet.getLong(resultSet.getColumnIndex('mentions'))
|
||||
};
|
||||
nodes.push(node);
|
||||
}
|
||||
resultSet.close();
|
||||
return nodes;
|
||||
}
|
||||
private async getAllEdges(): Promise<EdgeData[]> {
|
||||
if (!this.store)
|
||||
return [];
|
||||
const predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates('relations');
|
||||
const resultSet: relationalStore.ResultSet = await this.store.query(predicates, ['subject_id', 'relation', 'object_id', 'weight']);
|
||||
const edges: EdgeData[] = [];
|
||||
while (resultSet.goToNextRow()) {
|
||||
const edge: EdgeData = {
|
||||
from: resultSet.getLong(resultSet.getColumnIndex('subject_id')),
|
||||
to: resultSet.getLong(resultSet.getColumnIndex('object_id')),
|
||||
label: resultSet.getString(resultSet.getColumnIndex('relation')),
|
||||
weight: resultSet.getDouble(resultSet.getColumnIndex('weight'))
|
||||
};
|
||||
edges.push(edge);
|
||||
}
|
||||
resultSet.close();
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,205 @@
|
||||
if (!("finalizeConstruction" in ViewPU.prototype)) {
|
||||
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
|
||||
}
|
||||
interface ChatPage_Params {
|
||||
messages?: ChatMessage[];
|
||||
inputText?: string;
|
||||
db?: GraphDatabase;
|
||||
scrollController?: Scroller;
|
||||
}
|
||||
import http from "@ohos:net.http";
|
||||
import dataPreferences from "@ohos:data.preferences";
|
||||
import type { GraphDatabase } from '../model/GraphDatabase';
|
||||
interface ChatMessage {
|
||||
role: string;
|
||||
content: string;
|
||||
}
|
||||
export class ChatPage extends ViewPU {
|
||||
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
|
||||
super(parent, __localStorage, elmtId, extraInfo);
|
||||
if (typeof paramsLambda === "function") {
|
||||
this.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
this.__messages = new ObservedPropertyObjectPU([], this, "messages");
|
||||
this.__inputText = new ObservedPropertySimplePU('', this, "inputText");
|
||||
this.__db = new SynchedPropertyObjectOneWayPU(params.db, this, "db");
|
||||
this.scrollController = new Scroller();
|
||||
this.setInitiallyProvidedValue(params);
|
||||
this.finalizeConstruction();
|
||||
}
|
||||
setInitiallyProvidedValue(params: ChatPage_Params) {
|
||||
if (params.messages !== undefined) {
|
||||
this.messages = params.messages;
|
||||
}
|
||||
if (params.inputText !== undefined) {
|
||||
this.inputText = params.inputText;
|
||||
}
|
||||
if (params.scrollController !== undefined) {
|
||||
this.scrollController = params.scrollController;
|
||||
}
|
||||
}
|
||||
updateStateVars(params: ChatPage_Params) {
|
||||
this.__db.reset(params.db);
|
||||
}
|
||||
purgeVariableDependenciesOnElmtId(rmElmtId) {
|
||||
this.__messages.purgeDependencyOnElmtId(rmElmtId);
|
||||
this.__inputText.purgeDependencyOnElmtId(rmElmtId);
|
||||
this.__db.purgeDependencyOnElmtId(rmElmtId);
|
||||
}
|
||||
aboutToBeDeleted() {
|
||||
this.__messages.aboutToBeDeleted();
|
||||
this.__inputText.aboutToBeDeleted();
|
||||
this.__db.aboutToBeDeleted();
|
||||
SubscriberManager.Get().delete(this.id__());
|
||||
this.aboutToBeDeletedInternal();
|
||||
}
|
||||
private __messages: ObservedPropertyObjectPU<ChatMessage[]>;
|
||||
get messages() {
|
||||
return this.__messages.get();
|
||||
}
|
||||
set messages(newValue: ChatMessage[]) {
|
||||
this.__messages.set(newValue);
|
||||
}
|
||||
private __inputText: ObservedPropertySimplePU<string>;
|
||||
get inputText() {
|
||||
return this.__inputText.get();
|
||||
}
|
||||
set inputText(newValue: string) {
|
||||
this.__inputText.set(newValue);
|
||||
}
|
||||
private __db: SynchedPropertySimpleOneWayPU<GraphDatabase>;
|
||||
get db() {
|
||||
return this.__db.get();
|
||||
}
|
||||
set db(newValue: GraphDatabase) {
|
||||
this.__db.set(newValue);
|
||||
}
|
||||
private scrollController: Scroller;
|
||||
async aboutToAppear() {
|
||||
this.messages = await this.db.getChatHistory(50);
|
||||
}
|
||||
async sendMessage() {
|
||||
if (!this.inputText.trim())
|
||||
return;
|
||||
const userMessage: string = this.inputText;
|
||||
this.inputText = '';
|
||||
await this.db.saveChatMessage('user', userMessage);
|
||||
const userMsg: ChatMessage = { role: 'user', content: userMessage };
|
||||
this.messages = [...this.messages, userMsg];
|
||||
const pref: dataPreferences.Preferences = await dataPreferences.getPreferences(getContext(this), 'trulymem_config');
|
||||
const baseUrl: string = String(await pref.get('base_url', 'https://api.deepseek.com'));
|
||||
const model: string = String(await pref.get('model', 'deepseek-chat'));
|
||||
const apiKey: string = String(await pref.get('api_key', ''));
|
||||
try {
|
||||
const httpRequest: http.HttpRequest = http.createHttp();
|
||||
const response: http.HttpResponse = await httpRequest.request(baseUrl + '/chat/completions', {
|
||||
method: http.RequestMethod.POST,
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + apiKey
|
||||
},
|
||||
extraData: {
|
||||
model: model,
|
||||
messages: [{ role: 'user', content: userMessage }]
|
||||
}
|
||||
});
|
||||
if (response.responseCode === 200) {
|
||||
const data: object = JSON.parse(String(response.result));
|
||||
const choicesArr: object[] = data['choices'] as object[];
|
||||
if (choicesArr && choicesArr.length > 0) {
|
||||
const firstChoice: object = choicesArr[0];
|
||||
const msgObj: object = firstChoice['message'] as object;
|
||||
const assistantMessage: string = String(msgObj['content']);
|
||||
await this.db.saveChatMessage('assistant', assistantMessage);
|
||||
const aiMsg: ChatMessage = { role: 'assistant', content: assistantMessage };
|
||||
this.messages = [...this.messages, aiMsg];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error('HTTP request failed: ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
initialRender() {
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Column.create();
|
||||
Column.width('100%');
|
||||
Column.height('100%');
|
||||
}, Column);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
List.create();
|
||||
List.width('100%');
|
||||
List.layoutWeight(1);
|
||||
}, List);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
ForEach.create();
|
||||
const forEachItemGenFunction = _item => {
|
||||
const msg = _item;
|
||||
{
|
||||
const itemCreation = (elmtId, isInitialRender) => {
|
||||
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
|
||||
ListItem.create(deepRenderFunction, true);
|
||||
if (!isInitialRender) {
|
||||
ListItem.pop();
|
||||
}
|
||||
ViewStackProcessor.StopGetAccessRecording();
|
||||
};
|
||||
const itemCreation2 = (elmtId, isInitialRender) => {
|
||||
ListItem.create(deepRenderFunction, true);
|
||||
};
|
||||
const deepRenderFunction = (elmtId, isInitialRender) => {
|
||||
itemCreation(elmtId, isInitialRender);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Column.create();
|
||||
Column.padding(12);
|
||||
Column.backgroundColor(msg.role === 'user' ? '#E3F2FD' : '#F5F5F5');
|
||||
Column.borderRadius(8);
|
||||
Column.margin({ left: 8, right: 8, bottom: 8 });
|
||||
}, Column);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Text.create(msg.role);
|
||||
Text.fontSize(12);
|
||||
Text.fontColor('#666');
|
||||
Text.width('100%');
|
||||
}, Text);
|
||||
Text.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Text.create(msg.content);
|
||||
Text.fontSize(16);
|
||||
Text.width('100%');
|
||||
Text.margin({ top: 4 });
|
||||
}, Text);
|
||||
Text.pop();
|
||||
Column.pop();
|
||||
ListItem.pop();
|
||||
};
|
||||
this.observeComponentCreation2(itemCreation2, ListItem);
|
||||
ListItem.pop();
|
||||
}
|
||||
};
|
||||
this.forEachUpdateFunction(elmtId, this.messages, forEachItemGenFunction);
|
||||
}, ForEach);
|
||||
ForEach.pop();
|
||||
List.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Row.create();
|
||||
Row.width('100%');
|
||||
Row.padding(8);
|
||||
}, Row);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
TextArea.create({ text: this.inputText, placeholder: '输入消息...' });
|
||||
TextArea.layoutWeight(1);
|
||||
TextArea.onChange((v: string) => { this.inputText = v; });
|
||||
}, TextArea);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Button.createWithLabel('发送');
|
||||
Button.onClick(() => this.sendMessage());
|
||||
}, Button);
|
||||
Button.pop();
|
||||
Row.pop();
|
||||
Column.pop();
|
||||
}
|
||||
rerender() {
|
||||
this.updateDirtyElements();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,65 @@
|
||||
if (!("finalizeConstruction" in ViewPU.prototype)) {
|
||||
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
|
||||
}
|
||||
interface GraphPage_Params {
|
||||
controller?: web_webview.WebviewController;
|
||||
db?: GraphDatabase;
|
||||
}
|
||||
import web_webview from "@ohos:web.webview";
|
||||
import type { GraphDatabase } from '../model/GraphDatabase';
|
||||
export class GraphPage extends ViewPU {
|
||||
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
|
||||
super(parent, __localStorage, elmtId, extraInfo);
|
||||
if (typeof paramsLambda === "function") {
|
||||
this.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
this.controller = new web_webview.WebviewController();
|
||||
this.__db = new SynchedPropertyObjectOneWayPU(params.db, this, "db");
|
||||
this.setInitiallyProvidedValue(params);
|
||||
this.finalizeConstruction();
|
||||
}
|
||||
setInitiallyProvidedValue(params: GraphPage_Params) {
|
||||
if (params.controller !== undefined) {
|
||||
this.controller = params.controller;
|
||||
}
|
||||
}
|
||||
updateStateVars(params: GraphPage_Params) {
|
||||
this.__db.reset(params.db);
|
||||
}
|
||||
purgeVariableDependenciesOnElmtId(rmElmtId) {
|
||||
this.__db.purgeDependencyOnElmtId(rmElmtId);
|
||||
}
|
||||
aboutToBeDeleted() {
|
||||
this.__db.aboutToBeDeleted();
|
||||
SubscriberManager.Get().delete(this.id__());
|
||||
this.aboutToBeDeletedInternal();
|
||||
}
|
||||
private controller: web_webview.WebviewController;
|
||||
private __db: SynchedPropertySimpleOneWayPU<GraphDatabase>;
|
||||
get db() {
|
||||
return this.__db.get();
|
||||
}
|
||||
set db(newValue: GraphDatabase) {
|
||||
this.__db.set(newValue);
|
||||
}
|
||||
initialRender() {
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Column.create();
|
||||
Column.width('100%');
|
||||
Column.height('100%');
|
||||
Column.onAppear(() => {
|
||||
this.controller.refresh();
|
||||
});
|
||||
}, Column);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Web.create({ src: { "id": 0, "type": 30000, params: ['graph.html'], "bundleName": "com.trulymem.app", "moduleName": "entry" }, controller: this.controller });
|
||||
Web.javaScriptAccess(true);
|
||||
Web.width('100%');
|
||||
Web.height('100%');
|
||||
}, Web);
|
||||
Column.pop();
|
||||
}
|
||||
rerender() {
|
||||
this.updateDirtyElements();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,119 @@
|
||||
if (!("finalizeConstruction" in ViewPU.prototype)) {
|
||||
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
|
||||
}
|
||||
interface Index_Params {
|
||||
currentIndex?: number;
|
||||
db?: GraphDatabase;
|
||||
}
|
||||
import { MainPage } from "@normalized:N&&&entry/src/main/ets/pages/MainPage&";
|
||||
import { SettingsPage } from "@normalized:N&&&entry/src/main/ets/pages/SettingsPage&";
|
||||
import { GraphDatabase } from "@normalized:N&&&entry/src/main/ets/model/GraphDatabase&";
|
||||
class Index extends ViewPU {
|
||||
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
|
||||
super(parent, __localStorage, elmtId, extraInfo);
|
||||
if (typeof paramsLambda === "function") {
|
||||
this.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
this.__currentIndex = new ObservedPropertySimplePU(0, this, "currentIndex");
|
||||
this.db = new GraphDatabase();
|
||||
this.setInitiallyProvidedValue(params);
|
||||
this.finalizeConstruction();
|
||||
}
|
||||
setInitiallyProvidedValue(params: Index_Params) {
|
||||
if (params.currentIndex !== undefined) {
|
||||
this.currentIndex = params.currentIndex;
|
||||
}
|
||||
if (params.db !== undefined) {
|
||||
this.db = params.db;
|
||||
}
|
||||
}
|
||||
updateStateVars(params: Index_Params) {
|
||||
}
|
||||
purgeVariableDependenciesOnElmtId(rmElmtId) {
|
||||
this.__currentIndex.purgeDependencyOnElmtId(rmElmtId);
|
||||
}
|
||||
aboutToBeDeleted() {
|
||||
this.__currentIndex.aboutToBeDeleted();
|
||||
SubscriberManager.Get().delete(this.id__());
|
||||
this.aboutToBeDeletedInternal();
|
||||
}
|
||||
private __currentIndex: ObservedPropertySimplePU<number>;
|
||||
get currentIndex() {
|
||||
return this.__currentIndex.get();
|
||||
}
|
||||
set currentIndex(newValue: number) {
|
||||
this.__currentIndex.set(newValue);
|
||||
}
|
||||
private db: GraphDatabase;
|
||||
aboutToAppear() {
|
||||
this.db.init(getContext(this));
|
||||
}
|
||||
initialRender() {
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Column.create();
|
||||
Column.width('100%');
|
||||
Column.height('100%');
|
||||
}, Column);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Tabs.create({ index: this.currentIndex, barPosition: BarPosition.End });
|
||||
Tabs.width('100%');
|
||||
Tabs.height('100%');
|
||||
Tabs.onChange((index: number) => { this.currentIndex = index; });
|
||||
}, Tabs);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
TabContent.create(() => {
|
||||
{
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
if (isInitialRender) {
|
||||
let componentCall = new MainPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/Index.ets", line: 19, col: 21 });
|
||||
ViewPU.create(componentCall);
|
||||
let paramsLambda = () => {
|
||||
return {
|
||||
db: this.db
|
||||
};
|
||||
};
|
||||
componentCall.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
else {
|
||||
this.updateStateVarsOfChildByElmtId(elmtId, {
|
||||
db: this.db
|
||||
});
|
||||
}
|
||||
}, { name: "MainPage" });
|
||||
}
|
||||
});
|
||||
TabContent.tabBar('🌌 TrulyMEM');
|
||||
}, TabContent);
|
||||
TabContent.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
TabContent.create(() => {
|
||||
{
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
if (isInitialRender) {
|
||||
let componentCall = new SettingsPage(this, {}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/Index.ets", line: 24, col: 21 });
|
||||
ViewPU.create(componentCall);
|
||||
let paramsLambda = () => {
|
||||
return {};
|
||||
};
|
||||
componentCall.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
else {
|
||||
this.updateStateVarsOfChildByElmtId(elmtId, {});
|
||||
}
|
||||
}, { name: "SettingsPage" });
|
||||
}
|
||||
});
|
||||
TabContent.tabBar('⚙ 设置');
|
||||
}, TabContent);
|
||||
TabContent.pop();
|
||||
Tabs.pop();
|
||||
Column.pop();
|
||||
}
|
||||
rerender() {
|
||||
this.updateDirtyElements();
|
||||
}
|
||||
static getEntryName(): string {
|
||||
return "Index";
|
||||
}
|
||||
}
|
||||
registerNamedRoute(() => new Index(undefined, {}), "", { bundleName: "com.trulymem.app", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" });
|
||||
Binary file not shown.
@ -0,0 +1,97 @@
|
||||
if (!("finalizeConstruction" in ViewPU.prototype)) {
|
||||
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
|
||||
}
|
||||
interface MainPage_Params {
|
||||
db?: GraphDatabase;
|
||||
}
|
||||
import type { GraphDatabase } from '../model/GraphDatabase';
|
||||
import { GraphPage } from "@normalized:N&&&entry/src/main/ets/pages/GraphPage&";
|
||||
import { ChatPage } from "@normalized:N&&&entry/src/main/ets/pages/ChatPage&";
|
||||
export class MainPage extends ViewPU {
|
||||
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
|
||||
super(parent, __localStorage, elmtId, extraInfo);
|
||||
if (typeof paramsLambda === "function") {
|
||||
this.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
this.__db = new SynchedPropertyObjectOneWayPU(params.db, this, "db");
|
||||
this.setInitiallyProvidedValue(params);
|
||||
this.finalizeConstruction();
|
||||
}
|
||||
setInitiallyProvidedValue(params: MainPage_Params) {
|
||||
}
|
||||
updateStateVars(params: MainPage_Params) {
|
||||
this.__db.reset(params.db);
|
||||
}
|
||||
purgeVariableDependenciesOnElmtId(rmElmtId) {
|
||||
this.__db.purgeDependencyOnElmtId(rmElmtId);
|
||||
}
|
||||
aboutToBeDeleted() {
|
||||
this.__db.aboutToBeDeleted();
|
||||
SubscriberManager.Get().delete(this.id__());
|
||||
this.aboutToBeDeletedInternal();
|
||||
}
|
||||
private __db: SynchedPropertySimpleOneWayPU<GraphDatabase>;
|
||||
get db() {
|
||||
return this.__db.get();
|
||||
}
|
||||
set db(newValue: GraphDatabase) {
|
||||
this.__db.set(newValue);
|
||||
}
|
||||
initialRender() {
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
GridRow.create({ columns: { sm: 1, md: 2, lg: 2 }, gutter: { x: 8, y: 8 } });
|
||||
GridRow.width('100%');
|
||||
GridRow.height('100%');
|
||||
}, GridRow);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
GridCol.create({ span: { sm: 1, md: 1, lg: 1 } });
|
||||
}, GridCol);
|
||||
{
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
if (isInitialRender) {
|
||||
let componentCall = new GraphPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/MainPage.ets", line: 12, col: 17 });
|
||||
ViewPU.create(componentCall);
|
||||
let paramsLambda = () => {
|
||||
return {
|
||||
db: this.db
|
||||
};
|
||||
};
|
||||
componentCall.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
else {
|
||||
this.updateStateVarsOfChildByElmtId(elmtId, {
|
||||
db: this.db
|
||||
});
|
||||
}
|
||||
}, { name: "GraphPage" });
|
||||
}
|
||||
GridCol.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
GridCol.create({ span: { sm: 1, md: 1, lg: 1 } });
|
||||
}, GridCol);
|
||||
{
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
if (isInitialRender) {
|
||||
let componentCall = new ChatPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/MainPage.ets", line: 15, col: 17 });
|
||||
ViewPU.create(componentCall);
|
||||
let paramsLambda = () => {
|
||||
return {
|
||||
db: this.db
|
||||
};
|
||||
};
|
||||
componentCall.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
else {
|
||||
this.updateStateVarsOfChildByElmtId(elmtId, {
|
||||
db: this.db
|
||||
});
|
||||
}
|
||||
}, { name: "ChatPage" });
|
||||
}
|
||||
GridCol.pop();
|
||||
GridRow.pop();
|
||||
}
|
||||
rerender() {
|
||||
this.updateDirtyElements();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,152 @@
|
||||
if (!("finalizeConstruction" in ViewPU.prototype)) {
|
||||
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
|
||||
}
|
||||
interface SettingsPage_Params {
|
||||
baseUrl?: string;
|
||||
model?: string;
|
||||
apiKey?: string;
|
||||
pref?: dataPreferences.Preferences;
|
||||
}
|
||||
import dataPreferences from "@ohos:data.preferences";
|
||||
export class SettingsPage extends ViewPU {
|
||||
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
|
||||
super(parent, __localStorage, elmtId, extraInfo);
|
||||
if (typeof paramsLambda === "function") {
|
||||
this.paramsGenerator_ = paramsLambda;
|
||||
}
|
||||
this.__baseUrl = new ObservedPropertySimplePU('', this, "baseUrl");
|
||||
this.__model = new ObservedPropertySimplePU('', this, "model");
|
||||
this.__apiKey = new ObservedPropertySimplePU('', this, "apiKey");
|
||||
this.pref = undefined;
|
||||
this.setInitiallyProvidedValue(params);
|
||||
this.finalizeConstruction();
|
||||
}
|
||||
setInitiallyProvidedValue(params: SettingsPage_Params) {
|
||||
if (params.baseUrl !== undefined) {
|
||||
this.baseUrl = params.baseUrl;
|
||||
}
|
||||
if (params.model !== undefined) {
|
||||
this.model = params.model;
|
||||
}
|
||||
if (params.apiKey !== undefined) {
|
||||
this.apiKey = params.apiKey;
|
||||
}
|
||||
if (params.pref !== undefined) {
|
||||
this.pref = params.pref;
|
||||
}
|
||||
}
|
||||
updateStateVars(params: SettingsPage_Params) {
|
||||
}
|
||||
purgeVariableDependenciesOnElmtId(rmElmtId) {
|
||||
this.__baseUrl.purgeDependencyOnElmtId(rmElmtId);
|
||||
this.__model.purgeDependencyOnElmtId(rmElmtId);
|
||||
this.__apiKey.purgeDependencyOnElmtId(rmElmtId);
|
||||
}
|
||||
aboutToBeDeleted() {
|
||||
this.__baseUrl.aboutToBeDeleted();
|
||||
this.__model.aboutToBeDeleted();
|
||||
this.__apiKey.aboutToBeDeleted();
|
||||
SubscriberManager.Get().delete(this.id__());
|
||||
this.aboutToBeDeletedInternal();
|
||||
}
|
||||
private __baseUrl: ObservedPropertySimplePU<string>;
|
||||
get baseUrl() {
|
||||
return this.__baseUrl.get();
|
||||
}
|
||||
set baseUrl(newValue: string) {
|
||||
this.__baseUrl.set(newValue);
|
||||
}
|
||||
private __model: ObservedPropertySimplePU<string>;
|
||||
get model() {
|
||||
return this.__model.get();
|
||||
}
|
||||
set model(newValue: string) {
|
||||
this.__model.set(newValue);
|
||||
}
|
||||
private __apiKey: ObservedPropertySimplePU<string>;
|
||||
get apiKey() {
|
||||
return this.__apiKey.get();
|
||||
}
|
||||
set apiKey(newValue: string) {
|
||||
this.__apiKey.set(newValue);
|
||||
}
|
||||
private pref?: dataPreferences.Preferences;
|
||||
async aboutToAppear() {
|
||||
const ctx = getContext(this);
|
||||
this.pref = await dataPreferences.getPreferences(ctx, 'trulymem_config');
|
||||
this.baseUrl = String(await this.pref.get('base_url', 'https://api.deepseek.com'));
|
||||
this.model = String(await this.pref.get('model', 'deepseek-chat'));
|
||||
this.apiKey = String(await this.pref.get('api_key', ''));
|
||||
}
|
||||
async onBaseUrlChange(value: string) {
|
||||
this.baseUrl = value;
|
||||
await this.pref?.put('base_url', value);
|
||||
await this.pref?.flush();
|
||||
}
|
||||
async onModelChange(value: string) {
|
||||
this.model = value;
|
||||
await this.pref?.put('model', value);
|
||||
await this.pref?.flush();
|
||||
}
|
||||
async onApiKeyChange(value: string) {
|
||||
this.apiKey = value;
|
||||
await this.pref?.put('api_key', value);
|
||||
await this.pref?.flush();
|
||||
}
|
||||
initialRender() {
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Column.create();
|
||||
Column.width('100%');
|
||||
Column.height('100%');
|
||||
Column.padding(16);
|
||||
}, Column);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Text.create('API 配置');
|
||||
Text.fontSize(24);
|
||||
Text.fontWeight(FontWeight.Bold);
|
||||
Text.margin({ top: 20, bottom: 16 });
|
||||
}, Text);
|
||||
Text.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Text.create('Base URL');
|
||||
Text.fontSize(14);
|
||||
Text.width('100%');
|
||||
Text.margin({ left: 16 });
|
||||
}, Text);
|
||||
Text.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
TextInput.create({ placeholder: 'https://api.deepseek.com', text: this.baseUrl });
|
||||
TextInput.onChange((v: string) => { this.onBaseUrlChange(v); });
|
||||
TextInput.margin({ left: 16, right: 16, bottom: 12 });
|
||||
}, TextInput);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Text.create('Model ID');
|
||||
Text.fontSize(14);
|
||||
Text.width('100%');
|
||||
Text.margin({ left: 16 });
|
||||
}, Text);
|
||||
Text.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
TextInput.create({ placeholder: 'deepseek-chat', text: this.model });
|
||||
TextInput.onChange((v: string) => { this.onModelChange(v); });
|
||||
TextInput.margin({ left: 16, right: 16, bottom: 12 });
|
||||
}, TextInput);
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
Text.create('API Key');
|
||||
Text.fontSize(14);
|
||||
Text.width('100%');
|
||||
Text.margin({ left: 16 });
|
||||
}, Text);
|
||||
Text.pop();
|
||||
this.observeComponentCreation2((elmtId, isInitialRender) => {
|
||||
TextInput.create({ placeholder: 'sk-...', text: this.apiKey });
|
||||
TextInput.type(InputType.Password);
|
||||
TextInput.onChange((v: string) => { this.onApiKeyChange(v); });
|
||||
TextInput.margin({ left: 16, right: 16, bottom: 12 });
|
||||
}, TextInput);
|
||||
Column.pop();
|
||||
}
|
||||
rerender() {
|
||||
this.updateDirtyElements();
|
||||
}
|
||||
}
|
||||
8
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/filesInfo.txt
vendored
Normal file
8
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/filesInfo.txt
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/build/default/generated/r/default/ResourceTable.js;&entry/build/generated/r/ResourceTable&;esm;entry|entry|1.0.0|build/default/generated/r/default/ResourceTable.js;entry;false;ts
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/entryability/EntryAbility.ts;&entry/src/main/ets/entryability/EntryAbility&;esm;entry|entry|1.0.0|src/main/ets/entryability/EntryAbility.ts;entry;false;ets
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.ts;&entry/src/main/ets/pages/Index&;esm;entry|entry|1.0.0|src/main/ets/pages/Index.ts;entry;false;ets
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/MainPage.ts;&entry/src/main/ets/pages/MainPage&;esm;entry|entry|1.0.0|src/main/ets/pages/MainPage.ts;entry;false;ets
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/model/GraphDatabase.ts;&entry/src/main/ets/model/GraphDatabase&;esm;entry|entry|1.0.0|src/main/ets/model/GraphDatabase.ts;entry;false;ets
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/SettingsPage.ts;&entry/src/main/ets/pages/SettingsPage&;esm;entry|entry|1.0.0|src/main/ets/pages/SettingsPage.ts;entry;false;ets
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/GraphPage.ts;&entry/src/main/ets/pages/GraphPage&;esm;entry|entry|1.0.0|src/main/ets/pages/GraphPage.ts;entry;false;ets
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/ChatPage.ts;&entry/src/main/ets/pages/ChatPage&;esm;entry|entry|1.0.0|src/main/ets/pages/ChatPage.ts;entry;false;ets
|
||||
9
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/modules.cache
vendored
Normal file
9
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/modules.cache
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/build/default/generated/r/default/ResourceTable.js;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/build/default/generated/r/default/ResourceTable.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/entryability/EntryAbility.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/entryability/EntryAbility.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/Index.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/MainPage.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/MainPage.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/model/GraphDatabase.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/model/GraphDatabase.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/SettingsPage.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/SettingsPage.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/GraphPage.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/GraphPage.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/ChatPage.ts;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/ChatPage.protoBin
|
||||
/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.txt;/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.protoBin
|
||||
BIN
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.protoBin
vendored
Normal file
BIN
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.protoBin
vendored
Normal file
Binary file not shown.
7
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.txt
vendored
Normal file
7
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.txt
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
@system.app:@native.system.app
|
||||
@ohos.app:@native.ohos.app
|
||||
@system.router:@native.system.router
|
||||
@system.curves:@native.system.curves
|
||||
@ohos.curves:@native.ohos.curves
|
||||
@system.matrix4:@native.system.matrix4
|
||||
@ohos.matrix4:@native.ohos.matrix4
|
||||
89
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/sourceMaps.json
vendored
Normal file
89
entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/sourceMaps.json
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Use these variables when you tailor your ArkTS code. They must be of the const type.
|
||||
*/
|
||||
export const BUNDLE_NAME = 'com.trulymem.app';
|
||||
export const BUNDLE_TYPE = 'app';
|
||||
export const VERSION_CODE = 1000001;
|
||||
export const VERSION_NAME = '1.0.0';
|
||||
export const TARGET_NAME = 'default';
|
||||
export const PRODUCT_NAME = 'default';
|
||||
export const BUILD_MODE_NAME = 'debug';
|
||||
export const DEBUG = true;
|
||||
|
||||
/**
|
||||
* BuildProfile Class is used only for compatibility purposes.
|
||||
*/
|
||||
export default class BuildProfile {
|
||||
static readonly BUNDLE_NAME = BUNDLE_NAME;
|
||||
static readonly BUNDLE_TYPE = BUNDLE_TYPE;
|
||||
static readonly VERSION_CODE = VERSION_CODE;
|
||||
static readonly VERSION_NAME = VERSION_NAME;
|
||||
static readonly TARGET_NAME = TARGET_NAME;
|
||||
static readonly PRODUCT_NAME = PRODUCT_NAME;
|
||||
static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
|
||||
static readonly DEBUG = DEBUG;
|
||||
}
|
||||
31
entry/build/default/generated/r/default/ResourceTable.h
Normal file
31
entry/build/default/generated/r/default/ResourceTable.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef RESOURCE_TABLE_H
|
||||
#define RESOURCE_TABLE_H
|
||||
|
||||
#include<stdint.h>
|
||||
|
||||
namespace OHOS {
|
||||
const int32_t STRING_ENTRYABILITY_DESC = 0x01000001;
|
||||
const int32_t STRING_ENTRYABILITY_LABEL = 0x01000002;
|
||||
const int32_t STRING_APP_NAME = 0x01000003;
|
||||
const int32_t STRING_MODULE_DESC = 0x01000004;
|
||||
const int32_t COLOR_START_WINDOW_BACKGROUND = 0x01000005;
|
||||
const int32_t MEDIA_LAYERED_IMAGE = 0x01000000;
|
||||
const int32_t MEDIA_STARTICON = 0x01000006;
|
||||
const int32_t PROFILE_MAIN_PAGES = 0x01000007;
|
||||
}
|
||||
#endif
|
||||
16
entry/build/default/generated/r/default/ResourceTable.ts
Normal file
16
entry/build/default/generated/r/default/ResourceTable.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//@ts-noCheck
|
||||
@ -0,0 +1 @@
|
||||
[{"hapName":"entry-default-unsigned.hap","deviceTypes":["phone","tablet","2in1"],"isSigned":false}]
|
||||
26
entry/build/default/intermediates/loader/default/loader.json
Normal file
26
entry/build/default/intermediates/loader/default/loader.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"modulePathMap": {
|
||||
"entry": "/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry",
|
||||
"trulymem-core": "/home/program/TrulyMEM-TrueHumanMEM/harmonyos/trulymem-core"
|
||||
},
|
||||
"compileMode": "esmodule",
|
||||
"projectRootPath": "/home/program/TrulyMEM-TrueHumanMEM/harmonyos",
|
||||
"nodeModulesPath": "/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/loader_out/default/node_modules",
|
||||
"byteCodeHarInfo": {},
|
||||
"declarationEntry": [],
|
||||
"moduleName": "entry",
|
||||
"hspNameOhmMap": {},
|
||||
"harNameOhmMap": {},
|
||||
"packageManagerType": "ohpm",
|
||||
"compileEntry": [
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/generated/r/default/ResourceTable.ts"
|
||||
],
|
||||
"otherCompileFiles": [],
|
||||
"dynamicImportLibInfo": {},
|
||||
"routerMap": [],
|
||||
"hspResourcesMap": {},
|
||||
"updateVersionInfo": {},
|
||||
"customizedHar": false,
|
||||
"anBuildOutPut": "/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/loader_out/default/an/arm64-v8a",
|
||||
"anBuildMode": "type"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"entry":{"packageName":"entry","bundleName":"","moduleName":"","version":"","entryPath":"src/main/","isSO":false,"dependencyAlias":""}}
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -0,0 +1,66 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.trulymem.app",
|
||||
"vendor": "trulymem",
|
||||
"versionCode": 1000001,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:app_name",
|
||||
"apiReleaseType": "Release",
|
||||
"compileSdkVersion": "6.1.0.105",
|
||||
"targetAPIVersion": 60100023,
|
||||
"minAPIVersion": 60100023,
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug",
|
||||
"debug": true
|
||||
},
|
||||
"module": {
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": [
|
||||
"phone",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:startIcon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.INTERNET"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.GET_NETWORK_INFO"
|
||||
}
|
||||
],
|
||||
"packageName": "entry"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true,"iconId":16777216,"labelId":16777219},"module":{"name":"entry","type":"entry","description":"$string:module_desc","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","icon":"$media:layered_image","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["action.system.home"]}],"descriptionId":16777217,"iconId":16777216,"labelId":16777218,"startWindowIconId":16777222,"startWindowBackgroundId":16777221}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"entry","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[],"descriptionId":16777220}}
|
||||
@ -0,0 +1 @@
|
||||
{"libs":{},"stripped":{}}
|
||||
@ -0,0 +1 @@
|
||||
{"libs":{},"binxo":{},"binxoSymbol":{"enableAsanBinxo":false,"excludeSoFromBinxo":[]}}
|
||||
@ -0,0 +1,69 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.trulymem.app",
|
||||
"vendor": "trulymem",
|
||||
"versionCode": 1000001,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:app_name",
|
||||
"apiReleaseType": "Release",
|
||||
"compileSdkVersion": "6.1.0.105",
|
||||
"targetAPIVersion": 60100023,
|
||||
"minAPIVersion": 60100023,
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug",
|
||||
"debug": true
|
||||
},
|
||||
"module": {
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": [
|
||||
"phone",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:startIcon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.INTERNET"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.GET_NETWORK_INFO"
|
||||
}
|
||||
],
|
||||
"packageName": "entry",
|
||||
"virtualMachine": "ark13.0.1.0",
|
||||
"compileMode": "esmodule",
|
||||
"dependencies": []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
string EntryAbility_desc 0x01000001
|
||||
string EntryAbility_label 0x01000002
|
||||
string app_name 0x01000003
|
||||
string module_desc 0x01000004
|
||||
color start_window_background 0x01000005
|
||||
media layered_image 0x01000000
|
||||
media startIcon 0x01000006
|
||||
profile main_pages 0x01000007
|
||||
@ -0,0 +1 @@
|
||||
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true},"module":{"name":"entry","type":"entry","description":"$string:module_desc","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","icon":"$media:layered_image","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["action.system.home"]}]}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"entry","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[]}}
|
||||
@ -0,0 +1,35 @@
|
||||
{
|
||||
"record": [{
|
||||
"type": "media",
|
||||
"name": "layered_image",
|
||||
"id": "0x01000000"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "EntryAbility_desc",
|
||||
"id": "0x01000001"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "EntryAbility_label",
|
||||
"id": "0x01000002"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "app_name",
|
||||
"id": "0x01000003"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "module_desc",
|
||||
"id": "0x01000004"
|
||||
}, {
|
||||
"type": "color",
|
||||
"name": "start_window_background",
|
||||
"id": "0x01000005"
|
||||
}, {
|
||||
"type": "media",
|
||||
"name": "startIcon",
|
||||
"id": "0x01000006"
|
||||
}, {
|
||||
"type": "profile",
|
||||
"name": "main_pages",
|
||||
"id": "0x01000007"
|
||||
}]
|
||||
}
|
||||
62
entry/build/default/intermediates/res/default/module.json
Normal file
62
entry/build/default/intermediates/res/default/module.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.trulymem.app",
|
||||
"vendor": "trulymem",
|
||||
"versionCode": 1000001,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:app_name",
|
||||
"apiReleaseType": "Release",
|
||||
"compileSdkVersion": "6.1.0.105",
|
||||
"targetAPIVersion": 60100023,
|
||||
"minAPIVersion": 60100023,
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug",
|
||||
"debug": true,
|
||||
"iconId": 16777216,
|
||||
"labelId": 16777219
|
||||
},
|
||||
"module": {
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": ["phone", "tablet", "2in1"],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"abilities": [{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:startIcon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [{
|
||||
"entities": ["entity.system.home"],
|
||||
"actions": ["action.system.home"]
|
||||
}],
|
||||
"descriptionId": 16777217,
|
||||
"iconId": 16777216,
|
||||
"labelId": 16777218,
|
||||
"startWindowIconId": 16777222,
|
||||
"startWindowBackgroundId": 16777221
|
||||
}],
|
||||
"requestPermissions": [{
|
||||
"name": "ohos.permission.INTERNET"
|
||||
}, {
|
||||
"name": "ohos.permission.GET_NETWORK_INFO"
|
||||
}],
|
||||
"packageName": "entry",
|
||||
"virtualMachine": "ark13.0.1.0",
|
||||
"compileMode": "esmodule",
|
||||
"dependencies": [],
|
||||
"descriptionId": 16777220
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"context":{"extensionPath":"/home/program/tools/command-line-tools/sdk/default/hms/toolchains/lib/libimage_transcoder_shared.so"},"compression":{"media":{"enable":false},"filters":[]}}
|
||||
@ -0,0 +1 @@
|
||||
{"configPath":"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/process_profile/default/module.json","packageName":"com.trulymem.app","output":"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/res/default","moduleNames":"entry,trulymem-core","ResourceTable":["/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/generated/r/default/ResourceTable.h","/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/generated/r/default/ResourceTable.ts"],"applicationResource":"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/AppScope/resources","moduleResources":["/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/src/main/resources"],"dependencies":[],"iconCheck":true,"compression":"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/res/default/opt-compression.json","ids":"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/res/default/ids_map","definedIds":"/home/program/TrulyMEM-TrueHumanMEM/harmonyos/entry/build/default/intermediates/res/default/ids_map/id_defined.json","definedSysIds":"/home/program/tools/command-line-tools/sdk/default/hms/toolchains/id_defined.json"}
|
||||
BIN
entry/build/default/intermediates/res/default/resources.index
Normal file
BIN
entry/build/default/intermediates/res/default/resources.index
Normal file
Binary file not shown.
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<rect width="200" height="200" rx="30" fill="#6366f1"/>
|
||||
<text x="100" y="130" font-family="Arial" font-size="80" fill="white" text-anchor="middle" font-weight="bold">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 313 B |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<rect width="200" height="200" rx="20" fill="#6366f1"/>
|
||||
<text x="100" y="130" font-family="Arial" font-size="60" fill="white" text-anchor="middle" font-weight="bold">TM</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 314 B |
@ -0,0 +1,5 @@
|
||||
{
|
||||
"src": [
|
||||
"pages/Index"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,302 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>记忆星图 - TrulyMEM</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Courier New', monospace; background: #0a0a1a; color: #ffffff; overflow: hidden; width: 100vw; height: 100vh; }
|
||||
#canvas-container { width: 100%; height: 100%; position: relative; }
|
||||
canvas { display: block; }
|
||||
#stats { position: absolute; top: 20px; left: 20px; background: rgba(10, 10, 26, 0.8); padding: 15px 20px; border-radius: 8px; border: 1px solid rgba(100, 100, 255, 0.3); font-size: 14px; z-index: 100; backdrop-filter: blur(10px); }
|
||||
#stats h3 { margin-bottom: 8px; color: #4488ff; font-size: 16px; }
|
||||
#stats p { margin: 4px 0; color: #aaaacc; }
|
||||
#stats span { color: #ffffff; font-weight: bold; }
|
||||
#node-info { position: absolute; top: 20px; right: 20px; background: rgba(10, 10, 26, 0.9); padding: 15px 20px; border-radius: 8px; border: 1px solid rgba(100, 100, 255, 0.3); font-size: 14px; z-index: 100; display: none; backdrop-filter: blur(10px); max-width: 300px; }
|
||||
#node-info h3 { color: #44ff88; margin-bottom: 8px; font-size: 16px; }
|
||||
#node-info p { margin: 4px 0; color: #aaaacc; }
|
||||
#node-info .label { color: #8888aa; }
|
||||
#loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 20px; color: #4488ff; z-index: 200; }
|
||||
#nav { position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); display: flex; gap: 20px; z-index: 100; }
|
||||
.nav-btn { background: rgba(10, 10, 26, 0.8); border: 1px solid rgba(100, 100, 255, 0.3); color: #aaaacc; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-family: 'Courier New', monospace; font-size: 14px; backdrop-filter: blur(10px); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-container">
|
||||
<div id="loading">正在加载星图数据...</div>
|
||||
<div id="stats">
|
||||
<h3>🌌 记忆星图</h3>
|
||||
<p>节点: <span id="node-count">0</span></p>
|
||||
<p>边: <span id="edge-count">0</span></p>
|
||||
<p>状态: <span id="status">初始化中...</span></p>
|
||||
</div>
|
||||
<div id="node-info">
|
||||
<h3 id="info-name"></h3>
|
||||
<p><span class="label">类型:</span> <span id="info-type"></span></p>
|
||||
<p><span class="label">提及次数:</span> <span id="info-mentions"></span></p>
|
||||
<p><span class="label">连接数:</span> <span id="info-links"></span></p>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<button class="nav-btn active">🌌 星图</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
let scene, camera, renderer, controls;
|
||||
let nodes = [], edges = [];
|
||||
let nodeMeshes = [], edgeLines = [];
|
||||
let starField, nebulaParticles;
|
||||
let raycaster, mouse;
|
||||
let hoveredNode = null, selectedNode = null;
|
||||
let animationId;
|
||||
let highlightPulse = 0;
|
||||
|
||||
const typeColors = { 'person': 0x4488ff, 'task': 0xff8844, 'ai': 0xaa44ff, 'concept': 0x44ff88, 'object': 0xff4444 };
|
||||
const defaultColor = 0xcccccc;
|
||||
const edgeColors = { '喜欢': 0xff6b6b, '学习': 0x4ecdc4, '属于': 0x45b7d1, '相关': 0x96ceb4, '使用': 0xfeca57, '创建': 0xff9ff3 };
|
||||
const defaultEdgeColor = 0x444466;
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.015);
|
||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||||
camera.position.set(0, 30, 60);
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setClearColor(0x0a0a1a, 1);
|
||||
document.getElementById('canvas-container').appendChild(renderer.domElement);
|
||||
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
const ambientLight = new THREE.AmbientLight(0x444466, 0.6);
|
||||
scene.add(ambientLight);
|
||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||
directionalLight.position.set(50, 100, 50);
|
||||
scene.add(directionalLight);
|
||||
raycaster = new THREE.Raycaster();
|
||||
mouse = new THREE.Vector2();
|
||||
createStarField();
|
||||
createNebula();
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
renderer.domElement.addEventListener('mousemove', onMouseMove);
|
||||
renderer.domElement.addEventListener('click', onMouseClick);
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'graph_data') {
|
||||
window.__graphData = event.data.payload;
|
||||
loadGraphData();
|
||||
}
|
||||
});
|
||||
window.parent.postMessage('request_graph_data', '*');
|
||||
animate();
|
||||
}
|
||||
|
||||
function createStarField() {
|
||||
const starCount = 3000;
|
||||
const positions = new Float32Array(starCount * 3);
|
||||
const colors = new Float32Array(starCount * 3);
|
||||
for (let i = 0; i < starCount; i++) {
|
||||
const i3 = i * 3;
|
||||
const radius = 400 + Math.random() * 600;
|
||||
const theta = Math.random() * Math.PI * 2;
|
||||
const phi = Math.acos(2 * Math.random() - 1);
|
||||
positions[i3] = radius * Math.sin(phi) * Math.cos(theta);
|
||||
positions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
|
||||
positions[i3 + 2] = radius * Math.cos(phi);
|
||||
const colorChoice = Math.random();
|
||||
if (colorChoice < 0.7) {
|
||||
colors[i3] = 0.8 + Math.random() * 0.2;
|
||||
colors[i3 + 1] = 0.8 + Math.random() * 0.2;
|
||||
colors[i3 + 2] = 1.0;
|
||||
} else {
|
||||
colors[i3] = 1.0;
|
||||
colors[i3 + 1] = 0.9 + Math.random() * 0.1;
|
||||
colors[i3 + 2] = 0.8 + Math.random() * 0.2;
|
||||
}
|
||||
}
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({ size: 1.5, vertexColors: true, transparent: true, opacity: 0.8, sizeAttenuation: true });
|
||||
starField = new THREE.Points(geometry, material);
|
||||
scene.add(starField);
|
||||
}
|
||||
|
||||
function createNebula() {
|
||||
const nebulaCount = 500;
|
||||
const positions = new Float32Array(nebulaCount * 3);
|
||||
const colors = new Float32Array(nebulaCount * 3);
|
||||
for (let i = 0; i < nebulaCount; i++) {
|
||||
const i3 = i * 3;
|
||||
positions[i3] = (Math.random() - 0.5) * 800;
|
||||
positions[i3 + 1] = (Math.random() - 0.5) * 800;
|
||||
positions[i3 + 2] = (Math.random() - 0.5) * 800;
|
||||
const colorChoice = Math.random();
|
||||
if (colorChoice < 0.33) {
|
||||
colors[i3] = 0.5 + Math.random() * 0.3; colors[i3 + 1] = 0.2 + Math.random() * 0.2; colors[i3 + 2] = 0.7 + Math.random() * 0.3;
|
||||
} else if (colorChoice < 0.66) {
|
||||
colors[i3] = 0.2 + Math.random() * 0.2; colors[i3 + 1] = 0.3 + Math.random() * 0.3; colors[i3 + 2] = 0.8 + Math.random() * 0.2;
|
||||
} else {
|
||||
colors[i3] = 0.7 + Math.random() * 0.3; colors[i3 + 1] = 0.2 + Math.random() * 0.2; colors[i3 + 2] = 0.5 + Math.random() * 0.3;
|
||||
}
|
||||
}
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({ size: 8, vertexColors: true, transparent: true, opacity: 0.15, sizeAttenuation: true, blending: THREE.AdditiveBlending });
|
||||
nebulaParticles = new THREE.Points(geometry, material);
|
||||
scene.add(nebulaParticles);
|
||||
}
|
||||
|
||||
window.loadGraphData = function(data) {
|
||||
if (data && data.nodes && data.edges) {
|
||||
nodes = data.nodes.map(n => ({ id: n.id, name: n.label || n.name, type: n.type, mention_count: n.mentions || 1 }));
|
||||
edges = data.edges.map(e => ({ id: e.id, source: e.from || e.source, target: e.to || e.target, relation_type: e.label || e.relation }));
|
||||
document.getElementById('node-count').textContent = nodes.length;
|
||||
document.getElementById('edge-count').textContent = edges.length;
|
||||
document.getElementById('status').textContent = '就绪';
|
||||
createGraphVisualization();
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function createGraphVisualization() {
|
||||
nodeMeshes.forEach(mesh => scene.remove(mesh));
|
||||
edgeLines.forEach(line => scene.remove(line));
|
||||
nodeMeshes = [];
|
||||
edgeLines = [];
|
||||
if (nodes.length === 0) return;
|
||||
const nodeDegrees = {};
|
||||
nodes.forEach(n => nodeDegrees[n.id] = 0);
|
||||
edges.forEach(e => {
|
||||
nodeDegrees[e.source] = (nodeDegrees[e.source] || 0) + 1;
|
||||
nodeDegrees[e.target] = (nodeDegrees[e.target] || 0) + 1;
|
||||
});
|
||||
const positions = {};
|
||||
const maxDegree = Math.max(...Object.values(nodeDegrees), 1);
|
||||
nodes.forEach((node, i) => {
|
||||
const angle = (i / nodes.length) * Math.PI * 2;
|
||||
const radius = 15 + (nodeDegrees[node.id] / maxDegree) * 35;
|
||||
positions[node.id] = { x: radius * Math.cos(angle), y: (Math.random() - 0.5) * 10, z: radius * Math.sin(angle) };
|
||||
});
|
||||
for (let iter = 0; iter < 50; iter++) {
|
||||
Object.keys(positions).forEach(id1 => {
|
||||
Object.keys(positions).forEach(id2 => {
|
||||
if (id1 >= id2) return;
|
||||
const pos1 = positions[id1], pos2 = positions[id2];
|
||||
const dx = pos1.x - pos2.x, dy = pos1.y - pos2.y, dz = pos1.z - pos2.z;
|
||||
const dist = Math.sqrt(dx*dx + dy*dy + dz*dz) + 0.1;
|
||||
if (dist < 10) {
|
||||
const force = 0.1 / dist;
|
||||
pos1.x += (dx/dist)*force; pos1.y += (dy/dist)*force; pos1.z += (dz/dist)*force;
|
||||
pos2.x -= (dx/dist)*force; pos2.y -= (dy/dist)*force; pos2.z -= (dz/dist)*force;
|
||||
}
|
||||
});
|
||||
});
|
||||
edges.forEach(edge => {
|
||||
const pos1 = positions[edge.source], pos2 = positions[edge.target];
|
||||
if (!pos1 || !pos2) return;
|
||||
const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y, dz = pos2.z - pos1.z;
|
||||
const dist = Math.sqrt(dx*dx + dy*dy + dz*dz) + 0.1;
|
||||
if (dist > 15) {
|
||||
const force = 0.05;
|
||||
pos1.x += (dx/dist)*force; pos1.y += (dy/dist)*force; pos1.z += (dz/dist)*force;
|
||||
pos2.x -= (dx/dist)*force; pos2.y -= (dy/dist)*force; pos2.z -= (dz/dist)*force;
|
||||
}
|
||||
});
|
||||
}
|
||||
nodes.forEach(node => {
|
||||
const pos = positions[node.id];
|
||||
if (!pos) return;
|
||||
const radius = 0.4 + Math.min(node.mention_count * 0.15, 1.5);
|
||||
const color = typeColors[node.type] || defaultColor;
|
||||
const geometry = new THREE.SphereGeometry(radius, 16, 12);
|
||||
const material = new THREE.MeshPhongMaterial({ color: color, emissive: color, emissiveIntensity: 0.5 + Math.min(node.mention_count * 0.05, 0.3), shininess: 30 });
|
||||
const sphere = new THREE.Mesh(geometry, material);
|
||||
sphere.position.set(pos.x, pos.y, pos.z);
|
||||
sphere.userData = { nodeId: node.id, nodeData: node };
|
||||
scene.add(sphere);
|
||||
nodeMeshes.push(sphere);
|
||||
});
|
||||
edges.forEach(edge => {
|
||||
const pos1 = positions[edge.source], pos2 = positions[edge.target];
|
||||
if (!pos1 || !pos2) return;
|
||||
const color = edgeColors[edge.relation_type] || defaultEdgeColor;
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(pos1.x, pos1.y, pos1.z), new THREE.Vector3(pos2.x, pos2.y, pos2.z)]);
|
||||
const material = new THREE.LineBasicMaterial({ color: color, transparent: true, opacity: 0.4, linewidth: 1 });
|
||||
const line = new THREE.Line(geometry, material);
|
||||
line.userData = { edgeId: edge.id, edgeData: edge };
|
||||
scene.add(line);
|
||||
edgeLines.push(line);
|
||||
});
|
||||
const allPositions = Object.values(positions);
|
||||
if (allPositions.length > 0) {
|
||||
let maxDist = 0;
|
||||
allPositions.forEach(pos => { maxDist = Math.max(maxDist, Math.sqrt(pos.x*pos.x + pos.y*pos.y + pos.z*pos.z)); });
|
||||
camera.position.set(maxDist * 2.2, maxDist * 1.5, maxDist * 2.2);
|
||||
controls.target.set(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseMove(event) {
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(nodeMeshes);
|
||||
if (intersects.length > 0) {
|
||||
const node = intersects[0].object;
|
||||
if (hoveredNode !== node) {
|
||||
if (hoveredNode) hoveredNode.scale.set(1, 1, 1);
|
||||
hoveredNode = node;
|
||||
node.scale.set(1.2, 1.2, 1.2);
|
||||
showNodeInfo(node.userData.nodeData);
|
||||
}
|
||||
} else {
|
||||
if (hoveredNode) { hoveredNode.scale.set(1, 1, 1); hoveredNode = null; hideNodeInfo(); }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClick(event) {
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(nodeMeshes);
|
||||
if (intersects.length > 0) {
|
||||
const node = intersects[0].object;
|
||||
if (selectedNode === node) { selectedNode = null; document.getElementById('node-info').style.display = 'none'; }
|
||||
else { selectedNode = node; showNodeInfo(node.userData.nodeData, true); }
|
||||
}
|
||||
}
|
||||
|
||||
function showNodeInfo(nodeData, isClick = false) {
|
||||
document.getElementById('info-name').textContent = nodeData.name;
|
||||
document.getElementById('info-type').textContent = nodeData.type;
|
||||
document.getElementById('info-mentions').textContent = nodeData.mention_count;
|
||||
const linkCount = edges.filter(e => e.source === nodeData.id || e.target === nodeData.id).length;
|
||||
document.getElementById('info-links').textContent = linkCount;
|
||||
if (isClick) document.getElementById('node-info').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideNodeInfo() { if (!selectedNode) document.getElementById('node-info').style.display = 'none'; }
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
animationId = requestAnimationFrame(animate);
|
||||
const time = Date.now() * 0.001;
|
||||
highlightPulse = (highlightPulse + 0.02) % (Math.PI * 2);
|
||||
controls.update();
|
||||
if (starField) starField.rotation.y += 0.0001;
|
||||
if (hoveredNode) { const pulse = 1 + Math.sin(highlightPulse * 3) * 0.05; hoveredNode.scale.set(pulse * 1.2, pulse * 1.2, pulse * 1.2); }
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1 @@
|
||||
{"routerMap":[]}
|
||||
@ -0,0 +1 @@
|
||||
{"routerMap":[]}
|
||||
@ -0,0 +1 @@
|
||||
{"crossAppSharedConfig":[]}
|
||||
File diff suppressed because one or more lines are too long
89
entry/build/default/outputs/default/mapping/sourceMaps.map
Normal file
89
entry/build/default/outputs/default/mapping/sourceMaps.map
Normal file
File diff suppressed because one or more lines are too long
1
entry/build/default/outputs/default/pack.info
Normal file
1
entry/build/default/outputs/default/pack.info
Normal file
@ -0,0 +1 @@
|
||||
{"summary":{"app":{"bundleName":"com.trulymem.app","bundleType":"app","version":{"code":1000001,"name":"1.0.0"}},"modules":[{"mainAbility":"EntryAbility","deviceType":["phone","tablet","2in1"],"abilities":[{"name":"EntryAbility","label":"$string:EntryAbility_label"}],"distro":{"moduleType":"entry","installationFree":false,"deliveryWithInstall":true,"moduleName":"entry"},"apiVersion":{"compatible":23,"releaseType":"Release","target":23}}]},"packages":[{"deviceType":["phone","tablet","2in1"],"moduleType":"entry","deliveryWithInstall":true,"name":"entry-default"}]}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.example.rconmc",
|
||||
"debug": true,
|
||||
"versionCode": 1000000,
|
||||
"versionName": "1.0.0",
|
||||
"minAPIVersion": 60001021,
|
||||
"targetAPIVersion": 60001021,
|
||||
"apiReleaseType": "Release",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"compileSdkVersion": "6.0.2.130",
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug"
|
||||
},
|
||||
"module": {
|
||||
"name": "librcon",
|
||||
"type": "har",
|
||||
"requestPermissions": [],
|
||||
"deviceTypes": [
|
||||
"default",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"packageName": "librcon",
|
||||
"installationFree": false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.example.blockmanager",
|
||||
"debug": true,
|
||||
"versionCode": 1000001,
|
||||
"versionName": "1.0.1",
|
||||
"minAPIVersion": 60100023,
|
||||
"targetAPIVersion": 60100023,
|
||||
"apiReleaseType": "Release",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"compileSdkVersion": "6.1.0.105",
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug"
|
||||
},
|
||||
"module": {
|
||||
"name": "librcon",
|
||||
"type": "har",
|
||||
"requestPermissions": [],
|
||||
"deviceTypes": [
|
||||
"default",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"packageName": "librcon",
|
||||
"installationFree": false
|
||||
}
|
||||
}
|
||||
@ -300,7 +300,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.chat-msg .markdown-content p { margin: 4px 0; }
|
||||
.chat-msg .markdown-content code { background: rgba(255,255,255,0.1); padding: 1px 4px; border-radius: 3px; font-size: 12px; }
|
||||
.chat-msg .markdown-content pre { background: rgba(0,0,0,0.3); padding: 8px; border-radius: 4px; overflow-x: auto; margin: 6px 0; }
|
||||
.chat-msg .markdown-content pre code { background: none; padding: 0; }
|
||||
.chat-msg .markdown-content ul, .chat-msg .markdown-content ol { padding-left: 20px; margin: 4px 0; }
|
||||
.chat-msg .markdown-content h1, .chat-msg .markdown-content h2, .chat-msg .markdown-content h3, .chat-msg .markdown-content h4 { margin: 8px 0 4px; color: #aaccff; }
|
||||
.chat-msg .markdown-content blockquote { border-left: 3px solid #4488ff; padding-left: 10px; margin: 4px 0; color: #8899bb; }
|
||||
.chat-msg .markdown-content a { color: #4488ff; text-decoration: underline; }
|
||||
.chat-msg .markdown-content strong { color: #fff; }
|
||||
.chat-msg .markdown-content table { border-collapse: collapse; margin: 6px 0; width: 100%; }
|
||||
.chat-msg .markdown-content th, .chat-msg .markdown-content td { border: 1px solid rgba(100,100,255,0.3); padding: 4px 8px; text-align: left; }
|
||||
.chat-msg .markdown-content th { background: rgba(68,136,255,0.2); }
|
||||
.chat-msg .markdown-content hr { border: none; border-top: 1px solid rgba(100,100,255,0.2); margin: 8px 0; }
|
||||
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app-container">
|
||||
@ -312,6 +328,14 @@
|
||||
<p>节点: <span id="node-count">0</span></p>
|
||||
<p>边: <span id="edge-count">0</span></p>
|
||||
<p>状态: <span id="status">初始化中...</span></p>
|
||||
<p style="margin-top:8px;display:flex;align-items:center;gap:6px">
|
||||
<span style="color:#8888aa;font-size:12px">自动视角</span>
|
||||
<label style="position:relative;width:36px;height:20px;cursor:pointer;flex-shrink:0">
|
||||
<input type="checkbox" id="autoViewToggle" checked style="display:none">
|
||||
<span style="position:absolute;inset:0;background:rgba(60,60,80,0.8);border-radius:10px;transition:all 0.3s;border:1px solid rgba(100,100,255,0.2)"></span>
|
||||
<span style="position:absolute;width:16px;height:16px;left:2px;bottom:2px;background:#6666aa;border-radius:50%;transition:all 0.3s" class="auto-slider"></span>
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
<div id="node-info">
|
||||
<h3 id="info-name"></h3>
|
||||
@ -348,6 +372,17 @@
|
||||
<script>
|
||||
// Check authentication
|
||||
fetch('/api/check-auth').then(r=>r.json()).then(d=>{if(!d.authenticated)window.location.href='/login'}).catch(()=>window.location.href='/login')
|
||||
|
||||
function renderMarkdown(text) {
|
||||
if (!text) return '';
|
||||
try {
|
||||
const html = marked.parse(text);
|
||||
return DOMPurify.sanitize(html);
|
||||
} catch(e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
// 全局变量
|
||||
let scene, camera, renderer, controls;
|
||||
let nodes = [], edges = [];
|
||||
@ -356,6 +391,7 @@
|
||||
let raycaster, mouse;
|
||||
let hoveredNode = null, selectedNode = null;
|
||||
let animationId;
|
||||
let autoViewEnabled = true;
|
||||
let highlightPulse = 0;
|
||||
let lastHighlightCheck = 0;
|
||||
let currentHighlightIds = [];
|
||||
@ -450,6 +486,12 @@ function init() {
|
||||
}
|
||||
|
||||
// 创建星空背景
|
||||
|
||||
// 自动视角开关事件
|
||||
document.getElementById("autoViewToggle").addEventListener("change", function() {
|
||||
autoViewEnabled = this.checked;
|
||||
});
|
||||
|
||||
function createStarField() {
|
||||
const starCount = 3000;
|
||||
const positions = new Float32Array(starCount * 3);
|
||||
@ -847,7 +889,14 @@ function init() {
|
||||
});
|
||||
// 相机距离:节点范围加 20u 余量,但不超过 120u,不低于 25u
|
||||
const targetDist = Math.min(Math.max(maxDist + 15, 25), 120);
|
||||
// 如果有待聚焦节点且自动视角开启,飞过去;否则全局定位
|
||||
if (_pendingFocusNodeId) {
|
||||
const focusId = _pendingFocusNodeId;
|
||||
_pendingFocusNodeId = null;
|
||||
setTimeout(() => flyToNode(focusId, 800), 100);
|
||||
} else {
|
||||
camera.position.set(targetDist * 0.9, targetDist * 0.6, targetDist * 0.9);
|
||||
}
|
||||
controls.target.set(0, 0, 0);
|
||||
controls.update();
|
||||
}
|
||||
@ -972,6 +1021,7 @@ function init() {
|
||||
}
|
||||
|
||||
// 已消费的高亮变更ID(防止重复触发)
|
||||
let _pendingFocusNodeId = null;
|
||||
let _consumedHighlightIds = new Set();
|
||||
|
||||
// 平滑重新定位剩余的节点
|
||||
@ -1082,6 +1132,20 @@ function smoothReposition() {
|
||||
// 自适应相机
|
||||
function fitCameraToGraph() {
|
||||
if (nodeMeshes.length === 0) return;
|
||||
if (!autoViewEnabled) {
|
||||
// 关闭自动视角时直接跳转
|
||||
let maxDist = 0;
|
||||
nodeMeshes.forEach(m => {
|
||||
const d = m.position.length();
|
||||
if (d > maxDist) maxDist = d;
|
||||
});
|
||||
if (maxDist < 1) maxDist = 30;
|
||||
const targetDist = Math.min(Math.max(maxDist + 15, 25), 120);
|
||||
camera.position.set(targetDist * 0.9, targetDist * 0.6, targetDist * 0.9);
|
||||
controls.target.set(0, 0, 0);
|
||||
controls.update();
|
||||
return;
|
||||
}
|
||||
let maxDist = 0;
|
||||
nodeMeshes.forEach(m => {
|
||||
const d = m.position.length();
|
||||
@ -1105,6 +1169,28 @@ function smoothReposition() {
|
||||
lerpCamera();
|
||||
}
|
||||
|
||||
// 飞向指定节点(自动视角)
|
||||
function flyToNode(nodeId, duration = 600) {
|
||||
if (!autoViewEnabled) return;
|
||||
const mesh = nodeMeshes.find(m => m.userData.nodeId === nodeId);
|
||||
if (!mesh) return;
|
||||
const targetPos = mesh.position.clone();
|
||||
const startPos = camera.position.clone();
|
||||
const startTarget = controls.target.clone();
|
||||
const distance = targetPos.length() + 25;
|
||||
const endCamPos = new THREE.Vector3(targetPos.x, targetPos.y + distance * 0.4, targetPos.z + distance * 0.8);
|
||||
const startTime = Date.now();
|
||||
function lerp() {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const t = Math.min(elapsed / duration, 1);
|
||||
const ease = 1 - Math.pow(1 - t, 3);
|
||||
camera.position.lerpVectors(startPos, endCamPos, ease);
|
||||
controls.target.lerpVectors(startTarget, targetPos, ease);
|
||||
if (t < 1) requestAnimationFrame(lerp);
|
||||
}
|
||||
lerp();
|
||||
}
|
||||
|
||||
// 检查高亮 — 增量更新,不再全量 reload
|
||||
async function checkHighlight() {
|
||||
try {
|
||||
@ -1132,6 +1218,7 @@ function smoothReposition() {
|
||||
if (data.new_node_id && !_consumedHighlightIds.has('new:' + data.new_node_id)) {
|
||||
_consumedHighlightIds.add('new:' + data.new_node_id);
|
||||
// 新节点不在当前场景中,重新加载完整图
|
||||
_pendingFocusNodeId = data.new_node_id;
|
||||
loadGraphData();
|
||||
setTimeout(showDebugInfo, 500);
|
||||
return;
|
||||
@ -1573,7 +1660,10 @@ function smoothReposition() {
|
||||
if (parsed.content) displayText = parsed.content;
|
||||
} catch(e) {}
|
||||
|
||||
div.textContent = displayText;
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.className = 'markdown-content';
|
||||
contentDiv.innerHTML = renderMarkdown(displayText);
|
||||
div.appendChild(contentDiv);
|
||||
const time = document.createElement('div');
|
||||
time.className = 'chat-msg-time';
|
||||
time.textContent = new Date().toLocaleTimeString();
|
||||
@ -1620,7 +1710,7 @@ function smoothReposition() {
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
const reply = data.data?.content || '(无回复)';
|
||||
const reply = data.content || data.data?.content || '(无回复)';
|
||||
addChatMessage('assistant', reply);
|
||||
chatStatus.textContent = '🟢 已连接';
|
||||
chatStatus.className = 'chat-status';
|
||||
|
||||
@ -514,7 +514,24 @@ body {
|
||||
white-space: pre-wrap;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.message-content h1,.message-content h2,.message-content h3{color:#e94560;margin:8px 0 4px}
|
||||
.message-content p{margin:4px 0}
|
||||
.message-content ul,.message-content ol{margin:4px 0 4px 20px}
|
||||
.message-content code{background:#12121f;color:#ffd700;padding:1px 4px;border-radius:3px;font-size:0.9em}
|
||||
.message-content pre{background:#12121f;padding:8px;border-radius:4px;overflow-x:auto;margin:8px 0;border-left:3px solid #e94560}
|
||||
.message-content pre code{background:none;color:#eee;padding:0}
|
||||
.message-content blockquote{border-left:3px solid #0f3460;padding-left:8px;margin:8px 0;color:#888}
|
||||
.message-content a{color:#4169e1;text-decoration:none}
|
||||
.message-content a:hover{text-decoration:underline}
|
||||
.message-content table{border-collapse:collapse;margin:8px 0;width:100%}
|
||||
.message-content th,.message-content td{border:1px solid #0f3460;padding:4px 8px}
|
||||
.message-content th{background:#12121f;color:#e94560}
|
||||
.message-content img{max-width:100%;border-radius:4px}
|
||||
.message-content hr{border:none;border-top:1px solid #0f3460;margin:8px 0}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -654,6 +671,15 @@ async function init() {
|
||||
addWelcomeMessage();
|
||||
}
|
||||
|
||||
// Markdown Rendering
|
||||
function renderMarkdown(text) {
|
||||
if (typeof marked !== 'undefined' && typeof DOMPurify !== 'undefined') {
|
||||
marked.setOptions({ breaks: true, gfm: true });
|
||||
return DOMPurify.sanitize(marked.parse(text));
|
||||
}
|
||||
return text.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
// API Functions
|
||||
async function apiRequest(endpoint, method = 'GET', data = null) {
|
||||
const options = {
|
||||
@ -739,7 +765,7 @@ function addMessageToUI(role, content, toolCalls = null) {
|
||||
|
||||
const contentEl = document.createElement('div');
|
||||
contentEl.className = 'message-content';
|
||||
contentEl.textContent = content;
|
||||
contentEl.innerHTML = marked.parse(content);
|
||||
|
||||
widget.appendChild(header);
|
||||
widget.appendChild(contentEl);
|
||||
@ -790,7 +816,7 @@ function updateLatestMessage(content, toolCalls = null) {
|
||||
const latest = widgets[widgets.length - 1];
|
||||
const contentEl = latest.querySelector('.message-content');
|
||||
if (contentEl) {
|
||||
contentEl.textContent = content;
|
||||
contentEl.innerHTML = marked.parse(content);
|
||||
}
|
||||
|
||||
// Add tool calls if present
|
||||
|
||||
@ -189,6 +189,25 @@
|
||||
<div class="settings-container">
|
||||
<h1 class="settings-title"><i class="fas fa-cog"></i> 设置</h1>
|
||||
|
||||
<!-- API 配置 -->
|
||||
<div class="section-title"><i class="fas fa-plug"></i> API 配置</div>
|
||||
<form id="apiConfigForm">
|
||||
<div class="form-group">
|
||||
<label for="settings_api_key">API Key</label>
|
||||
<input type="password" id="settings_api_key" placeholder="输入 API Key">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="settings_base_url">Base URL</label>
|
||||
<input type="text" id="settings_base_url" placeholder="https://api.deepseek.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="settings_model">Model</label>
|
||||
<input type="text" id="settings_model" placeholder="deepseek-chat">
|
||||
</div>
|
||||
<button type="submit" class="btn" id="saveApiConfigBtn">保 存 API 配 置</button>
|
||||
</form>
|
||||
<div id="apiConfigStatus" style="text-align:center;font-size:12px;color:#666688;margin-top:5px;"></div>
|
||||
|
||||
<!-- 修改密码 -->
|
||||
<div class="section-title"><i class="fas fa-key"></i> 修改密码</div>
|
||||
<form id="passwordForm">
|
||||
@ -451,6 +470,65 @@
|
||||
|
||||
init();
|
||||
|
||||
// API 配置加载与保存
|
||||
async function loadApiConfig() {
|
||||
try {
|
||||
const resp = await fetch('/api/settings');
|
||||
const data = await resp.json();
|
||||
if (data.success && data.data) {
|
||||
const cfg = data.data.api_config || {};
|
||||
document.getElementById('settings_api_key').value = cfg.api_key || '';
|
||||
document.getElementById('settings_base_url').value = cfg.base_url || 'https://api.deepseek.com';
|
||||
document.getElementById('settings_model').value = cfg.model || 'deepseek-chat';
|
||||
document.getElementById('apiConfigStatus').textContent = '✅ 配置已加载';
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('apiConfigStatus').textContent = '⚠️ 无法加载配置';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('apiConfigForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const btn = document.getElementById('saveApiConfigBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '保存中...';
|
||||
const statusEl = document.getElementById('apiConfigStatus');
|
||||
statusEl.textContent = '🔄 保存中...';
|
||||
|
||||
try {
|
||||
const resp = await fetch('/api/settings', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
api_config: {
|
||||
api_key: document.getElementById('settings_api_key').value,
|
||||
base_url: document.getElementById('settings_base_url').value,
|
||||
model: document.getElementById('settings_model').value
|
||||
}
|
||||
})
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
statusEl.textContent = '✅ API 配置已保存并生效';
|
||||
setTimeout(() => { statusEl.textContent = ''; }, 3000);
|
||||
} else {
|
||||
statusEl.textContent = '⚠️ 保存失败: ' + (data.error || '未知错误');
|
||||
}
|
||||
} catch (e) {
|
||||
statusEl.textContent = '⚠️ 网络错误';
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '保 存 API 配 置';
|
||||
}
|
||||
});
|
||||
|
||||
// 补充 loadConfig 中调用 loadApiConfig
|
||||
const origLoadConfig = loadConfig;
|
||||
loadConfig = function() {
|
||||
origLoadConfig();
|
||||
loadApiConfig();
|
||||
};
|
||||
|
||||
// TUI 开关
|
||||
document.getElementById('enableTuiToggle').addEventListener('change', async function() {
|
||||
const enable = this.checked;
|
||||
|
||||
Reference in New Issue
Block a user