Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
steve02081504 committed Jan 19, 2025
1 parent de382be commit 6618c4b
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 58 deletions.
177 changes: 177 additions & 0 deletions src/public/home/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,183 @@ article img {
margin-right: 2px;
}

/* 电脑端卡片样式 */
.card {
height: 300px;
position: relative;
/* 为绝对定位的 tags 容器提供参考 */
}

.card>.card-content {
height: 100%;
position: relative;
/* 添加 position: relative */
z-index: 5;
}

.card>.card-content::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
/* 遮罩层 */
z-index: 1;
/* 确保遮罩在图片上方 */
}

.card>.card-content>figure {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 0;
}

.card>.card-content>figure>img {
object-fit: contain;
object-position: center;
width: 100%;
height: 100%;
}

.card>.card-content>.card-body {
position: relative;
z-index: 10;
color: white;
/* 将文字颜色设置为白色 */
display: flex;
flex-direction: column;
justify-content: flex-end;
height: 100%;
/* 使 card-body 撑满 card-content */
padding-bottom: 1rem;
/* 调整底部边距 */
}

/* 电脑端 tags 容器样式 */
.tags-container {
top: 0;
/* 与卡片顶部对齐 */
left: 100%;
/* 位于卡片右侧 */
width: 100%;
/* 宽度与卡片相同 */
height: 100%;
/* 高度与卡片相同 */
background-color: rgba(220, 220, 220, 0.5);
/* 设置背景色和透明度 */
border-radius: 10px;
/* 添加圆角 */
}

/* 关键:电脑端样式 */
@media (min-width: 1024px) {
.tags-container {
position: absolute;
/* 电脑端添加绝对定位 */
display: none;
/* 电脑端默认隐藏 */
}

/* 鼠标悬停时显示 */
.group:hover .tags-container {
display: block;
}
}

.tags-container .badge {
margin-bottom: 5px;
}

/* 手机端卡片样式 */
@media (max-width: 1023px) {
:root {
--scrollbar-width: 0px;
/* 默认滚动条宽度为 0 */
}

@supports (width: 100vw) {
:root {
--scrollbar-width: calc(100vw - 100%);
/* 动态计算滚动条宽度 */
}
}

.card {
display: flex;
/* 使用 Flex 布局 */
width: 100%;
/* 宽度充满 */
flex-wrap: wrap;
/* 允许换行 */
}

/* 使 card-content 和 tags-container 水平排列 */
.card>.card-content,
.card>.tags-container {
box-sizing: border-box;
/* 避免宽度计算错误 */
}

.card>.card-content {
flex: 0 0 50%;
/* 占据 50% 宽度 */
position: relative;
/* 允许其中的 figure 使用绝对定位 */
aspect-ratio: 3 / 4;
/* 设置宽高比为 3:4 */
}

.card>.card-content>figure {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

.card>.card-content>figure img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}

.card>.card-content>.card-body {
position: relative;
z-index: 5;
color: white;
padding: 0.5rem;
/* 调整 padding */
}

.card>.tags-container {
flex: 0 0 calc(50% - var(--scrollbar-width) / 2);
/* 占据 50% 宽度,减去滚动条宽度的一半 */
max-width: 50%;
/* 添加最大宽度限制 */
display: block !important;
background-color: rgb(220, 220, 220);
aspect-ratio: 3 / 4;
/* 设置宽高比为 3:4 */
}

.card>.tags-container>.tags-list-container {
margin: 0;
padding: 0.5rem;
border: none;
height: 100%;
}
}

.markdown-body pre {
overflow-x: auto;
}

.markdown-body code {
white-space: pre-wrap;
}
2 changes: 1 addition & 1 deletion src/public/home/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ <h1 class="text-2xl">角色选择页面</h1>
<label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label>
<div class="p-4 w-80 min-h-full bg-base-300 text-base-content border-l-2 border-base-content">
<h2 class="text-xl mb-2">角色信息</h2>
<article id="character-description"></article>
<article id="character-description" class="markdown-body"></article>
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/public/shells/chat/new/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ await createNewChat()

let serchParams = new URLSearchParams(window.location.search)

if (serchParams.has('charname'))
await addCharacter(serchParams.get('charname'))
if (serchParams.has('char'))
await addCharacter(serchParams.get('char'))

// jump to chat
window.history.replaceState(null, null, '/shells/chat/#' + currentChatId)
Expand Down
125 changes: 97 additions & 28 deletions src/public/shells/chat/src/public/ui/sidebar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,67 @@ const cachedDom = {
character: {},
}

/**
* 比较两个数组的差异
* @param {Array} oldList 旧数组
* @param {Array} newList 新数组
* @returns {{ added: Array, removed: Array, unchanged: Array }} 包含 added, removed, unchanged 三个数组的对象
*/
function compareLists(oldList, newList) {
const added = newList.filter(item => !oldList.includes(item))
const removed = oldList.filter(item => !newList.includes(item))
const unchanged = newList.filter(item => oldList.includes(item))

return { added, removed, unchanged }
}

