v0.2.80
This commit is contained in:
Generated
+1
-1
@@ -2581,7 +2581,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustpad"
|
name = "rustpad"
|
||||||
version = "0.2.79"
|
version = "0.2.80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustpad"
|
name = "rustpad"
|
||||||
version = "0.2.79"
|
version = "0.2.80"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.94"
|
rust-version = "1.94"
|
||||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||||
|
|||||||
+43
-60
@@ -2854,7 +2854,6 @@ dialog::backdrop {
|
|||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.files-head-actions .primary-button,
|
|
||||||
.files-head-actions .action-button {
|
.files-head-actions .action-button {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
@@ -2916,28 +2915,9 @@ dialog::backdrop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.file-row-main {
|
.file-row-main {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 44px minmax(0, 1fr);
|
|
||||||
gap: 12px;
|
|
||||||
align-items: center;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-type-badge {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
border: 1px solid var(--border-strong);
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--surface-2);
|
|
||||||
color: var(--accent-text-strong);
|
|
||||||
font-size: .64rem;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: .045em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-copy {
|
.file-copy {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
@@ -2980,9 +2960,7 @@ dialog::backdrop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.file-actions button,
|
.file-actions button,
|
||||||
.file-secondary-actions button {
|
.file-secondary-actions__buttons button {
|
||||||
min-height: 34px;
|
|
||||||
height: 34px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2997,11 +2975,9 @@ dialog::backdrop {
|
|||||||
|
|
||||||
.file-secondary-actions {
|
.file-secondary-actions {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
gap: 8px;
|
||||||
gap: 7px;
|
padding-top: 9px;
|
||||||
align-items: center;
|
|
||||||
padding-top: 11px;
|
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3009,32 +2985,58 @@ dialog::backdrop {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-secondary-actions__buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 7px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.file-code {
|
.file-code {
|
||||||
grid-column: 1 / -1;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
gap: 8px;
|
gap: 5px;
|
||||||
align-items: stretch;
|
align-items: center;
|
||||||
padding-top: 2px;
|
min-height: 30px;
|
||||||
|
padding: 3px 4px 3px 9px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: color-mix(in srgb, var(--surface-2) 74%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-code[hidden] {
|
.file-code[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-code textarea {
|
.file-code-value {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 72px;
|
height: 24px;
|
||||||
max-height: 150px;
|
min-width: 0;
|
||||||
resize: vertical;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 8px;
|
margin: 0;
|
||||||
font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font: 11px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-code button {
|
.file-code-value:focus {
|
||||||
align-self: stretch;
|
color: var(--text);
|
||||||
min-width: 74px;
|
}
|
||||||
|
|
||||||
|
.file-code-copy {
|
||||||
|
min-width: 56px;
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 4px 9px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-secondary-actions [data-show-file-code][aria-pressed="true"] {
|
||||||
|
border-color: var(--border-strong);
|
||||||
|
background: var(--surface-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-delete-notice {
|
.file-delete-notice {
|
||||||
@@ -3105,12 +3107,6 @@ dialog::backdrop {
|
|||||||
animation: file-skeleton-pulse 1.25s ease-in-out infinite;
|
animation: file-skeleton-pulse 1.25s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-skeleton--badge {
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-skeleton--name {
|
.file-skeleton--name {
|
||||||
width: min(260px, 80%);
|
width: min(260px, 80%);
|
||||||
height: 12px;
|
height: 12px;
|
||||||
@@ -3210,9 +3206,6 @@ dialog::backdrop {
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-code {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-insert-dialog {
|
.video-insert-dialog {
|
||||||
@@ -6017,16 +6010,6 @@ dialog::backdrop {
|
|||||||
background: var(--danger-hover);
|
background: var(--danger-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button.compact-button {
|
|
||||||
min-height: 36px;
|
|
||||||
padding: 7px 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-actions button,
|
|
||||||
.file-code button {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-access {
|
.footer-access {
|
||||||
color: var(--muted, var(--muted-fallback));
|
color: var(--muted, var(--muted-fallback));
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
+27
-23
@@ -153,14 +153,6 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileTypeBadge(file) {
|
|
||||||
const filename = String(file?.filename || "");
|
|
||||||
const extension = filename.includes(".") ? filename.split(".").pop().replace(/[^a-z0-9]/gi, "").slice(0, 4) : "";
|
|
||||||
if (extension) return extension.toUpperCase();
|
|
||||||
const major = String(file?.mime_type || "file").split("/", 1)[0];
|
|
||||||
return ({ image: "IMG", video: "VID", audio: "AUD", text: "TXT", application: "FILE" })[major] || "FILE";
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileActionButtons(file) {
|
function fileActionButtons(file) {
|
||||||
const attributes = `data-url="${escapeHtml(file.url)}" data-name="${escapeHtml(file.filename)}" data-mime="${escapeHtml(file.mime_type)}"`;
|
const attributes = `data-url="${escapeHtml(file.url)}" data-name="${escapeHtml(file.filename)}" data-mime="${escapeHtml(file.mime_type)}"`;
|
||||||
const primaryInsert = isVideo(file.mime_type)
|
const primaryInsert = isVideo(file.mime_type)
|
||||||
@@ -170,12 +162,12 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
? `<button class="action-button action-button--secondary compact-button" data-add-file-to-note data-insert-mode="download" ${attributes}>${escapeHtml(t("files.addDownload", {}, "Add download"))}</button>`
|
? `<button class="action-button action-button--secondary compact-button" data-add-file-to-note data-insert-mode="download" ${attributes}>${escapeHtml(t("files.addDownload", {}, "Add download"))}</button>`
|
||||||
: "";
|
: "";
|
||||||
const codeButtons = isVideo(file.mime_type)
|
const codeButtons = isVideo(file.mime_type)
|
||||||
? `<button class="action-button action-button--secondary compact-button" data-show-file-code="link" ${attributes}>${escapeHtml(t("files.link", {}, "Link"))}</button>
|
? `<button class="action-button action-button--secondary compact-button" data-show-file-code="link" aria-pressed="false" ${attributes}>${escapeHtml(t("files.link", {}, "Link"))}</button>
|
||||||
<button class="action-button action-button--secondary compact-button" data-show-file-code="player" ${attributes}>${escapeHtml(t("files.playerCode", {}, "Player code"))}</button>
|
<button class="action-button action-button--secondary compact-button" data-show-file-code="player" aria-pressed="false" ${attributes}>${escapeHtml(t("files.playerCode", {}, "Player code"))}</button>
|
||||||
<button class="action-button action-button--secondary compact-button" data-show-file-code="download" ${attributes}>${escapeHtml(t("files.downloadCode", {}, "Download code"))}</button>`
|
<button class="action-button action-button--secondary compact-button" data-show-file-code="download" aria-pressed="false" ${attributes}>${escapeHtml(t("files.downloadCode", {}, "Download code"))}</button>`
|
||||||
: `<button class="action-button action-button--secondary compact-button" data-show-file-code="link" ${attributes}>${escapeHtml(t("files.link", {}, "Link"))}</button>
|
: `<button class="action-button action-button--secondary compact-button" data-show-file-code="link" aria-pressed="false" ${attributes}>${escapeHtml(t("files.link", {}, "Link"))}</button>
|
||||||
<button class="action-button action-button--secondary compact-button" data-show-file-code="alias" ${attributes}>${escapeHtml(t("files.alias", {}, "Alias"))}</button>
|
<button class="action-button action-button--secondary compact-button" data-show-file-code="alias" aria-pressed="false" ${attributes}>${escapeHtml(t("files.alias", {}, "Alias"))}</button>
|
||||||
<button class="action-button action-button--secondary compact-button" data-show-file-code="markdown" ${attributes}>${escapeHtml(t("files.markdown", {}, "Markdown"))}</button>`;
|
<button class="action-button action-button--secondary compact-button" data-show-file-code="markdown" aria-pressed="false" ${attributes}>${escapeHtml(t("files.markdown", {}, "Markdown"))}</button>`;
|
||||||
const deleteButton = canDelete()
|
const deleteButton = canDelete()
|
||||||
? `<button class="action-button action-button--danger compact-button" data-delete-file="${file.id}" data-file-name="${escapeHtml(file.filename)}">${escapeHtml(t("files.delete", {}, "Delete file"))}</button>`
|
? `<button class="action-button action-button--danger compact-button" data-delete-file="${file.id}" data-file-name="${escapeHtml(file.filename)}">${escapeHtml(t("files.delete", {}, "Delete file"))}</button>`
|
||||||
: "";
|
: "";
|
||||||
@@ -200,7 +192,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fileSearchText(file) {
|
function fileSearchText(file) {
|
||||||
return `${file.filename || ""} ${file.mime_type || ""} ${fileTypeBadge(file)}`.toLocaleLowerCase();
|
return `${file.filename || ""} ${file.mime_type || ""}`.toLocaleLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileRow(file) {
|
function fileRow(file) {
|
||||||
@@ -211,7 +203,6 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
return `
|
return `
|
||||||
<article class="file-row" data-file-row="${file.id}" data-file-search="${escapeHtml(fileSearchText(file))}">
|
<article class="file-row" data-file-row="${file.id}" data-file-search="${escapeHtml(fileSearchText(file))}">
|
||||||
<div class="file-row-main">
|
<div class="file-row-main">
|
||||||
<div class="file-type-badge" aria-hidden="true">${escapeHtml(fileTypeBadge(file))}</div>
|
|
||||||
<div class="file-copy">
|
<div class="file-copy">
|
||||||
<div class="file-name" title="${escapeHtml(file.filename)}">${escapeHtml(file.filename)}</div>
|
<div class="file-name" title="${escapeHtml(file.filename)}">${escapeHtml(file.filename)}</div>
|
||||||
<div class="file-meta">
|
<div class="file-meta">
|
||||||
@@ -223,8 +214,13 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-actions">${actions.primary}</div>
|
<div class="file-actions">${actions.primary}</div>
|
||||||
<div class="file-secondary-actions" hidden>${actions.secondary}</div>
|
<div class="file-secondary-actions" hidden>
|
||||||
<div class="file-code" hidden><textarea readonly aria-label="${escapeHtml(generatedCodeLabel)}"></textarea><button class="action-button action-button--primary compact-button" data-copy-generated>${escapeHtml(t("share.copy", {}, "Copy"))}</button></div>
|
<div class="file-secondary-actions__buttons">${actions.secondary}</div>
|
||||||
|
<div class="file-code" hidden>
|
||||||
|
<input class="file-code-value" type="text" readonly aria-label="${escapeHtml(generatedCodeLabel)}" spellcheck="false">
|
||||||
|
<button class="action-button action-button--secondary file-code-copy" data-copy-generated>${escapeHtml(t("share.copy", {}, "Copy"))}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</article>`;
|
</article>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +254,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
if (refreshing && filesLoaded) return;
|
if (refreshing && filesLoaded) return;
|
||||||
list.innerHTML = Array.from({ length: 3 }, () => `
|
list.innerHTML = Array.from({ length: 3 }, () => `
|
||||||
<div class="file-row file-row--skeleton" aria-hidden="true">
|
<div class="file-row file-row--skeleton" aria-hidden="true">
|
||||||
<div class="file-row-main"><span class="file-skeleton file-skeleton--badge"></span><div class="file-copy"><span class="file-skeleton file-skeleton--name"></span><span class="file-skeleton file-skeleton--meta"></span></div></div>
|
<div class="file-row-main"><div class="file-copy"><span class="file-skeleton file-skeleton--name"></span><span class="file-skeleton file-skeleton--meta"></span></div></div>
|
||||||
<span class="file-skeleton file-skeleton--action"></span>
|
<span class="file-skeleton file-skeleton--action"></span>
|
||||||
</div>`).join("");
|
</div>`).join("");
|
||||||
}
|
}
|
||||||
@@ -431,12 +427,18 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
|
|
||||||
const toggleButton = event.target.closest("[data-toggle-file-actions]");
|
const toggleButton = event.target.closest("[data-toggle-file-actions]");
|
||||||
if (toggleButton) {
|
if (toggleButton) {
|
||||||
const panel = toggleButton.closest(".file-row")?.querySelector(".file-secondary-actions");
|
const row = toggleButton.closest(".file-row");
|
||||||
|
const panel = row?.querySelector(".file-secondary-actions");
|
||||||
if (!panel) return;
|
if (!panel) return;
|
||||||
const expanded = panel.hidden;
|
const expanded = panel.hidden;
|
||||||
panel.hidden = !expanded;
|
panel.hidden = !expanded;
|
||||||
toggleButton.setAttribute("aria-expanded", String(expanded));
|
toggleButton.setAttribute("aria-expanded", String(expanded));
|
||||||
toggleButton.textContent = expanded ? t("files.less", {}, "Less") : t("common.more", {}, "More");
|
toggleButton.textContent = expanded ? t("files.less", {}, "Less") : t("common.more", {}, "More");
|
||||||
|
if (!expanded) {
|
||||||
|
const codePanel = row.querySelector(".file-code");
|
||||||
|
if (codePanel) codePanel.hidden = true;
|
||||||
|
row.querySelectorAll("[data-show-file-code][aria-pressed="true"]").forEach(button => button.setAttribute("aria-pressed", "false"));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,8 +454,9 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
|
|
||||||
const showButton = event.target.closest("[data-show-file-code]");
|
const showButton = event.target.closest("[data-show-file-code]");
|
||||||
if (showButton) {
|
if (showButton) {
|
||||||
const panel = showButton.closest(".file-row").querySelector(".file-code");
|
const row = showButton.closest(".file-row");
|
||||||
const output = panel.querySelector("textarea");
|
const panel = row.querySelector(".file-code");
|
||||||
|
const output = panel.querySelector(".file-code-value");
|
||||||
const safeUrl = safeAttachmentUrl(showButton.dataset.url);
|
const safeUrl = safeAttachmentUrl(showButton.dataset.url);
|
||||||
const absolute = new URL(safeUrl, location.origin).href;
|
const absolute = new URL(safeUrl, location.origin).href;
|
||||||
let text = showButton.dataset.showFileCode === "link" && isVideo(showButton.dataset.mime)
|
let text = showButton.dataset.showFileCode === "link" && isVideo(showButton.dataset.mime)
|
||||||
@@ -470,6 +473,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
}
|
}
|
||||||
output.value = text;
|
output.value = text;
|
||||||
panel.hidden = false;
|
panel.hidden = false;
|
||||||
|
row.querySelectorAll("[data-show-file-code]").forEach(button => button.setAttribute("aria-pressed", String(button === showButton)));
|
||||||
output.focus();
|
output.focus();
|
||||||
output.select();
|
output.select();
|
||||||
return;
|
return;
|
||||||
@@ -478,7 +482,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
|||||||
const copyButton = event.target.closest("[data-copy-generated]");
|
const copyButton = event.target.closest("[data-copy-generated]");
|
||||||
if (copyButton) {
|
if (copyButton) {
|
||||||
try {
|
try {
|
||||||
await copyText(copyButton.closest(".file-code").querySelector("textarea").value);
|
await copyText(copyButton.closest(".file-code").querySelector(".file-code-value").value);
|
||||||
notify("success", "Generated file code copied to the clipboard.", { title: "Copied" });
|
notify("success", "Generated file code copied to the clipboard.", { title: "Copied" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notify("danger", error.message, { title: "Could not copy file code" });
|
notify("danger", error.message, { title: "Could not copy file code" });
|
||||||
|
|||||||
Reference in New Issue
Block a user