Files
tamabox/templates/chat/conversation.html
T
tamakyi 27856dd4a2 字号阶梯收尾 + 主色补到 WCAG AA(新增 --tb-on-primary)
一号事项(剩余令牌化):
- templates/user/profile.html 20 处、base.css 9 处 font-size 全部落到 --tb-fs-*
- base.css 导航四档(0.95/0.88/0.82/0.78)合并成「桌面 md → ≤1000 xs」;logo 三档统一 lg
- 有理由保留的:.tb-pin-flag / .md-body code 的 0.9em(相对父级)、验证码 1.4/1.2rem(等宽字形)、
  chat 关闭按钮 40px、password 页 3em、mail/*(邮件客户端不保证 var());行内 12px/0.75rem 等值顺手换掉

二号事项(主色对比度):
- base/default #1e87f0(3.42) → #0e6fd0(4.70);shiroko #4a7fc1(3.69) → #3b6dac(4.74);
  sakura #e5678f(2.80) → #ba3e66(4.70,饱和度压到 .5 保持柔和)。同色相只压亮度,目标 4.7 留余量
- 深色块 muted 之前只对 --tb-bg 算(4.7 以为过了),对压平后的卡片其实 4.31/4.17 → 提到 #a18790 / #7f8ea1
- 新增 --tb-on-primary(亮 #ffffff / 暗 #0b1016):深色模式主色偏亮,白字只有 2.2~3.1:1,
  用同一个令牌兼顾「链接色要深」和「按钮底要深」是矛盾的。.uk-button-primary 与
  .uk-label:not(语义色) 改吃它 → 深色按钮变成浅底深字(6.10 / 8.65 / 7.27)
- 顺手封掉几处主色泄漏:header 验证码 focus、profile .feat-day.is-on、explore 随机投票的 JS outline、
  vote-results 行内色,全部改成令牌(否则换主题时不跟着变)

生成器同步:paletteVars 导出 --tb-on-primary(白字/深字择优);?dump=palette 增加
onPrimaryCr / onPrimaryPickedBest / warnings —— 主色卡中间调时只警告不判失败;
配色面板给橙色提示。_scaffold 令牌 29 → 30。StaticVersion → 20261021c。

验证:TestSwatch 断言同步为 #ba3e66;8 种组合(base 亮暗 + 三主题亮暗)对比度全部 ≥4.5;
go test internal/{theme,templates,conf,i18n} 全绿;生成器 validate/fit/quality/palette/layers/fx/fxpipe 全过。
2026-10-21 18:39:08 +08:00

196 lines
9.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{template "base/header" .}}
<style>
/* 自定义灯箱样式 */
.custom-lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.85);
z-index: 9999;
align-items: center;
justify-content: center;
flex-direction: column;
}
.custom-lightbox.active {
display: flex;
}
.custom-lightbox img {
max-width: 90%;
max-height: 80%;
border-radius: var(--tb-radius-sm);
box-shadow: 0 0 30px rgba(0,0,0,0.5);
}
.custom-lightbox .close-btn {
position: absolute;
top: 20px;
right: 30px;
color: #fff;
font-size: 40px;
cursor: pointer;
font-weight: bold;
}
.custom-lightbox .caption {
color: #ccc;
margin-top: 10px;
font-size: 14px;
}
</style>
<div class="uk-container uk-container-small uk-margin-top" data-page-node-id="VJALGxXgA3LjvngL2idiPd">
{{template "base/alert" .}}
<h2 class="uk-text-center" data-page-node-id="4ZhL4EHH1LeTPtFA5wpgbh">
{{ if .IsOwner }}{{t .LangCode "chat.withAsker"}}{{ else }}{{t .LangCode "chat.withUser" .PageUser.Name}}{{ end }}
</h2>
<p class="uk-text-center uk-text-meta" data-page-node-id="NNKGVnXP49GNhyCvho0PAw">
{{ if .Conversation.IsClosed }}
<span class="uk-label uk-label-danger" data-page-node-id="QuKE1FE9CzlNmbQOXq68zA">{{t .LangCode "chat.closed"}}</span>
{{ else }}
{{t .LangCode "chat.remaining" (sub .Conversation.MaxMessages .Conversation.MessageCount) .Conversation.MaxMessages}}
{{ end }}
</p>
<div class="uk-card uk-card-default uk-card-body uk-margin uk-padding-small" data-page-node-id="Ngi6SbUffTDHaNpDFcCzjT">
<ul class="uk-list uk-list-divider" data-page-node-id="VrxQ4Bp6BfmklKAkEqykZf">
{{ range .Messages }}
{{ $isMyMessage := eq .SenderID $.LoggedUser.ID }}
<li class="uk-margin-small-bottom" data-page-node-id="LSFm4EINd1XxnwZSFbAlji">
<div class="uk-flex {{ if $isMyMessage }}uk-flex-right{{ else }}uk-flex-left{{ end }}" data-page-node-id="afhl9aZDsknmAlut7imPnp">
<div class="uk-card uk-card-hover uk-card-body uk-padding-small uk-margin-small-left uk-margin-small-right"
style="max-width: 75%; border-radius: var(--tb-radius); background-color: {{ if $isMyMessage }}#e6ffe6{{ else }}#f0f8ff{{ end }};" data-page-node-id="zPuwctcKF6xPKnwQKaYADD">
<div class="uk-flex uk-flex-middle uk-margin-small-bottom" data-page-node-id="NuHyY57VYOUYQWdpRPqeS6">
<span class="uk-icon" uk-icon="user" data-page-node-id="ctN3HeenrU4wiv39zSfOgY"></span>
<strong class="uk-margin-small-left" data-page-node-id="V7t5pBo43sNBv1a6rvp0jv">
{{ if $isMyMessage }}{{t $.LangCode "chat.me"}}{{ else }}{{t $.LangCode "chat.other"}}{{ end }}
</strong>
<span class="uk-text-meta uk-margin-small-left" style="font-size: var(--tb-fs-xs);" data-page-node-id="JidfstvpABpsDBPRL3WY6I">
{{ .CreatedAt.Format "01-02 15:04" }}
</span>
</div>
{{ if and $.IsOwner $.IsBlocked (eq .SenderID $.Conversation.AskerID) }}
<p class="uk-margin-remove" data-page-node-id="LSDlwCDxP0hWTAw2tjVSvA"><span class="uk-text-muted uk-text-italic" data-page-node-id="nTFMPXU5SSa8Affx4vPAm8">{{t $.LangCode "chat.userBlocked"}}</span></p>
{{ else }}
{{ if .Content }}
<p class="uk-margin-remove" data-page-node-id="KMOMzUxidmbA4tMskOa1z2">{{ .Content }}</p>
{{ end }}
{{ range (index $.MessageImages .ID) }}
<div class="uk-margin-small-top" data-page-node-id="sB1P5iHpDXEjFBdUymfkxz">
<img src="{{ .URL }}" alt="{{ .FileName }}"
style="max-width: 200px; max-height: 200px; border-radius: var(--tb-radius-sm); cursor: pointer;"
onclick="openLightbox('{{ .URL }}', '{{ .FileName }}')" data-page-node-id="siJUfJdLhG2zPrXzGC1KZ3" />
</div>
{{ end }}
{{ end }}
</div>
</div>
</li>
{{ end }}
</ul>
</div>
{{ if .CanSend }}
<div class="uk-card uk-card-default uk-card-body uk-margin-top" data-page-node-id="ZAXQKcFkCuSMdF0fOQ5dYH">
<form method="post" action="/_/{{.PageUser.Domain}}/chat/{{.Conversation.Token}}/send" enctype="multipart/form-data" data-page-node-id="9xSD9GJlKfqAkKPgVBuOAn">
{{ .CSRFTokenHTML }}
<textarea name="content" class="uk-textarea" rows="3" placeholder="{{t .LangCode "chat.input"}}" maxlength="{{ .ChatMessageLimit }}">{{ .Content }}</textarea>
<div class="uk-margin-small-top" style="display: flex; align-items: center; gap: 10px;" data-page-node-id="jtjlLfDzoOkYDKdiOzZ6vl">
<div uk-form-custom data-page-node-id="elqLK3luiM7Q4FamlGb3pV">
<a href="#" class="uk-icon-link" uk-icon="image" data-page-node-id="uZN2LSBiXgLUmffpnhXWH6"></a>
<span style="font-size: 13px; color: #666;" data-page-node-id="TKZ0z7rbdw4u4YcNs6VmAv">{{t .LangCode "chat.addImage"}}</span>
<input type="file" name="image" accept="image/*" data-page-node-id="37rmGJRHggLssaZUDxV4FV">
</div>
<span class="uk-text-meta" id="file-name" data-page-node-id="EBlY0CCL4DtDxgbNBrlLaw"></span>
</div>
<div class="uk-margin-small-top uk-text-right" data-page-node-id="Tr2UTO7GlEX9LFxDVo1RlK">
<button type="submit" class="uk-button uk-button-primary" data-page-node-id="IH6ljh8Db7Wo4xfsEz9P5w">{{t .LangCode "chat.send"}}</button>
</div>
</form>
</div>
{{ else }}
<div class="uk-text-center uk-margin-top" data-page-node-id="ZfG2zLwjExCXoA9YaDrqCn">
<p class="uk-text-warning" data-page-node-id="YKwFufPMOEUVlUvX4yBHAB">{{t .LangCode "chat.closedNoSend"}}</p>
<a href="/_/{{.PageUser.Domain}}" class="uk-button uk-button-default" data-page-node-id="iVHgoa52j3R1yuhJWbLPXF">{{t .LangCode "common.backHome"}}</a>
</div>
{{ end }}
{{ if .IsOwner }}
<div class="uk-margin-top uk-text-center" data-page-node-id="YuzOGI8cGxQAGXx3EsCnKq">
<a class="uk-button uk-button-{{ if .IsBlocked }}default{{ else }}warning{{ end }} uk-button-small" href="#" data-page-node-id="sG8R1GMpGC52pNeUFJEkTD">
{{ if .IsBlocked }}{{t .LangCode "chat.unblock"}}{{ else }}{{t .LangCode "chat.block"}}{{ end }}
</a>
<div class="uk-dropbar uk-dropbar-top" uk-drop="stretch: x; mode: click" data-page-node-id="iW9QyGsZ1XWa4Bq7Ch0805">
<div class="uk-card uk-card-default uk-card-body" data-page-node-id="UebLy6cpqCXeiPoX1JLRcE">
<h3 class="uk-card-title" data-page-node-id="qVRELxzwy4UR2ANNo6PNCi">{{ if .IsBlocked }}{{t .LangCode "chat.unblock"}}{{ else }}{{t .LangCode "chat.block"}}{{ end }}?</h3>
<p data-page-node-id="DrquhcYxj4Dkx7yGjoed4o">{{ if .IsBlocked }}{{t .LangCode "chat.confirmUnblock"}}{{ else }}{{t .LangCode "chat.confirmBlock"}}{{ end }}</p>
<form method="post" action="/_/{{.PageUser.Domain}}/chat/{{.Conversation.Token}}/block" data-page-node-id="gN7rHfCpdQ5b3HR05NNiaI">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-{{ if .IsBlocked }}primary{{ else }}danger{{ end }}" data-page-node-id="Bv6TCmw3NFsNXmUfGQ0s9K">
{{t .LangCode "chat.confirm"}}{{ if .IsBlocked }}{{t .LangCode "chat.unblock"}}{{ else }}{{t .LangCode "chat.block"}}{{ end }}
</button>
</form>
</div>
</div>
<a class="uk-button uk-button-danger uk-button-small" href="#" data-page-node-id="LD7jP6dOcVE2UuLTnEuNCZ">{{t .LangCode "chat.delete"}}</a>
<div class="uk-dropbar uk-dropbar-top" uk-drop="stretch: x; mode: click" data-page-node-id="hmBJTLVxTGAh3eaZJFbd4u">
<div class="uk-card uk-card-default uk-card-body" data-page-node-id="vdlAgEiEKDXhfsLG3U6qt3">
<h3 class="uk-card-title" data-page-node-id="uELuLnRDZnyl09lrlJGgpG">{{t .LangCode "qitem.danger"}}</h3>
<p data-page-node-id="vCyeXBe3K710pdnGTnCpBl">{{t .LangCode "chat.deleteConfirm"}}</p>
<form method="post" action="/_/{{.PageUser.Domain}}/chat/{{.Conversation.Token}}/delete" data-page-node-id="83mnoGpvXiYyvWETOUlsna">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-danger" data-page-node-id="XjUAxGnN5V8BrLG9n4zVcX">{{t .LangCode "qitem.deleteConfirmBtn"}}</button>
</form>
</div>
</div>
</div>
{{ end }}
</div>
<!-- 自定义灯箱结构 -->
<div id="customLightbox" class="custom-lightbox" onclick="closeLightbox()" data-page-node-id="xmqzFCKLG0BSnAeRPCQBEP">
<span class="close-btn" onclick="closeLightbox()" data-page-node-id="UVNFSFTxluTqzyZ4rPyYLe">&times;</span>
<img id="lightboxImg" src="" alt="" data-page-node-id="wX0DtrO8n9OxRAAdFaDtAx" />
<div id="lightboxCaption" class="caption" data-page-node-id="bCQzLy8AgSWOKL7ZcMU3ZR"></div>
</div>
<script>
// 显示已选文件名
var fileInput = document.querySelector('input[name="image"]');
var fileNameSpan = document.getElementById('file-name');
if (fileInput && fileNameSpan) {
fileInput.addEventListener('change', function() {
if (fileInput.files.length > 0) {
fileNameSpan.textContent = fileInput.files[0].name;
} else {
fileNameSpan.textContent = '';
}
});
}
// 自定义灯箱函数
function openLightbox(src, caption) {
document.getElementById('lightboxImg').src = src;
document.getElementById('lightboxCaption').textContent = caption || '';
document.getElementById('customLightbox').classList.add('active');
}
function closeLightbox() {
document.getElementById('customLightbox').classList.remove('active');
// 清空图片避免后台加载消耗
document.getElementById('lightboxImg').src = '';
}
// ESC 关闭
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeLightbox();
}
});
</script>
{{template "base/footer" .}}