/**
* 更新选择列表
* @param {HTMLSelectElement} selectElement 选择列表元素
* @param {string} currentName 当前选中项的名称
* @param {Function} listGetter 获取列表数据的函数
* @param {Function} detailsRenderer 渲染详情的函数
* @param {boolean} forceUpdate 是否强制更新详情, 为 true 时强制更新
*/
async function updateSelectList(selectElement, currentName, listGetter, detailsRenderer, forceUpdate = false) {
const newList = await listGetter()
newList.unshift('') // 添加一个空选项

const oldList = Array.from(selectElement.options).map(option => option.value)
const { added, removed, unchanged } = compareLists(oldList, newList)

// 删除已移除的选项
removed.forEach(name => {
const optionToRemove = selectElement.querySelector(`option[value="${name}"]`)
if (optionToRemove) selectElement.removeChild(optionToRemove)
})

// 添加新增的选项
added.forEach(name => {
const option = document.createElement('option')
option.value = name || ''
option.text = name || '无'
selectElement.add(option)
})

// 更新当前选中项 (如果需要)
if (currentName !== selectElement.value)
selectElement.value = currentName || ''


// 更新详情 (仅当选中项改变或强制更新时)
if (selectElement.value !== (selectElement.previousValue || '') || forceUpdate)
await detailsRenderer(selectElement.value)


selectElement.previousValue = selectElement.value
}

/**
* 渲染世界信息列表
*/
async function renderWorldList() {
const worlds = await getWorldList()
worlds.unshift('') // 添加一个空选项
worldSelect.innerHTML = worlds
.map((world) => `<option value="${world ?? ''}" ${world === worldName ? 'selected' : ''}>${world || '无'}</option>`)
.join('')

await renderWorldDetails(worldName)
await updateSelectList(worldSelect, worldName, getWorldList, renderWorldDetails)
}

/**
Expand All @@ -70,13 +120,7 @@ async function renderWorldDetails(worldName) {
* 渲染角色信息列表
*/
async function renderPersonaList() {
const personas = await getPersonaList()
personas.unshift('') // 添加一个空选项
personaSelect.innerHTML = personas
.map((persona) => `<option value="${persona ?? ''}" ${persona === personaName ? 'selected' : ''}>${persona || '无'}</option>`)
.join('')

await renderPersonaDetails(personaName)
await updateSelectList(personaSelect, personaName, getPersonaList, renderPersonaDetails)
}

/**
Expand Down Expand Up @@ -104,32 +148,55 @@ async function renderPersonaDetails(personaName) {
*/
async function renderCharList(data) {
const allChars = await getCharList()
const availableChars = allChars.filter((char) => !charList.includes(char))
charSelect.innerHTML = availableChars
.map((char) => `<option value="${char}">${char}</option>`)
.join('')

// 处理角色详情
const currentCharsRendered = Array.from(charDetailsContainer.children).map(child => child.getAttribute('data-char-name'))
const { added, removed, unchanged } = compareLists(currentCharsRendered, charList)

// 删除已经移除的角色
currentCharsRendered.forEach(char => {
if (!charList.includes(char)) {
const charCardToRemove = charDetailsContainer.querySelector(`[data-char-name="${char}"]`)
if (charCardToRemove)
charDetailsContainer.removeChild(charCardToRemove)
removed.forEach(char => {
const charCardToRemove = charDetailsContainer.querySelector(`[data-char-name="${char}"]`)
if (charCardToRemove) {
charDetailsContainer.removeChild(charCardToRemove)
delete cachedDom.character[char] // 清理缓存
}
})

// 添加新的角色
for (const char of charList)
if (!currentCharsRendered.includes(char))
await renderCharDetails(char, data.frequency_data[char])
for (const char of added)
await renderCharDetails(char, data.frequency_data[char])

// 更新已存在的角色 (如果频率数据有更新)
for (const char of unchanged) {
const charCard = charDetailsContainer.querySelector(`[data-char-name="${char}"]`)
const frequencySlider = charCard.querySelector('.frequency-slider')
const currentFrequency = parseInt(frequencySlider.value)
const newFrequency = Math.round(data.frequency_data[char] * 100)

if (currentFrequency !== newFrequency)
frequencySlider.value = newFrequency
}

// 更新可用角色列表
const availableChars = allChars.filter((char) => !charList.includes(char))
const charSelectOldList = Array.from(charSelect.options).map(option => option.value)
const { added: charSelectAdded, removed: charSelectRemoved } = compareLists(charSelectOldList, availableChars)

charSelectRemoved.forEach(name => {
const optionToRemove = charSelect.querySelector(`option[value="${name}"]`)
if (optionToRemove) charSelect.removeChild(optionToRemove)
})

charSelectAdded.forEach(name => {
const option = document.createElement('option')
option.value = name
option.text = name
charSelect.add(option)
})
}

/**
* 渲染聊天角色详情
* @param {string} charName 角色名称
* @param {number} frequency_num
*/
async function renderCharDetails(charName, frequency_num) {
let charData
Expand Down Expand Up @@ -251,6 +318,8 @@ export async function setupSidebar() {

export async function triggerSidebarHeartbeat(data) {
if (!leftDrawerCheckbox.checked) return

// 尝试更新数据
await renderWorldList()
await renderPersonaList()
await renderCharList(data)
Expand Down
Loading

0 comments on commit 6618c4b

Please sign in to comment.