Support pasting and showing images in the wiki.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4655 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2023-12-03 18:03:42 +00:00
parent a060eadab7
commit 5e1ef01bc0
2 changed files with 80 additions and 7 deletions

View File

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "📝",
"previous": "&r+JXDhWclHwMbeZHYjueOnSPaYCAXUlnl1Gyjgw6TxM=.sha256"
"previous": "&C6kk2ozHZKVTtxbEqLOD/xbsCjosnLb6HJWZuPZrtS8=.sha256"
}

View File

@ -20,9 +20,9 @@ class TfWikiDocElement extends LitElement {
}
markdown(md) {
var reader = new commonmark.Parser({safe: true});
var writer = new commonmark.HtmlRenderer();
var parsed = reader.parse(md || '');
let reader = new commonmark.Parser({safe: true});
let writer = new commonmark.HtmlRenderer();
let parsed = reader.parse(md || '');
let walker = parsed.walker();
let event;
while ((event = walker.next())) {
@ -33,6 +33,10 @@ class TfWikiDocElement extends LitElement {
node.destination.indexOf('/') == -1) {
node.destination = `#${this.wiki?.name}/${node.destination}`;
}
} else if (node.type == 'image') {
if (node.destination.startsWith('&')) {
node.destination = '/' + node.destination + '/view';
}
}
}
}
@ -71,13 +75,12 @@ class TfWikiDocElement extends LitElement {
key: this.value.id,
parent: this.value.parent,
blob: id,
mentions: this.blob.match(/(&.{44}.sha256)/g)?.map(x => ({link: x})),
};
if (draft) {
message.recps = this.value.editors;
print(message);
message = await tfrpc.rpc.encrypt(this.whoami, this.value.editors, JSON.stringify(message));
}
print(message);
await tfrpc.rpc.appendMessage(this.whoami, message);
this.is_editing = false;
}
@ -90,6 +93,74 @@ class TfWikiDocElement extends LitElement {
return this.append_message(false);
}
convert_to_format(buffer, type, mime_type) {
return new Promise(function(resolve, reject) {
let img = new Image();
img.onload = function() {
let canvas = document.createElement('canvas');
let width_scale = Math.min(img.width, 1024) / img.width;
let height_scale = Math.min(img.height, 1024) / img.height;
let scale = Math.min(width_scale, height_scale);
canvas.width = img.width * scale;
canvas.height = img.height * scale;
let context = canvas.getContext('2d');
context.drawImage(img, 0, 0, canvas.width, canvas.height);
let data_url = canvas.toDataURL(mime_type);
let result = atob(data_url.split(',')[1]).split('').map(x => x.charCodeAt(0));
resolve(result);
};
img.onerror = function(event) {
reject(new Error('Failed to load image.'));
};
let raw = Array.from(new Uint8Array(buffer)).map(b => String.fromCharCode(b)).join('');
let original = `data:${type};base64,${btoa(raw)}`;
img.src = original;
});
}
async add_file(editor, file) {
try {
let self = this;
let buffer = await file.arrayBuffer();
let type = file.type;
if (type.startsWith('image/')) {
let best_buffer;
let best_type;
for (let format of ['image/png', 'image/jpeg', 'image/webp']) {
let test_buffer = await self.convert_to_format(buffer, file.type, format);
if (!best_buffer || test_buffer.length < best_buffer.length) {
best_buffer = test_buffer;
best_type = format;
}
}
buffer = best_buffer;
type = best_type;
} else {
buffer = Array.from(new Uint8Array(buffer));
}
let id = await tfrpc.rpc.store_blob(buffer);
let name = type.split('/')[0] + ':' + file.name;
editor.value += `\n![${name}](${id})`;
self.on_edit({srcElement: editor});
} catch(e) {
alert(e?.message);
}
}
paste(event) {
let self = this;
for (let item of event.clipboardData.items) {
if (item.type?.startsWith('image/')) {
let file = item.getAsFile();
if (!file) {
continue;
}
self.add_file(event.srcElement, file);
break;
}
}
}
render() {
let value = JSON.stringify(this.value);
if (this.blob_for_value != value) {
@ -110,7 +181,9 @@ class TfWikiDocElement extends LitElement {
<textarea
?hidden=${!this.is_editing}
style="flex: 1 1; min-height: 10em"
@input=${this.on_edit} .value=${this.blob ?? ''}></textarea>
@input=${this.on_edit}
@paste=${this.paste}
.value=${this.blob ?? ''}></textarea>
<div style="flex: 1 1">${unsafeHTML(this.markdown(this.blob))}</div>
</div>
`;