SHA256
UI: уплотнить журнал загрузок Arweave
This commit is contained in:
+1
-1
@@ -1,2 +1,2 @@
|
||||
client.version=1.4.3
|
||||
client.version=1.4.4
|
||||
server.version=1.4.0
|
||||
|
||||
@@ -61,7 +61,8 @@ UI поддерживает три сценария:
|
||||
- выбрать файл из журнала файлов, загруженных в текущей браузерной сессии.
|
||||
|
||||
Журнал хранится только в `sessionStorage` текущего браузера и не является частью блокчейна.
|
||||
В настройках клиента есть отдельный экран журнала загрузок Arweave: пользователь может заранее загрузить файл в Arweave без создания сообщения, увидеть плитку с именем, размером, датой, `txId` и статусом доступности через gateway, а затем выбрать этот файл из истории при создании сообщения.
|
||||
В настройках клиента есть отдельный экран `Загрузить файлы в блокчейн`: пользователь может заранее загрузить файл в Arweave без создания сообщения, увидеть плитку с именем, размером, временем загрузки, `txId` и статусом доступности через gateway, а затем выбрать этот файл из истории при создании сообщения.
|
||||
Если файл загружен заранее, но ещё не был отправлен ни в одном сообщении SHiNE, клиент показывает локальный флаг `Не добавлен в SHiNE`. Флаг снимается после успешной отправки сообщения с этим вложением.
|
||||
Если история очищена, уже созданные сообщения не меняются: в блокчейне остаются attach-блоки с `txId`.
|
||||
|
||||
## Отображение
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# История изменений документации блокчейна
|
||||
|
||||
## 2026-07-31 13:48:57 +0400
|
||||
- Базовый коммит-ориентир: `98eba54`.
|
||||
- Уточнено UI-поведение журнала загрузок Arweave: экран называется `Загрузить файлы в блокчейн`, использует компактные плитки и локальный флаг `Не добавлен в SHiNE` для файлов, которые загружены заранее, но ещё не отправлены во вложении сообщения.
|
||||
- Зафиксировано, что флаг хранится только в клиентском sessionStorage и снимается после успешной отправки сообщения с выбранным вложением; формат attach-блока не изменён.
|
||||
|
||||
## 2026-07-31 13:12:24 +0400
|
||||
- Базовый коммит-ориентир: `c530627`.
|
||||
- Для UI-вложений добавлен отдельный экран журнала загрузок Arweave в настройках: загрузка файла без создания сообщения, плитки истории с `txId`, размером, датой и статусом доступности через gateway.
|
||||
|
||||
@@ -13,13 +13,23 @@ const HISTORY_KEY = 'shine-ui-arweave-attachment-history-v1';
|
||||
const MAX_HISTORY_ITEMS = 100;
|
||||
const RECENT_UPLOAD_MS = 20 * 60 * 1000;
|
||||
|
||||
function normalizeHistoryItem(input = {}) {
|
||||
const item = normalizeAttachment(input);
|
||||
const placedInShineAtMs = Number(input.placedInShineAtMs || 0) || 0;
|
||||
return {
|
||||
...item,
|
||||
pendingPlacement: input.pendingPlacement === true && !placedInShineAtMs,
|
||||
placedInShineAtMs,
|
||||
};
|
||||
}
|
||||
|
||||
export function readArweaveAttachmentHistory(login) {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(HISTORY_KEY);
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
const key = String(login || '').trim().toLowerCase();
|
||||
const rows = Array.isArray(parsed?.[key]) ? parsed[key] : [];
|
||||
return rows.map((item) => normalizeAttachment(item)).filter(Boolean);
|
||||
return rows.map((item) => normalizeHistoryItem(item)).filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -30,7 +40,9 @@ export function saveArweaveAttachmentHistory(login, rows) {
|
||||
const key = String(login || '').trim().toLowerCase();
|
||||
const raw = sessionStorage.getItem(HISTORY_KEY);
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
parsed[key] = (Array.isArray(rows) ? rows : []).slice(0, MAX_HISTORY_ITEMS);
|
||||
parsed[key] = (Array.isArray(rows) ? rows : [])
|
||||
.map((item) => normalizeHistoryItem(item))
|
||||
.slice(0, MAX_HISTORY_ITEMS);
|
||||
sessionStorage.setItem(HISTORY_KEY, JSON.stringify(parsed));
|
||||
} catch {
|
||||
// ignore sessionStorage errors
|
||||
@@ -41,12 +53,52 @@ export function clearArweaveAttachmentHistory(login) {
|
||||
saveArweaveAttachmentHistory(login, []);
|
||||
}
|
||||
|
||||
export function addArweaveAttachmentToHistory(login, attachment) {
|
||||
const item = normalizeAttachment(attachment);
|
||||
const rows = readArweaveAttachmentHistory(login).filter((row) => row.ar !== item.ar);
|
||||
rows.unshift(item);
|
||||
saveArweaveAttachmentHistory(login, rows);
|
||||
return item;
|
||||
export function addArweaveAttachmentToHistory(login, attachment, { pendingPlacement = undefined, markPlaced = false } = {}) {
|
||||
const item = normalizeHistoryItem(attachment);
|
||||
const rows = readArweaveAttachmentHistory(login);
|
||||
const existing = rows.find((row) => row.ar === item.ar) || {};
|
||||
const placedInShineAtMs = markPlaced
|
||||
? Date.now()
|
||||
: Number(existing.placedInShineAtMs || item.placedInShineAtMs || 0) || 0;
|
||||
const nextPendingPlacement = pendingPlacement === undefined
|
||||
? (existing.pendingPlacement === true && !placedInShineAtMs)
|
||||
: (!!pendingPlacement && !placedInShineAtMs);
|
||||
const nextItem = {
|
||||
...item,
|
||||
pendingPlacement: nextPendingPlacement,
|
||||
placedInShineAtMs,
|
||||
};
|
||||
const nextRows = rows.filter((row) => row.ar !== item.ar);
|
||||
nextRows.unshift(nextItem);
|
||||
saveArweaveAttachmentHistory(login, nextRows);
|
||||
return nextItem;
|
||||
}
|
||||
|
||||
export function markArweaveAttachmentPlaced(login, attachment) {
|
||||
const item = normalizeHistoryItem(attachment);
|
||||
const rows = readArweaveAttachmentHistory(login);
|
||||
const existing = rows.find((row) => row.ar === item.ar) || {};
|
||||
const nextItem = {
|
||||
...item,
|
||||
uploadedAtMs: item.uploadedAtMs || existing.uploadedAtMs || Date.now(),
|
||||
pendingPlacement: false,
|
||||
placedInShineAtMs: Number(existing.placedInShineAtMs || 0) || Date.now(),
|
||||
};
|
||||
const nextRows = rows.filter((row) => row.ar !== item.ar);
|
||||
nextRows.unshift(nextItem);
|
||||
saveArweaveAttachmentHistory(login, nextRows);
|
||||
return nextItem;
|
||||
}
|
||||
|
||||
export function formatArweaveHistoryTime(ms) {
|
||||
const value = Number(ms || 0);
|
||||
if (!Number.isFinite(value) || value <= 0) return '—';
|
||||
const diffMs = Date.now() - value;
|
||||
if (diffMs >= 0 && diffMs < RECENT_UPLOAD_MS) {
|
||||
const minutes = Math.max(1, Math.floor(diffMs / 60000));
|
||||
return `${minutes} мин назад`;
|
||||
}
|
||||
return new Date(value).toLocaleString('ru-RU');
|
||||
}
|
||||
|
||||
export function formatArweaveHistoryDateTime(ms) {
|
||||
@@ -87,6 +139,7 @@ export function openArweaveAttachmentManager({
|
||||
gateway,
|
||||
onSelect,
|
||||
selectedTxIds = [],
|
||||
historyOnly = false,
|
||||
} = {}) {
|
||||
const cleanLogin = String(login || '').trim();
|
||||
const cleanStoragePwd = String(storagePwd || '').trim();
|
||||
@@ -118,9 +171,12 @@ export function openArweaveAttachmentManager({
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
function finish(resolve, attachment) {
|
||||
const item = addArweaveAttachmentToHistory(cleanLogin, attachment);
|
||||
if (typeof onSelect === 'function') onSelect(item);
|
||||
function finish(resolve, attachment, { pendingPlacement = undefined } = {}) {
|
||||
const item = addArweaveAttachmentToHistory(cleanLogin, attachment, {
|
||||
pendingPlacement,
|
||||
markPlaced: false,
|
||||
});
|
||||
if (!pendingPlacement && typeof onSelect === 'function') onSelect(item);
|
||||
close(resolve, item);
|
||||
}
|
||||
|
||||
@@ -151,19 +207,19 @@ export function openArweaveAttachmentManager({
|
||||
root.innerHTML = `
|
||||
<div class="modal" data-ar-attach-modal="true">
|
||||
<div class="modal-card stack ar-attachment-manager-card">
|
||||
<h3 class="modal-title">Добавить вложение</h3>
|
||||
<h3 class="modal-title">${historyOnly ? 'Загрузить файл в блокчейн' : 'Добавить вложение'}</h3>
|
||||
<label class="meta-muted" for="ar-attach-wallet">Кошелёк оплаты Arweave</label>
|
||||
<select class="input" id="ar-attach-wallet"></select>
|
||||
<button class="ghost-btn" type="button" data-action="add-wallet">Добавить кошелёк</button>
|
||||
<p class="meta-muted">Для надёжности лучше сначала загрузить файл в Arweave, дождаться доступности в журнале, а потом добавить его из истории загрузок.</p>
|
||||
${historyOnly ? '' : '<p class="meta-muted">Для надёжности лучше сначала загрузить файл в Arweave, дождаться доступности в журнале, а потом добавить его из истории загрузок.</p>'}
|
||||
<label class="meta-muted" for="ar-attach-file">Файл для загрузки</label>
|
||||
<input class="input" id="ar-attach-file" type="file" />
|
||||
<div class="ar-attachment-meta" data-meta="true"></div>
|
||||
<p class="meta-muted inline-error" data-error="true"></p>
|
||||
<div class="form-actions-grid">
|
||||
<button class="secondary-btn" type="button" data-action="existing">Ввести txId</button>
|
||||
<button class="secondary-btn" type="button" data-action="history">История загрузок</button>
|
||||
<button class="primary-btn" type="button" data-action="upload" disabled>Загрузить</button>
|
||||
${historyOnly ? '' : '<button class="secondary-btn" type="button" data-action="existing">Ввести txId</button>'}
|
||||
${historyOnly ? '' : '<button class="secondary-btn" type="button" data-action="history">История загрузок</button>'}
|
||||
<button class="primary-btn" type="button" data-action="upload" disabled>${historyOnly ? 'Загрузить в журнал' : 'Загрузить'}</button>
|
||||
</div>
|
||||
<button class="secondary-btn" type="button" data-action="cancel">Отмена</button>
|
||||
</div>
|
||||
@@ -261,7 +317,7 @@ export function openArweaveAttachmentManager({
|
||||
sha256: selectedSha256,
|
||||
ar: uploaded.id,
|
||||
uploadedAtMs: Date.now(),
|
||||
});
|
||||
}, { pendingPlacement: historyOnly });
|
||||
} catch (error) {
|
||||
uploadBtn.disabled = false;
|
||||
setText(errorEl, error?.message || 'Не удалось загрузить файл в Arweave.');
|
||||
@@ -349,11 +405,13 @@ export function openArweaveAttachmentManager({
|
||||
const isSelected = selectedTxIdSet.has(item.ar);
|
||||
return `
|
||||
<article class="ar-attachment-history-tile${isSelected ? ' is-selected' : ''}" data-history-tile="${index}">
|
||||
<div class="ar-attachment-history-tile-head">
|
||||
<strong>${escapeHtml(item.name)}</strong>
|
||||
<strong class="ar-attachment-history-name">${escapeHtml(item.name)}</strong>
|
||||
<div class="ar-attachment-history-meta-row">
|
||||
<span>${escapeHtml(formatBytes(item.size))}</span>
|
||||
<span>${escapeHtml(formatArweaveHistoryTime(item.uploadedAtMs))}</span>
|
||||
<span class="ar-attachment-status ar-attachment-status--pending" data-status="${index}">Проверяем...</span>
|
||||
${item.pendingPlacement ? '<span class="ar-attachment-placement-flag">Не добавлен в SHiNE</span>' : ''}
|
||||
</div>
|
||||
<div class="meta-muted">${escapeHtml(formatBytes(item.size))} · ${escapeHtml(formatArweaveHistoryDateTime(item.uploadedAtMs))}</div>
|
||||
<div class="mono-cell ar-attachment-history-txid">${escapeHtml(item.ar)}</div>
|
||||
<button class="${isSelected ? 'secondary-btn' : 'ghost-btn'}" type="button" data-pick="${index}" ${isSelected ? 'disabled' : ''}>${isSelected ? 'Уже добавлен ✓' : 'Выбрать'}</button>
|
||||
</article>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import {
|
||||
clearArweaveAttachmentHistory,
|
||||
formatArweaveHistoryDateTime,
|
||||
formatArweaveHistoryTime,
|
||||
getArweaveAttachmentAvailability,
|
||||
openArweaveAttachmentManager,
|
||||
readArweaveAttachmentHistory,
|
||||
@@ -10,7 +9,7 @@ import { formatBytes } from '../services/attachment-format.js';
|
||||
import { toUserMessage } from '../services/ui-error-texts.js';
|
||||
import { state } from '../state.js';
|
||||
|
||||
export const pageMeta = { id: 'arweave-uploads-view', title: 'Загрузки в Arweave' };
|
||||
export const pageMeta = { id: 'arweave-uploads-view', title: 'Загрузить файлы в блокчейн' };
|
||||
|
||||
function setStatusBadge(node, status, label) {
|
||||
if (!node) return;
|
||||
@@ -22,10 +21,8 @@ function renderTile(item, index) {
|
||||
const tile = document.createElement('article');
|
||||
tile.className = 'ar-attachment-history-tile ar-attachment-history-tile--page';
|
||||
|
||||
const head = document.createElement('div');
|
||||
head.className = 'ar-attachment-history-tile-head';
|
||||
|
||||
const name = document.createElement('strong');
|
||||
name.className = 'ar-attachment-history-name';
|
||||
name.textContent = item.name;
|
||||
|
||||
const status = document.createElement('span');
|
||||
@@ -34,15 +31,24 @@ function renderTile(item, index) {
|
||||
status.textContent = 'Проверяем...';
|
||||
|
||||
const meta = document.createElement('div');
|
||||
meta.className = 'meta-muted';
|
||||
meta.textContent = `${formatBytes(item.size)} · ${formatArweaveHistoryDateTime(item.uploadedAtMs)}`;
|
||||
meta.className = 'ar-attachment-history-meta-row';
|
||||
const size = document.createElement('span');
|
||||
size.textContent = formatBytes(item.size);
|
||||
const time = document.createElement('span');
|
||||
time.textContent = formatArweaveHistoryTime(item.uploadedAtMs);
|
||||
meta.append(size, time, status);
|
||||
if (item.pendingPlacement) {
|
||||
const flag = document.createElement('span');
|
||||
flag.className = 'ar-attachment-placement-flag';
|
||||
flag.textContent = 'Не добавлен в SHiNE';
|
||||
meta.append(flag);
|
||||
}
|
||||
|
||||
const txId = document.createElement('div');
|
||||
txId.className = 'mono-cell ar-attachment-history-txid';
|
||||
txId.textContent = item.ar;
|
||||
|
||||
head.append(name, status);
|
||||
tile.append(head, meta, txId);
|
||||
tile.append(name, meta, txId);
|
||||
return tile;
|
||||
}
|
||||
|
||||
@@ -50,25 +56,16 @@ export function render({ navigate }) {
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack arweave-uploads-screen';
|
||||
|
||||
screen.append(
|
||||
renderHeader({
|
||||
title: 'Загрузки в Arweave',
|
||||
leftAction: { label: '←', onClick: () => navigate('settings-view') },
|
||||
}),
|
||||
);
|
||||
|
||||
const intro = document.createElement('div');
|
||||
intro.className = 'card stack';
|
||||
intro.innerHTML = `
|
||||
<p class="field-label">Журнал файлов этой сессии</p>
|
||||
<p class="meta-muted">Здесь можно заранее загрузить файл в Arweave. Он попадёт только в журнал, а в сообщение его потом можно добавить из истории загрузок.</p>
|
||||
`;
|
||||
|
||||
const controls = document.createElement('div');
|
||||
controls.className = 'card arweave-uploads-toolbar';
|
||||
controls.className = 'arweave-uploads-toolbar';
|
||||
controls.innerHTML = `
|
||||
<button class="secondary-btn arweave-uploads-back" type="button" data-action="back" aria-label="Назад">←</button>
|
||||
<button class="primary-btn" type="button" data-action="upload">Добавить файл</button>
|
||||
<button class="secondary-btn" type="button" data-action="clear">Очистить историю</button>
|
||||
<button class="secondary-btn arweave-uploads-menu-btn" type="button" data-action="menu" aria-label="Меню">⋯</button>
|
||||
<div class="arweave-uploads-menu" data-menu="true" hidden>
|
||||
<button class="text-btn" type="button" data-action="clear">Очистить историю</button>
|
||||
<button class="text-btn" type="button" data-action="help">Справка</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const statusLine = document.createElement('p');
|
||||
@@ -80,7 +77,7 @@ export function render({ navigate }) {
|
||||
function renderHistory() {
|
||||
const rows = readArweaveAttachmentHistory(state.session.login).slice().reverse();
|
||||
list.innerHTML = '';
|
||||
statusLine.textContent = rows.length ? `Файлов в журнале: ${rows.length}` : 'Журнал пока пуст.';
|
||||
statusLine.textContent = '';
|
||||
|
||||
if (!rows.length) {
|
||||
const empty = document.createElement('div');
|
||||
@@ -113,6 +110,7 @@ export function render({ navigate }) {
|
||||
login: state.session.login,
|
||||
storagePwd: state.session.storagePwdInMemory,
|
||||
gateway: state.entrySettings.arweaveServer,
|
||||
historyOnly: true,
|
||||
});
|
||||
renderHistory();
|
||||
} catch (error) {
|
||||
@@ -120,14 +118,30 @@ export function render({ navigate }) {
|
||||
}
|
||||
});
|
||||
|
||||
controls.querySelector('[data-action="back"]')?.addEventListener('click', () => navigate('settings-view'));
|
||||
controls.querySelector('[data-action="menu"]')?.addEventListener('click', () => {
|
||||
const menu = controls.querySelector('[data-menu="true"]');
|
||||
if (menu) menu.hidden = !menu.hidden;
|
||||
});
|
||||
controls.querySelector('[data-action="clear"]')?.addEventListener('click', () => {
|
||||
const menu = controls.querySelector('[data-menu="true"]');
|
||||
if (menu) menu.hidden = true;
|
||||
const confirmed = window.confirm('Очистить журнал загруженных файлов этой сессии?');
|
||||
if (!confirmed) return;
|
||||
clearArweaveAttachmentHistory(state.session.login);
|
||||
renderHistory();
|
||||
});
|
||||
controls.querySelector('[data-action="help"]')?.addEventListener('click', () => {
|
||||
const menu = controls.querySelector('[data-menu="true"]');
|
||||
if (menu) menu.hidden = true;
|
||||
window.alert(
|
||||
'Здесь вы можете заранее добавить файл в Arweave. Файл загрузится в блокчейн-хранилище и сохранится в журнале только этой браузерной сессии.\n\n'
|
||||
+ 'Потом при создании сообщения можно открыть историю загрузок и выбрать уже загруженный файл по txId. '
|
||||
+ 'Если файл только что загружен, gateway может несколько минут его не отдавать, поэтому статус может быть “ещё обновляется”.'
|
||||
);
|
||||
});
|
||||
|
||||
screen.append(intro, controls, statusLine, list);
|
||||
screen.append(controls, statusLine, list);
|
||||
renderHistory();
|
||||
return screen;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '../services/channels-ux.js';
|
||||
import { navigateBack } from '../router.js';
|
||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||
import { openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
|
||||
import { markArweaveAttachmentPlaced, openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
|
||||
import {
|
||||
composeMessageWithAttachments,
|
||||
createAttachmentCarouselElement,
|
||||
@@ -409,6 +409,7 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
|
||||
try {
|
||||
await onSubmit(composeMessageWithAttachments(text, attachments));
|
||||
attachments.forEach((attachment) => markArweaveAttachmentPlaced(state.session.login, attachment));
|
||||
close();
|
||||
} catch (error) {
|
||||
setBusy(false);
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '../services/channels-ux.js';
|
||||
import { navigateBack } from '../router.js';
|
||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||
import { openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
|
||||
import { markArweaveAttachmentPlaced, openArweaveAttachmentManager } from '../components/arweave-attachment-manager.js';
|
||||
import {
|
||||
composeMessageWithAttachments,
|
||||
createAttachmentCarouselElement,
|
||||
@@ -384,6 +384,7 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
|
||||
try {
|
||||
await onSubmit(composeMessageWithAttachments(text, attachments));
|
||||
attachments.forEach((attachment) => markArweaveAttachmentPlaced(state.session.login, attachment));
|
||||
close();
|
||||
} catch (error) {
|
||||
setBusy(false);
|
||||
@@ -543,6 +544,7 @@ function openAddMessageModal({ channelName, onSubmit, navigate }) {
|
||||
|
||||
try {
|
||||
await onSubmit(composeMessageWithAttachments(body, attachments));
|
||||
attachments.forEach((attachment) => markArweaveAttachmentPlaced(state.session.login, attachment));
|
||||
close();
|
||||
} catch (error) {
|
||||
setBusy(false);
|
||||
|
||||
@@ -56,7 +56,7 @@ export function render({ navigate }) {
|
||||
</button>
|
||||
<button class="text-btn" type="button" id="settings-arweave-uploads">
|
||||
<span style="display:block; text-align:left;">
|
||||
<strong>Загрузки в Arweave</strong>
|
||||
<strong>Загрузить файлы в блокчейн</strong>
|
||||
<span class="meta-muted" style="display:block; margin-top:0.2rem;">Заранее загрузить файл и выбрать его потом из истории</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -2500,20 +2500,27 @@ textarea.input {
|
||||
|
||||
.ar-attachment-history-tiles {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
max-height: 58vh;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ar-attachment-history-tile {
|
||||
background: linear-gradient(135deg, rgba(248, 250, 252, 0.96), rgba(236, 253, 245, 0.96));
|
||||
border: 1px solid rgba(20, 184, 166, 0.22);
|
||||
border-radius: 1rem;
|
||||
border-radius: 0.8rem;
|
||||
box-sizing: border-box;
|
||||
color: #0f172a;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
padding: 0.9rem;
|
||||
gap: 0.32rem;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
padding: 0.62rem 0.7rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ar-attachment-history-tile.is-selected {
|
||||
@@ -2532,6 +2539,16 @@ textarea.input {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ar-attachment-history-name {
|
||||
display: block;
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.2;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ar-attachment-history-tile-head strong {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -2539,19 +2556,34 @@ textarea.input {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ar-attachment-history-meta-row {
|
||||
align-items: center;
|
||||
color: #64748b;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.76rem;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ar-attachment-history-txid {
|
||||
background: rgba(15, 23, 42, 0.06);
|
||||
border-radius: 0.65rem;
|
||||
padding: 0.45rem 0.55rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.62rem;
|
||||
line-height: 1.25;
|
||||
overflow: hidden;
|
||||
padding: 0.32rem 0.42rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ar-attachment-status {
|
||||
border-radius: 999px;
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.74rem;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
padding: 0.35rem 0.55rem;
|
||||
padding: 0.28rem 0.42rem;
|
||||
}
|
||||
|
||||
.ar-attachment-status--available {
|
||||
@@ -2569,20 +2601,59 @@ textarea.input {
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.ar-attachment-placement-flag {
|
||||
background: rgba(219, 234, 254, 0.96);
|
||||
border-radius: 999px;
|
||||
color: #1e3a8a;
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
padding: 0.28rem 0.42rem;
|
||||
}
|
||||
|
||||
.arweave-uploads-screen {
|
||||
min-height: calc(100vh - 2rem);
|
||||
min-height: calc(100vh - 1rem);
|
||||
}
|
||||
|
||||
.arweave-uploads-toolbar {
|
||||
align-items: center;
|
||||
background: rgba(248, 250, 252, 0.96);
|
||||
border: 1px solid rgba(15, 23, 42, 0.08);
|
||||
border-radius: 0.9rem;
|
||||
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.7rem;
|
||||
gap: 0.5rem;
|
||||
justify-content: space-between;
|
||||
padding: 0.45rem;
|
||||
position: sticky;
|
||||
top: 0.5rem;
|
||||
z-index: 15;
|
||||
}
|
||||
|
||||
.arweave-uploads-back,
|
||||
.arweave-uploads-menu-btn {
|
||||
min-width: 2.6rem;
|
||||
padding-inline: 0.7rem;
|
||||
}
|
||||
|
||||
.arweave-uploads-menu {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(15, 23, 42, 0.12);
|
||||
border-radius: 0.85rem;
|
||||
box-shadow: 0 18px 50px rgba(15, 23, 42, 0.18);
|
||||
display: grid;
|
||||
min-width: 13rem;
|
||||
padding: 0.35rem;
|
||||
position: absolute;
|
||||
right: 0.45rem;
|
||||
top: calc(100% + 0.35rem);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.arweave-uploads-list {
|
||||
max-height: calc(100vh - 18rem);
|
||||
min-height: 18rem;
|
||||
max-height: calc(100vh - 7.5rem);
|
||||
min-height: calc(100vh - 7.5rem);
|
||||
}
|
||||
|
||||
.mono-cell {
|
||||
|
||||
Reference in New Issue
Block a user