From 1ef940ade25c39b29843cb4b281e6cd8fc879b9e Mon Sep 17 00:00:00 2001 From: multipleof4 Date: Tue, 18 Nov 2025 21:01:44 -0800 Subject: [PATCH] Fix: Import partsToText from utils --- src/ui.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui.js b/src/ui.js index 2d8178c..00635fd 100644 --- a/src/ui.js +++ b/src/ui.js @@ -3,7 +3,7 @@ import { state, cacheStore } from './state.js' import { SUNE } from './sune.js' import { THREAD } from './thread.js' import { USER } from './user.js' -import { esc, gid, positionPopover, imgToWebp, asDataURL, b64, titleFrom } from './utils.js' +import { esc, gid, positionPopover, imgToWebp, asDataURL, b64, titleFrom, partsToText } from './utils.js' import { generateTitleWithAI } from './api.js' export const getModelShort=m=>{const mm=m||SUNE.model||'';return mm.includes('/')?mm.split('/').pop():mm} @@ -16,7 +16,6 @@ export const renderSidebar=()=>{const list=[...SUNE.list].sort((a,b)=>(b.pinned- export const getSuneLabel=m=>{const name=(m&&m.sune_name)||SUNE.name,modelShort=getModelShort(m&&m.model);return `${name} · ${modelShort}`} export function _createMessageRow(m){const role=typeof m==='string'?m:(m&&m.role)||'assistant',meta=typeof m==='string'?{}:m||{},isUser=role==='user',$row=$('
'),$head=$('
'),$avatar=$('
');const uAva=isUser?USER.avatar:meta.avatar;uAva?$avatar.attr('class','msg-avatar shrink-0 h-7 w-7 rounded-full overflow-hidden').html(``):$avatar.attr('class',`${isUser?'bg-gray-900 text-white':'bg-gray-200 text-gray-900'} msg-avatar shrink-0 h-7 w-7 rounded-full flex items-center justify-center`).text(isUser?'👤':'✺');const $name=$('
').text(isUser?USER.name:getSuneLabel(meta));const $deleteBtn=$('').on('click',async e=>{e.stopPropagation();state.messages=state.messages.filter(msg=>msg.id!==m.id);$row.remove();await THREAD.persist()});const $copyBtn=$('').on('click',async function(e){e.stopPropagation();try{await navigator.clipboard.writeText(partsToText(m.content));$(this).html('');icons();setTimeout(()=>{$(this).html('');icons()},1200)}catch{}});$head.append($avatar,$name,$copyBtn,$deleteBtn);const $bubble=$(`
`);$row.append($head,$bubble);return $row} export function msgRow(m){const $row=_createMessageRow(m);$(el.messages).append($row);queueMicrotask(()=>{el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'});icons()});return $row.find('.msg-bubble')[0]} -export function partsToText(parts){if(!parts)return'';if(Array.isArray(parts))return parts.map(p=>p?.type==='text'?p.text:(p?.type==='image_url'?`![](${p.image_url?.url||''})`:(p?.type==='file'?`[${p.file?.filename||'file'}]`:(p?.type==='input_audio'?`(audio:${p.input_audio?.format||''})`:'')))).join('\n');return String(parts)} export const addMessage=function(m,track=true){m.id=m.id||gid();if(!Array.isArray(m.content)&&m.content!=null){m.content=[{type:'text',text:String(m.content)}]}const bubble=msgRow(m);bubble.dataset.mid=m.id;renderMarkdown(bubble,partsToText(m.content));if(track)state.messages.push(m);if(m.role==='assistant')el.composer.dispatchEvent(new CustomEvent('sune:newSuneResponse',{detail:{message:m}}));return bubble} export const addSuneBubbleStreaming=(meta,id)=>msgRow(Object.assign({role:'assistant',id},meta)) export const clearChat=()=>{el.suneHtml.dispatchEvent(new CustomEvent('sune:unmount'));state.messages=[];el.messages.innerHTML='';state.attachments=[];updateAttachBadge();el.fileInput.value=''}