styled_crop_and_fix
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustpad"
|
||||
version = "0.2.53"
|
||||
version = "0.2.54"
|
||||
edition = "2024"
|
||||
rust-version = "1.94"
|
||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||
|
||||
+131
-3
@@ -2062,13 +2062,21 @@ dialog::backdrop {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: var(--surface-panel);
|
||||
box-shadow: inset 0 1px 0 var(--wash-soft);
|
||||
}
|
||||
|
||||
.image-editor-controls label {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
color: var(--muted);
|
||||
font-size: .78rem;
|
||||
align-content: start;
|
||||
gap: 7px;
|
||||
min-width: 0;
|
||||
color: var(--text-label);
|
||||
font-size: .76rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.image-editor-controls select,
|
||||
@@ -2076,6 +2084,126 @@ dialog::backdrop {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-editor-controls select {
|
||||
min-height: 40px;
|
||||
padding: 0 36px 0 12px;
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 9px;
|
||||
background-color: var(--control-bg);
|
||||
background-image: linear-gradient(45deg, transparent 50%, var(--select-arrow) 50%),
|
||||
linear-gradient(135deg, var(--select-arrow) 50%, transparent 50%);
|
||||
background-position: calc(100% - 16px) 17px, calc(100% - 11px) 17px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 5px 5px, 5px 5px;
|
||||
box-shadow: inset 0 1px 0 var(--wash-soft);
|
||||
color: var(--text);
|
||||
font-size: .84rem;
|
||||
font-weight: 600;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
cursor: pointer;
|
||||
transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
|
||||
.image-editor-controls select:hover {
|
||||
border-color: var(--select-border);
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
|
||||
.image-editor-controls select:focus,
|
||||
.image-editor-controls select:focus-visible {
|
||||
border-color: var(--focus);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px var(--focus-ring), inset 0 1px 0 var(--wash-soft);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"] {
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
margin: 0;
|
||||
padding: 0 2px;
|
||||
border: 0;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: 5px;
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 999px;
|
||||
background: var(--surface-strong-2);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]::-webkit-slider-thumb {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
margin-top: -7px;
|
||||
border: 2px solid var(--surface-panel);
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 0 1px var(--accent-border), 0 2px 5px var(--shadow-28);
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
transition: transform .12s ease, background-color .12s ease;
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]:hover::-webkit-slider-thumb {
|
||||
background: var(--accent-hover);
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]:focus-visible::-webkit-slider-thumb {
|
||||
box-shadow: 0 0 0 1px var(--accent-border), 0 0 0 4px var(--focus-ring);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]::-moz-range-track {
|
||||
height: 5px;
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 999px;
|
||||
background: var(--surface-strong-2);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]::-moz-range-progress {
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]::-moz-range-thumb {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border: 2px solid var(--surface-panel);
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 0 1px var(--accent-border), 0 2px 5px var(--shadow-28);
|
||||
transition: transform .12s ease, background-color .12s ease;
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]:hover::-moz-range-thumb {
|
||||
background: var(--accent-hover);
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]:focus-visible::-moz-range-thumb {
|
||||
box-shadow: 0 0 0 1px var(--accent-border), 0 0 0 4px var(--focus-ring);
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: .45;
|
||||
}
|
||||
|
||||
.image-editor-controls input[type="range"]:disabled::-webkit-slider-thumb,
|
||||
.image-editor-controls input[type="range"]:disabled::-moz-range-thumb {
|
||||
background: var(--muted-2);
|
||||
box-shadow: 0 0 0 1px var(--border-strong);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.image-editor-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function prepareImageFile(file) {
|
||||
<div class="image-editor-controls">
|
||||
<label>Crop<select data-aspect><option value="original" selected>Whole image</option><option value="free">Free</option><option value="1">Square</option><option value="1.333333">4:3</option><option value="1.777778">16:9</option></select></label>
|
||||
<label>Zoom<input data-zoom type="range" min="1" max="3" value="1" step="0.01"></label>
|
||||
<label>Max size<select data-size><option value="320">320 px</option><option value="480">480 px</option><option value="640">640 px</option><option value="800">800 px</option><option value="1000">1000 px</option><option value="1200">1200 px</option><option value="1600" selected>1600 px</option><option value="2000">2000 px</option><option value="0">Original</option></select></label>
|
||||
<label>Max size<select data-size><option value="320">320 px</option><option value="480">480 px</option><option value="640">640 px</option><option value="800">800 px</option><option value="1000">1000 px</option><option value="1200">1200 px</option><option value="1600">1600 px</option><option value="2000">2000 px</option><option value="0" selected>Original</option></select></label>
|
||||
</div>
|
||||
<div class="image-editor-actions"><button class="secondary-button" value="cancel">Cancel</button><button type="button" class="primary-button" data-apply>Use image</button></div>
|
||||
</form>`;
|
||||
|
||||
+18
-5
@@ -203,12 +203,25 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
||||
return { start: start + text.length, end: start + text.length };
|
||||
}
|
||||
|
||||
function moveCursorToNextLine(position, inputType = "insertText") {
|
||||
const cursor = Math.max(0, Math.min(position, editor.value.length));
|
||||
if (editor.value[cursor] === "\n") {
|
||||
editor.setSelectionRange(cursor + 1, cursor + 1);
|
||||
return { start: cursor + 1, end: cursor + 1 };
|
||||
}
|
||||
return insertText("\n", { start: cursor, end: cursor }, inputType);
|
||||
}
|
||||
|
||||
function insertAttachmentText(text, range = null, inputType = "insertText") {
|
||||
const inserted = insertText(text, range, inputType);
|
||||
return moveCursorToNextLine(inserted.end, inputType);
|
||||
}
|
||||
|
||||
function insertVideoPlayer(text, range = null, inputType = "insertText") {
|
||||
const start = Math.max(0, Math.min(range?.start ?? editor.selectionStart, editor.value.length));
|
||||
const end = Math.max(start, Math.min(range?.end ?? editor.selectionEnd, editor.value.length));
|
||||
const prefix = start > 0 && editor.value[start - 1] !== "\n" ? "\n" : "";
|
||||
const suffix = end < editor.value.length && editor.value[end] !== "\n" ? "\n" : "";
|
||||
return insertText(`${prefix}${text}${suffix}`, { start, end }, inputType);
|
||||
return insertAttachmentText(`${prefix}${text}`, { start, end }, inputType);
|
||||
}
|
||||
|
||||
async function uploadFile(file, onUploaded, insertMode = "auto") {
|
||||
@@ -272,7 +285,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
||||
const insertMode = isLikelyVideoFile(file) ? await chooseVideoInsertMode(file.name) : "auto";
|
||||
if (!insertMode) return;
|
||||
const range = { start: editor.selectionStart, end: editor.selectionEnd };
|
||||
await uploadFile(file, text => insertMode === "player" ? insertVideoPlayer(text, range) : insertText(text, range), insertMode);
|
||||
await uploadFile(file, text => insertMode === "player" ? insertVideoPlayer(text, range) : insertAttachmentText(text, range), insertMode);
|
||||
});
|
||||
|
||||
document.querySelector("#editor-workspace")?.addEventListener("paste", async event => {
|
||||
@@ -307,7 +320,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
||||
const insertMode = isLikelyVideoFile(file) ? await chooseVideoInsertMode(file.name) : "auto";
|
||||
if (insertMode) await uploadFile(file, insertPastedAlias, insertMode);
|
||||
}
|
||||
editor.setSelectionRange(state.cursor, state.cursor);
|
||||
if (state.inserted) moveCursorToNextLine(state.cursor, "insertFromPaste");
|
||||
});
|
||||
|
||||
document.querySelector("#files-button").addEventListener("click", () => loadFiles({ open: true }));
|
||||
@@ -319,7 +332,7 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, getUploadMaxS
|
||||
const mode = addButton.dataset.insertMode;
|
||||
const text = aliasCode(addButton.dataset.name, addButton.dataset.name, addButton.dataset.mime, mode);
|
||||
if (mode === "player") insertVideoPlayer(text);
|
||||
else insertText(text);
|
||||
else insertAttachmentText(text);
|
||||
toast("Added to note");
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user