forked from cory/tildefriends
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:
parent
a060eadab7
commit
5e1ef01bc0
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "📝",
|
"emoji": "📝",
|
||||||
"previous": "&r+JXDhWclHwMbeZHYjueOnSPaYCAXUlnl1Gyjgw6TxM=.sha256"
|
"previous": "&C6kk2ozHZKVTtxbEqLOD/xbsCjosnLb6HJWZuPZrtS8=.sha256"
|
||||||
}
|
}
|
@ -20,9 +20,9 @@ class TfWikiDocElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
markdown(md) {
|
markdown(md) {
|
||||||
var reader = new commonmark.Parser({safe: true});
|
let reader = new commonmark.Parser({safe: true});
|
||||||
var writer = new commonmark.HtmlRenderer();
|
let writer = new commonmark.HtmlRenderer();
|
||||||
var parsed = reader.parse(md || '');
|
let parsed = reader.parse(md || '');
|
||||||
let walker = parsed.walker();
|
let walker = parsed.walker();
|
||||||
let event;
|
let event;
|
||||||
while ((event = walker.next())) {
|
while ((event = walker.next())) {
|
||||||
@ -33,6 +33,10 @@ class TfWikiDocElement extends LitElement {
|
|||||||
node.destination.indexOf('/') == -1) {
|
node.destination.indexOf('/') == -1) {
|
||||||
node.destination = `#${this.wiki?.name}/${node.destination}`;
|
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,
|
key: this.value.id,
|
||||||
parent: this.value.parent,
|
parent: this.value.parent,
|
||||||
blob: id,
|
blob: id,
|
||||||
|
mentions: this.blob.match(/(&.{44}.sha256)/g)?.map(x => ({link: x})),
|
||||||
};
|
};
|
||||||
if (draft) {
|
if (draft) {
|
||||||
message.recps = this.value.editors;
|
message.recps = this.value.editors;
|
||||||
print(message);
|
|
||||||
message = await tfrpc.rpc.encrypt(this.whoami, this.value.editors, JSON.stringify(message));
|
message = await tfrpc.rpc.encrypt(this.whoami, this.value.editors, JSON.stringify(message));
|
||||||
}
|
}
|
||||||
print(message);
|
|
||||||
await tfrpc.rpc.appendMessage(this.whoami, message);
|
await tfrpc.rpc.appendMessage(this.whoami, message);
|
||||||
this.is_editing = false;
|
this.is_editing = false;
|
||||||
}
|
}
|
||||||
@ -90,6 +93,74 @@ class TfWikiDocElement extends LitElement {
|
|||||||
return this.append_message(false);
|
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() {
|
render() {
|
||||||
let value = JSON.stringify(this.value);
|
let value = JSON.stringify(this.value);
|
||||||
if (this.blob_for_value != value) {
|
if (this.blob_for_value != value) {
|
||||||
@ -110,7 +181,9 @@ class TfWikiDocElement extends LitElement {
|
|||||||
<textarea
|
<textarea
|
||||||
?hidden=${!this.is_editing}
|
?hidden=${!this.is_editing}
|
||||||
style="flex: 1 1; min-height: 10em"
|
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 style="flex: 1 1">${unsafeHTML(this.markdown(this.blob))}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
Loading…
Reference in New Issue
Block a user