@@ -7,10 +7,11 @@ const SIDEBAR_TREE_JS: &str = r##"
if ( window . __mnoteSidebarTreeRuntimeStarted ) return ;
window . __mnoteSidebarTreeRuntimeStarted = true ;
var PAGE_AI_SESSION_STORAGE_VERSION = 3 ;
var PAGE_DRAG_MIME = ' application / x - mnote - page - tree - node ' ;
var FILETREE_DRAG_MIME = ' application / x - mnote - filetree - row - ids ' ;
var MNOTE_SIDEBAR_TREE_MODE_KEY = ' mnote . sidebar . tree . mode ' ;
var MNOTE_RECENT_LOCAL_ROOTS_KEY = ' mnote . localFolder . recentRoots ' ;
var MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX = ' mnote . localFolder . recentRoots : ' ;
var MNOTE_LAST_CLOUD_WORKSPACE_KEY = ' mnote . workspace . lastCloudWorkspaceId ' ;
var MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY = ' mnote . global . showHeadingNumbers ' ;
var mnoteNavigationInFlight = ' ' ;
@@ -39,7 +40,7 @@ const SIDEBAR_TREE_JS: &str = r##"
pageAiPage : ' chat ' ,
pageAiRunStatus : ' idle ' ,
pageAiCurrentRunId : ' ' ,
pageAiAcpRuntime : ' ' ,
pageAiAcpRuntime : ' reasonix ' ,
pageAiAcpRuntimes : [ ] ,
pageAiQueueLength : 0 ,
pageAiQueuedItems : [ ] ,
@@ -618,7 +619,22 @@ const SIDEBAR_TREE_JS: &str = r##"
}
function currentSourceKind ( ) {
return ( new URLSearchParams ( window . location . search ) . get ( ' sourceKind ' ) | | ' convex_workspace ' ) . trim ( ) | | ' convex_workspace ' ;
var params = new URLSearchParams ( window . location . search ) ;
var fromUrl = ( params . get ( ' sourceKind ' ) | | ' ' ) . trim ( ) ;
if ( fromUrl ) return fromUrl ;
if (
window . location . pathname = = = '/' & &
! ( params . get ( ' workspaceId ' ) | | ' ' ) . trim ( ) & &
! ( params . get ( ' rootUri ' ) | | ' ' ) . trim ( ) & &
! ( params . get ( ' pageId ' ) | | ' ' ) . trim ( )
) {
return ' local_folder ' ;
}
return ' convex_workspace ' ;
}
function currentRootUri ( ) {
return ( new URLSearchParams ( window . location . search ) . get ( ' rootUri ' ) | | ' ' ) . trim ( ) ;
}
function rememberCloudWorkspaceId ( workspaceId ) {
@@ -680,9 +696,22 @@ const SIDEBAR_TREE_JS: &str = r##"
return payload ;
}
function currentActorStorageId ( ) {
var fromBody = document . body instanceof HTMLElement ? ( document . body . getAttribute ( ' data - mnote - actor - id ' ) | | ' ' ) . trim ( ) : ' ' ;
if ( fromBody & & fromBody ! = = ' anonymous ' ) return fromBody ;
return ' ' ;
}
function recentLocalRootsStorageKey ( ) {
var actorId = currentActorStorageId ( ) ;
return actorId ? MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX + encodeURIComponent ( actorId ) : ' ' ;
}
function readRecentLocalRoots ( ) {
try {
var raw = window . localStorage ? window . localStorage . getItem ( MNOTE_RECENT_LOCAL_ROOTS_KEY ) : ' ' ;
var storageKey = recentLocalRootsStorageKey ( ) ;
if ( ! storageKey ) return [ ] ;
var raw = window . localStorage ? window . localStorage . getItem ( storageKey ) : ' ' ;
var parsed = raw ? JSON . parse ( raw ) : [ ] ;
return Array . isArray ( parsed ) ? parsed . filter ( function ( value ) {
return typeof value = = = ' string ' & & value . trim ( ) ;
@@ -695,9 +724,11 @@ const SIDEBAR_TREE_JS: &str = r##"
function rememberLocalRoot ( rootUri ) {
try {
if ( ! window . localStorage ) return ;
var storageKey = recentLocalRootsStorageKey ( ) ;
if ( ! storageKey ) return ;
var roots = readRecentLocalRoots ( ) . filter ( function ( value ) { return value ! = = rootUri ; } ) ;
roots . unshift ( rootUri ) ;
window . localStorage . setItem ( MNOTE_RECENT_LOCAL_ROOTS_KEY , JSON . stringify ( roots . slice ( 0 , 10 ) ) ) ;
window . localStorage . setItem ( storageKey , JSON . stringify ( roots . slice ( 0 , 10 ) ) ) ;
} catch ( _ ) { }
}
@@ -1036,6 +1067,47 @@ const SIDEBAR_TREE_JS: &str = r##"
openLocalFolderDialog ( ' ' ) ;
}
async function createDefaultLocalWorkspace ( trigger ) {
setCommandPending ( trigger , true ) ;
try {
var response = await fetch ( ' / api / local - folder / workspaces / default ' , {
method : ' POST ' ,
headers : { ' content - type ' : ' application / json ' } ,
body : ' { } '
} ) ;
var payload = await response . json ( ) . catch ( function ( ) { return null ; } ) ;
if ( ! response . ok ) {
throw new Error ( ( payload & & payload . message ) | | ( ' local_workspace_create_failed_ ' + response . status ) ) ;
}
var workspace = payload & & payload . workspace | | { } ;
var rootUri = String ( workspace . rootUri | | ' ' ) . trim ( ) ;
if ( ! rootUri ) throw new Error ( ' local_workspace_create_missing_root_uri ' ) ;
openLocalFolderRoot ( rootUri ) ;
} catch ( error ) {
var status = document . querySelector ( ' [ data - testid = " mnote-local-folder-status " ] ' ) ;
if ( status instanceof HTMLElement ) {
status . textContent = error & & error . message ? error . message : String ( error ) ;
} else {
openLocalFolderDialog ( error & & error . message ? error . message : String ( error ) ) ;
}
} finally {
setCommandPending ( trigger , false ) ;
}
}
function autoOpenRecentLocalRootOnHome ( ) {
if ( window . location . pathname ! = = '/' ) return false ;
var params = new URLSearchParams ( window . location . search ) ;
if ( ( params . get ( ' sourceKind ' ) | | ' ' ) . trim ( ) ) return false ;
if ( ( params . get ( ' rootUri ' ) | | ' ' ) . trim ( ) ) return false ;
if ( ( params . get ( ' workspaceId ' ) | | ' ' ) . trim ( ) ) return false ;
if ( ( params . get ( ' pageId ' ) | | ' ' ) . trim ( ) ) return false ;
var recent = readRecentLocalRoots ( ) ;
if ( ! recent . length ) return false ;
openLocalFolderRoot ( recent [ 0 ] ) ;
return true ;
}
function closeWorkspaceSourceMenu ( ) {
var existing = document . querySelector ( ' [ data - testid = " mnote-workspace-source-menu " ] ' ) ;
if ( existing & & existing . parentElement ) existing . parentElement . removeChild ( existing ) ;
@@ -1109,6 +1181,8 @@ const SIDEBAR_TREE_JS: &str = r##"
trigger . closest ( ' [ data - testid = " wolai-workspace-identity " ] ' ) ? . appendChild ( menu ) ;
}
autoOpenRecentLocalRootOnHome ( ) ;
function setCommandPending ( trigger , pending ) {
if ( ! ( trigger instanceof HTMLElement ) ) return ;
trigger . setAttribute ( ' data - pending ' , pending ? ' true ' : ' false ' ) ;
@@ -1970,6 +2044,21 @@ const SIDEBAR_TREE_JS: &str = r##"
return assetId . indexOf ( ' mindmap_ ' ) = = = 0 | | assetId . indexOf ( ' mindmap - ' ) = = = 0 ;
}
function localFilePathFromAssetId ( assetId ) {
var value = String ( assetId | | ' ' ) . trim ( ) ;
return value . indexOf ( ' local - file :' ) = = = 0 ? value . slice ( ' local - file :' . length ) : ' ' ;
}
function buildLocalFileOpenUrl ( relativePath , download ) {
var rootUri = currentRootUri ( ) ;
if ( ! rootUri | | ! relativePath ) return ' ' ;
var url = new URL ( ' / api / local - folder / files / open ' , window . location . origin ) ;
url . searchParams . set ( ' rootUri ' , rootUri ) ;
url . searchParams . set ( ' path ' , relativePath ) ;
if ( download ) url . searchParams . set ( ' download ' , ' true ' ) ;
return url . toString ( ) ;
}
function readFileTreeObjectIdentity ( row ) {
if ( ! row ) return null ;
var raw = row . getAttribute ( ' data - object - identity ' ) | | ' ' ;
@@ -1999,6 +2088,12 @@ const SIDEBAR_TREE_JS: &str = r##"
async function openConvexAssetFromFileTree ( detail ) {
var assetId = String ( detail & & detail . assetId | | ' ' ) . trim ( ) ;
if ( ! assetId ) return ;
var localFilePath = localFilePathFromAssetId ( assetId ) ;
if ( localFilePath ) {
var localFileUrl = buildLocalFileOpenUrl ( localFilePath , false ) ;
if ( localFileUrl ) window . open ( localFileUrl , '_ blank ' , ' noopener , noreferrer ' ) ;
return ;
}
var documentId = String ( detail & & detail . documentId | | ' ' ) . trim ( ) ;
if ( isMindmapAssetDetail ( detail ) & & documentId ) {
document . documentElement . setAttribute ( ' data - mnote - last - mindmap - asset - open - mode ' , ' mindmap - object - shell ' ) ;
@@ -2133,13 +2228,18 @@ const SIDEBAR_TREE_JS: &str = r##"
}
function uploadedAssetUrl ( asset ) {
return String ( asset & & ( asset . file_url | | asset . signedUrl | | asset . signed_url | | asset . thumbnail_url ) | | ' ' ) . trim ( ) ;
return String ( asset & & ( asset . sourcePath | | asset . file_url | | asset . signedUrl | | asset . signed_url | | asset . thumbnail_url ) | | ' ' ) . trim ( ) ;
}
function uploadedAssetType ( asset ) {
return String ( asset & & ( asset . asset_type | | asset . assetType | | asset . mime_type | | ' ' ) | | ' ' ) . trim ( ) ;
}
function isLocalUploadedAsset ( asset ) {
var id = String ( asset & & asset . id | | ' ' ) . trim ( ) ;
return String ( asset & & asset . sourceKind | | ' ' ) . trim ( ) = = = ' local_folder ' | | id . indexOf ( ' local :asset :' ) = = = 0 ;
}
function uploadedAssetExtension ( asset ) {
var match = uploadedAssetTitle ( asset ) . toLowerCase ( ) . match ( / \ . ( [ a - z0 - 9 ] + ) $ / ) ;
return match ? match [ 1 ] : ' ' ;
@@ -2224,6 +2324,7 @@ const SIDEBAR_TREE_JS: &str = r##"
}
function buildOnlyOfficeAssetOpenUrl ( asset , userId ) {
if ( isLocalUploadedAsset ( asset ) ) return ' ' ;
var title = uploadedAssetTitle ( asset ) ;
var fileType = inferOnlyOfficeFileType ( title , asset & & asset . mime_type ) ;
if ( ! fileType ) return ' ' ;
@@ -2591,8 +2692,9 @@ const SIDEBAR_TREE_JS: &str = r##"
if ( type = = = ' image ' & & url ) {
return editor . chain ( ) . focus ( ) . setImage ( { src : url , alt : title , title : title } ) . run ( ) = = = true ;
}
var isLocalAsset = isLocalUploadedAsset ( asset ) ;
var userId = ' ' ;
var onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl ( asset , userId ) ;
var onlyOfficeUrl = isLocalAsset ? ' ' : buildOnlyOfficeAssetOpenUrl ( asset , userId ) ;
if ( onlyOfficeUrl & & assetId ) {
userId = await fetchCurrentOnlyOfficeUserId ( ) ;
onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl ( asset , userId ) ;
@@ -2647,6 +2749,35 @@ const SIDEBAR_TREE_JS: &str = r##"
}
async function uploadFileToMediaAsset ( file , plan , options ) {
if ( currentSourceKind ( ) = = = ' local_folder ' ) {
var rootUri = ( new URLSearchParams ( window . location . search ) . get ( ' rootUri ' ) | | ' ' ) . trim ( ) ;
var documentId = String ( plan & & plan . targetDocumentId | | currentDocumentId ( ) | | ' ' ) . trim ( ) ;
if ( ! rootUri | | ! documentId ) {
throw new Error ( ' 本 地 Markdown 上 传 缺 少 rootUri 或 documentId ' ) ;
}
var localForm = new FormData ( ) ;
localForm . append ( ' file ' , file ) ;
localForm . append ( ' rootUri ' , rootUri ) ;
localForm . append ( ' documentId ' , documentId ) ;
localForm . append ( ' kind ' , file & & String ( file . type | | ' ' ) . indexOf ( ' image / ' ) = = = 0 ? ' image ' : ' attachment ' ) ;
var localResponse = await fetch ( ' / api / local - folder / assets / upload ' , {
method : ' POST ' ,
credentials : ' include ' ,
body : localForm
} ) ;
var localPayload = await localResponse . json ( ) . catch ( function ( ) { return null ; } ) ;
if ( ! localResponse . ok | | ! localPayload | | ! localPayload . asset ) {
throw new Error ( localPayload & & localPayload . error ? localPayload . error : ' 上 传 失 败 ' ) ;
}
if ( options & & options . insertIntoEditor ) {
await insertUploadedAssetIntoEditor ( localPayload . asset ) ;
}
void refreshLocalFolderSidebarSnapshot ( ) ;
window . dispatchEvent ( new CustomEvent ( ' wolai :local - assets - changed ' , {
detail : { docId : documentId , asset : localPayload . asset , assetIds : [ localPayload . asset . id ] }
} ) ) ;
return localPayload . asset ;
}
var form = new FormData ( ) ;
form . append ( ' file ' , file ) ;
form . append ( ' workspaceId ' , plan . workspaceId ) ;
@@ -3951,6 +4082,8 @@ const SIDEBAR_TREE_JS: &str = r##"
params . set ( ' workspaceId ' , resolveWorkspaceId ( document . body ) ) ;
params . set ( ' documentId ' , currentDocumentId ( ) ) ;
params . set ( ' profile ' , pageAiRunProfile ( ) ) ;
params . set ( ' sourceKind ' , currentSourceKind ( ) ) ;
if ( currentRootUri ( ) ) params . set ( ' rootUri ' , currentRootUri ( ) ) ;
Object . keys ( extra | | { } ) . forEach ( function ( key ) {
var value = extra [ key ] ;
if ( value ! = = undefined & & value ! = = null & & String ( value ) . trim ( ) ! = = ' ' ) {
@@ -3966,7 +4099,7 @@ const SIDEBAR_TREE_JS: &str = r##"
id : ' sess_ ' + now + '_' + Math . random ( ) . toString ( 16 ) . slice ( 2 , 8 ) ,
title : title | | ' 新 会 话 ' ,
profile : pageAiCurrentProfile ( ) ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' ' ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' reasonix ' ,
createdAt : now ,
updatedAt : now ,
source : ' local ' ,
@@ -4093,14 +4226,20 @@ const SIDEBAR_TREE_JS: &str = r##"
return ( Array . isArray ( sessions ) ? sessions : [ ] )
. slice ( 0 , 20 )
. map ( function ( session ) {
var sessionStorage = String ( session & & ( session . sessionStorage | | session . session_storage ) | | ' ' ) . trim ( ) ;
var persistence = String ( session & & session . persistence | | ' ' ) . trim ( ) ;
return {
id : String ( session & & session . id | | ' ' ) . trim ( ) | | pageAiNewSession ( ) . id ,
title : String ( session & & session . title | | ' ' ) . trim ( ) | | ' 新 会 话 ' ,
profile : String ( session & & session . profile | | pageAiCurrentProfile ( ) ) . trim ( ) | | ' default ' ,
acpRuntime : String ( session & & session . acpRuntime | | ' ' ) . trim ( ) ,
acpRuntime : String ( session & & session . acpRuntime | | ' reasonix ' ) . trim ( ) ,
createdAt : pageAiTimestamp ( session & & session . createdAt ) ,
updatedAt : pageAiTimestamp ( session & & session . updatedAt ) ,
source : String ( session & & session . source | | ' local ' ) . trim ( ) | | ' local ' ,
persistence : persistence ,
sessionStorage : sessionStorage ,
permissionLevel : String ( session & & ( session . permissionLevel | | session . permission_level ) | | ' ' ) . trim ( ) ,
shareId : String ( session & & ( session . shareId | | session . share_id ) | | ' ' ) . trim ( ) ,
runId : String ( session & & ( session . runId | | session . run_id ) | | ' ' ) . trim ( ) ,
status : String ( session & & session . status | | ' ' ) . trim ( ) ,
usage : session & & session . usage & & typeof session . usage = = = ' object ' ? session . usage : null ,
@@ -4120,6 +4259,10 @@ const SIDEBAR_TREE_JS: &str = r##"
if ( ! sessionId ) return null ;
var title = String ( row . title | | payload . title | | payload . message | | ' ' ) . trim ( ) ;
if ( title . length > 28 ) title = title . slice ( 0 , 28 ) + '…' ;
var persistence = String ( row . persistence | | payload . persistence | | ' ' ) . trim ( ) ;
var sessionStorage = String ( row . sessionStorage | | row . session_storage | | payload . sessionStorage | | ' ' ) . trim ( ) ;
if ( ! sessionStorage & & persistence = = = ' convex_acp_runtime_store ' ) sessionStorage = ' cloud ' ;
if ( ! sessionStorage & & persistence = = = ' local_ai_session_jsonl ' ) sessionStorage = String ( row . shareId | | payload . shareId | | ' ' ) . trim ( ) ? ' local_shared ' : ' local_private ' ;
return {
id : sessionId ,
title : title | | ' 当 前 页 问 答 ' ,
@@ -4127,7 +4270,11 @@ const SIDEBAR_TREE_JS: &str = r##"
acpRuntime : String ( row . acpRuntime | | row . acp_runtime | | payload . acpRuntime | | ' reasonix ' ) . trim ( ) ,
createdAt : pageAiTimestamp ( row . createdAt | | row . created_at ) ,
updatedAt : pageAiTimestamp ( row . updatedAt | | row . updated_at ) ,
source : ' acp ' ,
source : sessionStorage = = = ' local_private ' | | sessionStorage = = = ' local_shared ' ? ' local ' : ' acp ' ,
persistence : persistence ,
sessionStorage : sessionStorage ,
permissionLevel : String ( row . permissionLevel | | row . permission_level | | payload . permissionLevel | | ' ' ) . trim ( ) ,
shareId : String ( row . shareId | | row . share_id | | payload . shareId | | ' ' ) . trim ( ) ,
runId : String ( row . runId | | row . run_id | | ' ' ) . trim ( ) ,
status : String ( row . status | | ( row . runtime & & row . runtime . status ) | | ' ' ) . trim ( ) ,
usage : row . usage & & typeof row . usage = = = ' object ' ? row . usage : null ,
@@ -4150,6 +4297,16 @@ const SIDEBAR_TREE_JS: &str = r##"
return pageAiNormalizeSessions ( Object . keys ( byId ) . map ( function ( id ) { return byId [ id ] ; } ) ) ;
}
function pageAiSessionStorageLabel ( session ) {
var storage = String ( session & & ( session . sessionStorage | | session . session_storage ) | | ' ' ) . trim ( ) ;
var persistence = String ( session & & session . persistence | | ' ' ) . trim ( ) ;
if ( storage = = = ' local_shared ' ) return ' 共 享 会 话 ' ;
if ( storage = = = ' local_private ' ) return ' 本 地 私 有 ' ;
if ( storage = = = ' cloud ' | | persistence = = = ' convex_acp_runtime_store ' ) return ' 云 端 会 话 ' ;
if ( persistence = = = ' local_ai_session_jsonl ' ) return ' 本 地 私 有 ' ;
return String ( session & & session . source | | ' ' ) . trim ( ) = = = ' acp ' ? ' 云 端 会 话 ' : ' 本 地 私 有 ' ;
}
function pageAiLoadSessions ( ) {
if ( pageUiState . pageAiSessions . length & & pageUiState . pageAiActiveSessionId ) return ;
try {
@@ -4159,14 +4316,15 @@ const SIDEBAR_TREE_JS: &str = r##"
var activeProfile = String ( parsed & & parsed . activeProfileName | | ' ' ) . trim ( ) ;
var activeAcpRuntime = String ( parsed & & parsed . activeAcpRuntime | | ' ' ) . trim ( ) ;
var sessions = pageAiNormalizeSessions ( parsed & & parsed . sessions ) ;
if ( activeAcpRuntime ) pageUiState . pageAiAcpRuntime = activeAcpRuntime ;
var storageVersion = Number ( parsed & & parsed . version | | 0 ) ;
if ( storageVersion > = PAGE_AI_SESSION_STORAGE_VERSION & & activeAcpRuntime ) pageUiState . pageAiAcpRuntime = activeAcpRuntime ;
if ( activeProfile ) pageAiSetActiveProfile ( activeProfile ) ;
if ( sessions . length ) {
pageUiState . pageAiSessions = sessions ;
var activeSession = sessions . find ( function ( session ) { return session . id = = = activeId ; } ) | | sessions [ 0 ] ;
pageUiState . pageAiActiveSessionId = activeSession . id ;
if ( activeSession . profile ) pageAiSetActiveProfile ( activeSession . profile ) ;
if ( activeSession . acpRuntime ) pageUiState . pageAiAcpRuntime = activeSession . acpRuntime ;
if ( ! pageUiState . pageAiAcpRuntime & & activeSession . acpRuntime ) pageUiState . pageAiAcpRuntime = activeSession . acpRuntime ;
pageUiState . pageAiMessages = Array . isArray ( activeSession . messages ) ? activeSession . messages . slice ( ) : [ ] ;
return ;
}
@@ -4201,7 +4359,7 @@ const SIDEBAR_TREE_JS: &str = r##"
var active = pageAiCurrentSession ( ) ;
if ( active ) {
if ( active . profile ) pageAiSetActiveProfile ( active . profile ) ;
if ( active . acpRuntime ) pageUiState . pageAiAcpRuntime = active . acpRuntime ;
if ( ! pageUiState . pageAiAcpRuntime & & active . acpRuntime ) pageUiState . pageAiAcpRuntime = active . acpRuntime ;
pageUiState . pageAiMessages = Array . isArray ( active . messages ) ? active . messages . slice ( ) : pageUiState . pageAiMessages ;
}
pageUiState . pageAiSessionError = ' ' ;
@@ -4319,10 +4477,10 @@ const SIDEBAR_TREE_JS: &str = r##"
try {
pageAiSyncCurrentSessionMessages ( ) ;
window . localStorage . setItem ( pageAiStorageKey ( ) , JSON . stringify ( {
version : 2 ,
version : PAGE_AI_SESSION_STORAGE_VERSION ,
activeSessionId : pageUiState . pageAiActiveSessionId ,
activeProfileName : pageAiCurrentProfile ( ) ,
activeAcpRuntime : pageUiState . pageAiAcpRuntime | | ' ' ,
activeAcpRuntime : pageUiState . pageAiAcpRuntime | | ' reasonix ' ,
sessions : pageAiNormalizeSessions ( pageUiState . pageAiSessions )
} ) ) ;
} catch ( _ ) { }
@@ -4338,6 +4496,8 @@ const SIDEBAR_TREE_JS: &str = r##"
body : JSON . stringify ( {
workspaceId : resolveWorkspaceId ( document . body ) ,
documentId : currentDocumentId ( ) ,
sourceKind : currentSourceKind ( ) ,
rootUri : currentRootUri ( ) ,
traceId : ' page - ai - ' + Date . now ( ) . toString ( 36 ) ,
profile : pageAiCurrentProfile ( ) ,
title : current & & current . title ? current . title : ' 当 前 页 问 答 '
@@ -4351,7 +4511,11 @@ const SIDEBAR_TREE_JS: &str = r##"
id : String ( payload . sessionId | | ' ' ) . trim ( ) ,
title : String ( payload . title | | ' 当 前 页 问 答 ' ) ,
profile : String ( payload . profile | | pageAiCurrentProfile ( ) ) . trim ( ) | | ' default ' ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' ' ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' reasonix ' ,
persistence : String ( payload . persistence | | ' ' ) . trim ( ) ,
sessionStorage : String ( payload . sessionStorage | | ' ' ) . trim ( ) ,
permissionLevel : String ( payload . permissionLevel | | ' ' ) . trim ( ) ,
shareId : String ( payload . shareId | | ' ' ) . trim ( ) ,
createdAt : Date . now ( ) ,
updatedAt : Date . now ( ) ,
messages : pageUiState . pageAiMessages . slice ( )
@@ -4411,7 +4575,7 @@ const SIDEBAR_TREE_JS: &str = r##"
if ( ! session ) return ;
session . messages = Array . isArray ( pageUiState . pageAiMessages ) ? pageUiState . pageAiMessages . slice ( - 300 ) : [ ] ;
session . profile = pageAiCurrentProfile ( ) ;
session . acpRuntime = pageUiState . pageAiAcpRuntime | | ' ' ;
session . acpRuntime = pageUiState . pageAiAcpRuntime | | ' reasonix ' ;
session . updatedAt = Date . now ( ) ;
}
@@ -4518,6 +4682,36 @@ const SIDEBAR_TREE_JS: &str = r##"
return Array . isArray ( value ) ? value : [ ] ;
}
function pageAiDefaultAcpRuntimes ( ) {
return [
{
name : ' reasonix ' ,
title : ' ACP · Reasonix ' ,
description : ' 通 过 ACP 协 议 直 连 Reasonix ( DeepSeek 缓 存 优 先 ) ' ,
model : ' deepseek - chat ' ,
preset : ' auto '
} ,
{
name : ' hermes ' ,
title : ' ACP · Hermes ' ,
description : ' 通 过 ACP 协 议 直 连 Hermes agent runtime '
}
] ;
}
function pageAiNormalizeAcpRuntimes ( runtimes ) {
var byName = { } ;
pageAiDefaultAcpRuntimes ( ) . forEach ( function ( runtime ) {
byName [ runtime . name ] = Object . assign ( { } , runtime ) ;
} ) ;
pageAiNormalizeArray ( runtimes ) . forEach ( function ( runtime ) {
var name = String ( runtime & & runtime . name | | ' ' ) . trim ( ) ;
if ( name ! = = ' reasonix ' & & name ! = = ' hermes ' ) return ;
byName [ name ] = Object . assign ( { } , byName [ name ] | | { } , runtime , { name : name } ) ;
} ) ;
return [ ' reasonix ' , ' hermes ' ] . map ( function ( name ) { return byName [ name ] ; } ) . filter ( Boolean ) ;
}
function pageAiUnwrapUpstream ( payload ) {
if ( payload & & typeof payload = = = ' object ' & & payload . upstream ) return payload . upstream ;
return payload | | null ;
@@ -4540,7 +4734,7 @@ const SIDEBAR_TREE_JS: &str = r##"
}
function pageAiRunProfile ( ) {
return String ( pageUiState . pageAiAcpRuntime | | ' ' ) . trim ( ) = = = ' reasonix ' ? ' reasonix ' : pageAiCurrentProfile ( ) ;
return String ( pageUiState . pageAiAcpRuntime | | ' reasonix ' ) . trim ( ) = = = ' reasonix ' ? ' reasonix ' : pageAiCurrentProfile ( ) ;
}
function pageAiMnoteToolModel ( ) {
@@ -4705,6 +4899,15 @@ const SIDEBAR_TREE_JS: &str = r##"
return String ( name | | ' ' ) . trim ( ) . replace ( / _ / g , '.' ) ;
}
function pageAiFormatChangedFiles ( files ) {
return pageAiNormalizeArray ( files ) . map ( function ( file ) {
var path = String ( file & & file . path | | ' ' ) . trim ( ) ;
var changeType = String ( file & & file . changeType | | file . change_type | | ' modified ' ) . trim ( ) ;
var summary = String ( file & & file . summary | | ' ' ) . trim ( ) ;
return [ changeType , path , summary ] . filter ( Boolean ) . join ( ' · ' ) ;
} ) . filter ( Boolean ) . join ( '\n' ) ;
}
function pageAiToolEventDeepFindString ( value , keys , depth ) {
if ( ! value | | typeof value ! = = ' object ' | | depth > 5 ) return ' ' ;
for ( var index = 0 ; index < keys . length ; index + = 1 ) {
@@ -5005,7 +5208,7 @@ const SIDEBAR_TREE_JS: &str = r##"
body : JSON . stringify ( {
sessionId : pageUiState . pageAiActiveSessionId ,
profile : pageAiRunProfile ( ) ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' ' ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' reasonix ' ,
reason : ' page_ai_user_stop '
} )
} ) ;
@@ -5063,7 +5266,7 @@ const SIDEBAR_TREE_JS: &str = r##"
var profiles = pageAiNormalizeProfiles ( payload ) ;
pageUiState . pageAiProfiles = profiles . length ? profiles : [ { name : ' mnoteai ' , active : true } ] ;
var acpRuntimes = Array . isArray ( payload . acpRuntimes ) ? payload . acpRuntimes : [ ] ;
pageUiState . pageAiAcpRuntimes = acpRuntimes ;
pageUiState . pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes ( acpRuntimes ) ;
var current = pageAiCurrentProfile ( ) ;
var hasCurrent = pageUiState . pageAiProfiles . some ( function ( profile ) { return pageAiProfileValue ( profile ) = = = current ; } ) ;
var hasMnoteAi = pageUiState . pageAiProfiles . some ( function ( profile ) { return pageAiProfileValue ( profile ) = = = ' mnoteai ' ; } ) ;
@@ -5074,6 +5277,7 @@ const SIDEBAR_TREE_JS: &str = r##"
} catch ( error ) {
pageUiState . pageAiProfileError = error instanceof Error ? error . message : String ( error ) ;
if ( ! pageUiState . pageAiProfiles . length ) pageUiState . pageAiProfiles = [ { name : ' mnoteai ' , active : true } ] ;
pageUiState . pageAiAcpRuntimes = pageAiNormalizeAcpRuntimes ( pageUiState . pageAiAcpRuntimes ) ;
pageAiSetActiveProfile ( pageAiCurrentProfile ( ) ) ;
}
renderPageAiProviderButtons ( ) ;
@@ -5156,7 +5360,7 @@ const SIDEBAR_TREE_JS: &str = r##"
async function pageAiLoadSkills ( ) {
try {
var runtime = String ( pageUiState . pageAiAcpRuntime | | ' ' ) . trim ( ) ;
var runtime = String ( pageUiState . pageAiAcpRuntime | | ' reasonix ' ) . trim ( ) ;
var params = runtime = = = ' reasonix '
? ' runtime = reasonix '
: ' profile = ' + encodeURIComponent ( pageAiCurrentProfile ( ) ) ;
@@ -5178,7 +5382,7 @@ const SIDEBAR_TREE_JS: &str = r##"
async function pageAiToggleSkill ( skillName , enabled ) {
var name = String ( skillName | | ' ' ) . trim ( ) ;
if ( ! name ) return ;
if ( String ( pageUiState . pageAiAcpRuntime | | ' ' ) . trim ( ) = = = ' reasonix ' ) return ;
if ( String ( pageUiState . pageAiAcpRuntime | | ' reasonix ' ) . trim ( ) = = = ' reasonix ' ) return ;
var previous = null ;
pageAiSkillListEntries ( ) . forEach ( function ( skill ) {
if ( skill . name = = = name & & previous = = null ) previous = skill . enabled ! = = false ;
@@ -5275,16 +5479,15 @@ const SIDEBAR_TREE_JS: &str = r##"
var isAcp = pageUiState . pageAiAcpRuntime ! = = ' ' ;
var activeProfile = pageAiRunProfile ( ) ;
drawer . setAttribute ( ' data - page - ai - page ' , pageUiState . pageAiPage | | ' chat ' ) ;
drawer . setAttribute ( ' data - mnote - acp - runtime ' , pageUiState . pageAiAcpRuntime | | ' ' ) ;
drawer . setAttribute ( ' data - mnote - acp - runtime ' , pageUiState . pageAiAcpRuntime | | ' reasonix ' ) ;
// Populate ACP runtime dropdown
var acpSelect = drawer . querySelector ( ' [ data - page - ai - acp - runtime ] ' ) ;
if ( acpSelect instanceof HTMLSelectElement ) {
var runtimes = pageUiState . pageAiAcpRuntimes . length ? pageUiState . pageAiAcpRuntimes : [ ] ;
acpSelect . innerHTML = ' < option value = " " > 默 认 ( Hermes HTTP ) < / option > ' +
runtimes . map ( function ( rt ) {
var runtimes = pageUiState . pageAiAcpRuntimes . length ? pageUiState . pageAiAcpRuntimes : pageAiNormalizeAcpRuntimes ( [ ] ) ;
acpSelect . innerHTML = runtimes . map ( function ( rt ) {
return ' < option value = " ' + escapeHtml(rt.name || '') + ' " ' + ( ( rt . name | | ' ' ) = = = pageUiState . pageAiAcpRuntime ? ' selected ' : ' ' ) + '>' + escapeHtml ( rt . title | | rt . name ) + ' < / option > ' ;
} ) . join ( ' ' ) ;
acpSelect . value = pageUiState . pageAiAcpRuntime | | ' ' ;
acpSelect . value = pageUiState . pageAiAcpRuntime | | ' reasonix ' ;
}
// Show/hide Hermes-specific profile select
var profileLabel = drawer . querySelector ( ' [ data - page - ai - hermes - profile ] ' ) ;
@@ -5366,7 +5569,7 @@ const SIDEBAR_TREE_JS: &str = r##"
var session = pageAiCurrentSession ( ) ;
var usageText = session & & session . usage ? pageAiUsageSummary ( session . usage ) : ' ' ;
sessionNode . textContent = session & & session . id
? [ String ( session . title | | ' 当 前 页 问 答 ' ) , usageText ] . filter ( Boolean ) . join ( ' · ' )
? [ String ( session . title | | ' 当 前 页 问 答 ' ) , pageAiSessionStorageLabel ( session ) , usageText ] . filter ( Boolean ) . join ( ' · ' )
: ' 等 待 Hermes session ' ;
}
var modelNode = drawer . querySelector ( ' [ data - page - ai - model - status ] ' ) ;
@@ -5438,7 +5641,7 @@ const SIDEBAR_TREE_JS: &str = r##"
if ( skillList instanceof HTMLElement ) {
var skills = pageAiFilteredSkillEntries ( ) ;
if ( ! skills . length ) {
var emptyText = String ( pageUiState . pageAiAcpRuntime | | ' ' ) . trim ( ) = = = ' reasonix '
var emptyText = String ( pageUiState . pageAiAcpRuntime | | ' reasonix ' ) . trim ( ) = = = ' reasonix '
? ' 没 有 匹 配 的 Reasonix skill 。 '
: ' 没 有 匹 配 的 Hermes skill 。 ' ;
skillList . innerHTML = ' < div class = " wolai-page-ai-empty " > ' + escapeHtml ( emptyText ) + ' < / div > ' ;
@@ -5447,7 +5650,7 @@ const SIDEBAR_TREE_JS: &str = r##"
var sourceText = pageAiSkillOriginLabel ( skill ) + ( skill . modified ? ' · modified ' : ' ' ) ;
var description = String ( skill . description | | ' ' ) . trim ( ) ;
var hasDescription = description & & description ! = = ' - - - ' & & description ! = = ' 无 描 述 ' ;
var canToggle = skill . toggleable ! = = false & & String ( pageUiState . pageAiAcpRuntime | | ' ' ) . trim ( ) ! = = ' reasonix ' ;
var canToggle = skill . toggleable ! = = false & & String ( pageUiState . pageAiAcpRuntime | | ' reasonix ' ) . trim ( ) ! = = ' reasonix ' ;
return ' ' +
' < div class = " wolai-page-ai-skill-row " > ' +
' < div class = " wolai-page-ai-skill-copy " > ' +
@@ -5618,9 +5821,7 @@ const SIDEBAR_TREE_JS: &str = r##"
' < div class = " wolai-page-ai-settings-grid " > ' +
' < label class = " wolai-page-ai-profile-select " > ' +
' < span > ACP < / span > ' +
' < select data - page - ai - acp - runtime > ' +
' < option value = " " > 默 认 ( Hermes HTTP ) < / option > ' +
' < / select > ' +
' < select data - page - ai - acp - runtime > < / select > ' +
' < / label > ' +
' < label class = " wolai-page-ai-profile-select " data - page - ai - hermes - profile > ' +
' < span > agent / profile < / span > ' +
@@ -5756,7 +5957,7 @@ const SIDEBAR_TREE_JS: &str = r##"
: ( session . snippet | | session . preview | | ' 暂 无 消 息 ' ) ;
var active = session . id = = = pageUiState . pageAiActiveSessionId ;
var usage = pageAiUsageSummary ( session . usage ) ;
var meta = [ session . source = = = ' acp ' ? ' Convex ' : ' 本 地 ' , session . status , usage ] . filter ( Boolean ) . join ( ' · ' ) ;
var meta = [ pageAiSessionStorageLabel ( session ) , session . permissionLevel , session . shareId , session . status , usage ] . filter ( Boolean ) . join ( ' · ' ) ;
return ' ' +
' < div class = " wolai-page-ai-message wolai-page-ai-message--history' + (active ? ' is-active' : '') + ' " data - page - ai - session - row = " ' + escapeHtml(session.id) + ' " > ' +
' < button type = " button " class = " wolai-page-ai-message-text " data - page - ai - session = " ' + escapeHtml(session.id) + ' " > ' +
@@ -5784,6 +5985,7 @@ const SIDEBAR_TREE_JS: &str = r##"
var detailRows = [
item . argsSummary ? ' < div class = " wolai-page-ai-tool-meta " > 参 数 ' + escapeHtml ( item . argsSummary ) + ' < / div > ' : ' ' ,
item . resultSummary ? ' < div class = " wolai-page-ai-tool-meta " > 结 果 ' + escapeHtml ( item . resultSummary ) + ' < / div > ' : ' ' ,
item . changedFiles & & item . changedFiles . length ? ' < pre class = " wolai-page-ai-tool-meta wolai-page-ai-changed-files " > ' + escapeHtml ( pageAiFormatChangedFiles ( item . changedFiles ) ) + ' < / pre > ' : ' ' ,
( item . traceId | | item . auditId ) ? ' < div class = " wolai-page-ai-tool-meta " > ' + escapeHtml ( [ item . traceId , item . auditId ] . filter ( Boolean ) . join ( ' · ' ) ) + ' < / div > ' : ' '
] . filter ( Boolean ) . join ( ' ' ) ;
return ' ' +
@@ -5975,6 +6177,7 @@ const SIDEBAR_TREE_JS: &str = r##"
}
async function pageAiTryBlockEditWorkflow ( prompt , scopedContext ) {
if ( currentSourceKind ( ) = = = ' local_folder ' ) return false ;
if ( ! pageAiLooksLikeBlockEdit ( prompt ) ) return false ;
var runId = ' page - ai - fast - ' + Date . now ( ) . toString ( 36 ) ;
var traceId = ' page - ai - fast - ' + Date . now ( ) . toString ( 36 ) ;
@@ -5985,11 +6188,13 @@ const SIDEBAR_TREE_JS: &str = r##"
method : ' POST ' ,
headers : { ' content - type ' : ' application / json ' } ,
body : JSON . stringify ( {
workspaceId : resolveWorkspaceId ( document . body ) ,
documentId : currentDocumentId ( ) ,
sessionId : pageUiState . pageAiActiveSessionId ,
runId : runId ,
profile : pageAiCurrentProfile ( ) ,
workspaceId : resolveWorkspaceId ( document . body ) ,
documentId : currentDocumentId ( ) ,
sourceKind : currentSourceKind ( ) ,
rootUri : currentRootUri ( ) ,
sessionId : pageUiState . pageAiActiveSessionId ,
runId : runId ,
profile : pageAiCurrentProfile ( ) ,
model : pageAiMnoteToolModel ( ) ,
message : prompt ,
pageContext : scopedContext . pageContext ,
@@ -6083,9 +6288,11 @@ const SIDEBAR_TREE_JS: &str = r##"
body : JSON . stringify ( {
workspaceId : resolveWorkspaceId ( document . body ) ,
documentId : currentDocumentId ( ) ,
sourceKind : currentSourceKind ( ) ,
rootUri : currentRootUri ( ) ,
sessionId : pageUiState . pageAiActiveSessionId ,
profile : pageAiRunProfile ( ) ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' ' ,
acpRuntime : pageUiState . pageAiAcpRuntime | | ' reasonix ' ,
contextScope : pageUiState . pageAiContextScope ,
message : prompt ,
model : pageAiMnoteToolModel ( ) ,
@@ -6184,6 +6391,23 @@ const SIDEBAR_TREE_JS: &str = r##"
var completedSession = pageAiCurrentSession ( ) ;
if ( completedSession ) completedSession . usage = completed . usage ;
}
var agentAudit = completed & & completed . agentAudit & & typeof completed . agentAudit = = = ' object ' ? completed . agentAudit : null ;
var changedFiles = pageAiNormalizeArray ( agentAudit & & agentAudit . changedFiles ) ;
if ( changedFiles . length ) {
pageUiState . pageAiMessages . push ( {
role : ' tool ' ,
content : ' agent . changed_files ' ,
toolCallId : runId + ' :agent . changed_files ' ,
toolName : ' agent . changed_files ' ,
toolKind : ' audit ' ,
status : ' completed ' ,
argsSummary : String ( agentAudit . rootUri | | ' ' ) ,
resultSummary : String ( agentAudit . diffSummary | | changedFiles . length + ' changed file ( s ) ' ) ,
changedFiles : changedFiles ,
traceId : runTraceId ,
auditId : String ( agentAudit . eventId | | ' ' )
} ) ;
}
} catch ( _ ) { }
pageAiSetRunStatus ( ' completed ' , runId ) ;
}
@@ -7014,6 +7238,13 @@ const SIDEBAR_TREE_JS: &str = r##"
return ;
}
var createLocalWorkspaceTrigger = closestAction ( e . target , ' [ data - mnote - action = " create-local-workspace " ] ' ) ;
if ( createLocalWorkspaceTrigger ) {
e . preventDefault ( ) ;
void createDefaultLocalWorkspace ( createLocalWorkspaceTrigger ) ;
return ;
}
var createTrigger = closestAction ( e . target , ' [ data - mnote - action = " create-page " ] ' ) ;
if ( createTrigger ) {
e . preventDefault ( ) ;
@@ -7231,7 +7462,7 @@ const SIDEBAR_TREE_JS: &str = r##"
document . addEventListener ( ' change ' , function ( event ) {
var pageAiAcpRuntimeSelect = closestAction ( event . target , ' [ data - page - ai - acp - runtime ] ' ) ;
if ( pageAiAcpRuntimeSelect instanceof HTMLSelectElement ) {
var next = String ( pageAiAcpRuntimeSelect . value | | ' ' ) . trim ( ) ;
var next = String ( pageAiAcpRuntimeSelect . value | | ' reasonix ' ) . trim ( ) | | ' reasonix ' ;
pageUiState . pageAiAcpRuntime = next ;
pageUiState . pageAiSkills = { categories : [ ] , archived : [ ] } ;
pageUiState . pageAiSkillError = ' ' ;
@@ -7829,6 +8060,9 @@ pub fn PageLayout(
/// 顶栏当前页面标题(可选)
#[ prop(optional) ]
topbar_title : Option < String > ,
/// 是否显示管理员授权入口
#[ prop(optional) ]
show_admin_access_policy : bool ,
) -> impl IntoView {
let sidebar_tree_html = sidebar_tree_html . unwrap_or_default ( ) ;
@@ -7894,6 +8128,21 @@ pub fn PageLayout(
< a href = " /help " title = " 帮助 " aria - label = " 帮助 " > < span class = " material-symbols-outlined nav-icon " data - icon = " help " aria - hidden = " true " > < / span > < / a >
< a href = " /files " title = " 文件 " aria - label = " 文件 " > < span class = " material-symbols-outlined nav-icon " data - icon = " inventory_2 " aria - hidden = " true " > < / span > < / a >
< button type = " button " title = " 打开本地文件夹 " aria - label = " 打开本地文件夹 " data - mnote - action = " open-local-folder " > < span class = " material-symbols-outlined nav-icon " data - icon = " folder_open " aria - hidden = " true " > < / span > < / button >
{ if show_admin_access_policy {
view! {
< a
href = " /admin/access-policy "
class :active = { current_nav = = " admin " }
title = " 目录授权 "
aria - label = " 目录授权 "
data - testid = " mnote-admin-access-policy-entry "
>
< span class = " material-symbols-outlined nav-icon " data - icon = " admin_panel_settings " aria - hidden = " true " > < / span >
< / a >
} . into_any ( )
} else {
view! { } . into_any ( )
} }
< a href = " /more " title = " 更多 " aria - label = " 更多 " > < span class = " material-symbols-outlined nav-icon " data - icon = " more_horiz " aria - hidden = " true " > < / span > < / a >
< / nav >
< div class = " wolai-sidebar-body " inner_html = { sidebar_sections_html } > < / div >
@@ -7987,6 +8236,9 @@ mod tests {
assert! ( SIDEBAR_TREE_JS . contains ( " objectIdentity: objectIdentity " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " workspaceId: resolveWorkspaceId(fileRow) " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " /api/media/sign?assetId= " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " function localFilePathFromAssetId " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " /api/local-folder/files/open " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " local-file: " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " fetchCurrentOnlyOfficeUserId " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " /api/auth/whoami " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " buildOnlyOfficeOpenUrl " ) ) ;
@@ -7998,8 +8250,11 @@ mod tests {
assert! ( SIDEBAR_TREE_JS . contains ( " tree-rename-input " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " sidebarFileTreeClipboard " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " pasteSidebarFileTreeClipboard " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " MNOTE_RECENT_LOCAL_ROOTS_KEY " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " function recentLocalRootsStorageKey " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " data-mnote-actor-id " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " requestOpenLocalFolder " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " autoOpenRecentLocalRootOnHome " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " sourceKind', 'local_folder " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " switchToCloudWorkspace " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " mnote-switch-cloud-workspace " ) ) ;
@@ -8018,6 +8273,26 @@ mod tests {
) ;
}
#[ test ]
fn page_ai_fast_path_is_not_local_first_main_path ( ) {
assert! (
SIDEBAR_TREE_JS . contains ( " if (currentSourceKind() === 'local_folder') return false; " ) ,
" local-first 页面 AI 不应继续加厚 page-ai fast-path;本地编辑应走受控文件工具 "
) ;
}
#[ test ]
fn page_ai_local_source_passes_file_reference_fields_to_agent_run ( ) {
assert! ( SIDEBAR_TREE_JS . contains ( " function currentRootUri() " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " sourceKind: currentSourceKind() " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " rootUri: currentRootUri() " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " pageContext: scopedContext.pageContext " ) ) ;
assert! (
SIDEBAR_TREE_JS . contains ( " if (currentSourceKind() === 'local_folder') return false; " ) ,
" local source 不应进入 page-ai fast-path,后端会把 run 收敛为文件引用 scope "
) ;
}
#[ test ]
fn page_ai_uses_backend_acp_session_runtime_store ( ) {
assert! ( SIDEBAR_TREE_JS . contains ( " function pageAiLoadBackendSessions " ) ) ;
@@ -8043,6 +8318,33 @@ mod tests {
) ;
}
#[ test ]
fn page_ai_session_ui_labels_local_shared_and_cloud_storage ( ) {
assert! ( SIDEBAR_TREE_JS . contains ( " function pageAiSessionStorageLabel " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " local_private " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " local_shared " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " convex_acp_runtime_store " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " 本地私有 " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " 共享会话 " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " 云端会话 " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " sessionStorage: " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " permissionLevel: " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " shareId: " ) ) ;
}
#[ test ]
fn page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch ( ) {
assert! ( SIDEBAR_TREE_JS . contains ( " var PAGE_AI_SESSION_STORAGE_VERSION = 3 " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " pageAiAcpRuntime: 'reasonix' " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " function pageAiNormalizeAcpRuntimes " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " return ['reasonix', 'hermes'] " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " pageAiNormalizeAcpRuntimes(acpRuntimes) " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " pageAiAcpRuntimeSelect.value || 'reasonix' " ) ) ;
assert! ( SIDEBAR_TREE_JS
. contains ( " activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix' " ) ) ;
assert! ( ! SIDEBAR_TREE_JS . contains ( " 默认 (Hermes HTTP) " ) ) ;
}
#[ test ]
fn sidebar_tree_runtime_does_not_use_retired_query_preferred_snapshot_selector ( ) {
assert! ( TREE_LIVE_CONTROLLER_JS . contains ( " tree:snapshot " ) ) ;
@@ -8081,6 +8383,18 @@ mod tests {
assert! ( ! SIDEBAR_TREE_JS . contains ( " window.location.reload " ) ) ;
}
#[ test ]
fn sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder ( ) {
assert! ( SIDEBAR_TREE_JS . contains ( " currentSourceKind() === 'local_folder' " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " /api/local-folder/assets/upload " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " localForm.append('rootUri', rootUri) " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " localForm.append('documentId', documentId) " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " isLocalUploadedAsset(asset) " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " if (isLocalUploadedAsset(asset)) return ''; " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " refreshLocalFolderSidebarSnapshot " ) ) ;
assert! ( SIDEBAR_TREE_JS . contains ( " wolai:local-assets-changed " ) ) ;
}
#[ test ]
fn sidebar_tree_runtime_contains_dev_hot_reload_client ( ) {
assert! ( SIDEBAR_TREE_JS . contains ( " installMnoteDevHotReload " ) ) ;