Changeset 0.20.0 (#85)

This commit is contained in:
2026-03-01 12:40:15 +00:00
parent eb74180611
commit 817062e5fc
57 changed files with 5435 additions and 72 deletions

View File

@@ -37,12 +37,37 @@ const ChatInput = {
return this._textarea;
},
/** Get the visible input element (for positioning, getBoundingClientRect) */
get el() {
if (this._editor) return this._editor.getView().dom;
return this._textarea;
},
/** Get the wrapper DOM element (for resize, etc.) */
getWrapDom() {
if (this._editor) return this._editor.getView().dom;
return this._textarea;
},
/** Get cursor byte offset (for @mention autocomplete) */
getCursorPos() {
if (this._editor) {
const view = this._editor.getView();
return view.state.selection.main.head;
}
return this._textarea?.selectionStart || 0;
},
/** Set cursor byte offset (for @mention autocomplete) */
setCursorPos(pos) {
if (this._editor) {
const view = this._editor.getView();
view.dispatch({ selection: { anchor: pos } });
} else if (this._textarea) {
this._textarea.selectionStart = this._textarea.selectionEnd = pos;
}
},
/** Initialize — try CM6, fall back to textarea */
init() {
this._textarea = document.getElementById('messageInput');
@@ -54,20 +79,35 @@ const ChatInput = {
this._editor = CM.chatInput(wrap, {
placeholder: 'Send a message...',
onSubmit: () => sendMessage(),
onChange: () => updateInputTokens(),
onChange: () => {
updateInputTokens();
// @mention autocomplete (v0.20.0)
if (typeof ChannelModels !== 'undefined') ChannelModels.onInput(ChatInput);
},
maxHeight: 200,
});
// @mention autocomplete: intercept keys on CM6 contentDOM (v0.20.0)
this._editor.getView().contentDOM.addEventListener('keydown', (e) => {
if (typeof ChannelModels !== 'undefined' && ChannelModels.handleKey(e)) {
e.preventDefault();
e.stopPropagation();
}
});
DebugLog?.push?.('chat', '[CM6] Chat input initialized');
} else {
// Fallback: textarea with manual event handling
DebugLog?.push?.('chat', '[CM6] Not available — using textarea fallback');
this._textarea.addEventListener('keydown', (e) => {
// @mention autocomplete intercepts arrow/enter/escape (v0.20.0)
if (typeof ChannelModels !== 'undefined' && ChannelModels.handleKey(e)) return;
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
});
this._textarea.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 200) + 'px';
updateInputTokens();
// @mention autocomplete (v0.20.0)
if (typeof ChannelModels !== 'undefined') ChannelModels.onInput(this);
});
}
},
@@ -202,6 +242,9 @@ async function selectChat(chatId) {
// Restore the last-used model/preset for this chat
_restoreChatModel(chatId, chat);
// Load multi-model roster for @mention routing (v0.20.0)
if (typeof ChannelModels !== 'undefined') ChannelModels.load(chatId);
// Load attachments for message rendering (non-blocking, best-effort)
await loadChannelAttachments(chatId);
@@ -827,6 +870,9 @@ function _initChatListeners() {
// Input — CM6 or textarea fallback
ChatInput.init();
// Multi-model channel roster (v0.20.0)
if (typeof ChannelModels !== 'undefined') ChannelModels.init();
// Close modals on overlay click
document.querySelectorAll('.modal-overlay').forEach(overlay => {
overlay.addEventListener('click', (e) => {