Compare commits

...

10 commits

Author SHA1 Message Date
dakkar
70199a8fab merge: handle non-ASCII emoji names (!464)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/464
2024-04-07 15:37:22 +00:00
dakkar
b6f41a28ed pull in sfm-js that supports non-ascii in emoji names 2024-04-07 16:37:31 +01:00
Marie
bb7b4a8ea4 merge: fix: send null for empty edited_at in mastodon api (!487)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/487

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <marie@kaifa.ch>
2024-04-07 15:36:59 +00:00
dakkar
0690b9a429 merge: fix: load libopenmpt on demand (!469)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/469

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <marie@kaifa.ch>
2024-04-07 14:56:16 +00:00
Sugar🍬🍭🏳️‍⚧
e779c1e667 fix: send null for empty edited_at in mastodon api 2024-04-04 10:43:28 +02:00
Alina Sireneva
ecfaf7ff7a chore: added license and patch info 2024-03-14 21:39:34 +03:00
Alina Sireneva
a69315a24b fix: added wasm in vite config 2024-03-14 14:41:24 +03:00
Alina Sireneva
d991eccd3f fix: Promise.resolve 2024-03-11 16:42:10 +03:00
Alina Sireneva
0085305579 fix: load libopenmpt on demand 2024-03-11 15:32:59 +03:00
dakkar
354cb2a675 handle non-ASCII emoji names
* use the more inclusive regexp for validating emoji names
* always normalize emoji names, aliases, categories

the latter point is necessary to allow matching, for example, `ä`
against `a`+combining diaeresis

this will also need to bump the version of `sfm-js` once we merge
https://activitypub.software/TransFem-org/sfm-js/-/merge_requests/2
2024-03-09 12:51:51 +00:00
39 changed files with 158 additions and 62 deletions

View file

@ -88,7 +88,7 @@
"@smithy/node-http-handler": "2.1.10", "@smithy/node-http-handler": "2.1.10",
"@swc/cli": "0.1.63", "@swc/cli": "0.1.63",
"@swc/core": "1.3.107", "@swc/core": "1.3.107",
"@transfem-org/sfm-js": "0.24.4", "@transfem-org/sfm-js": "0.24.5",
"@twemoji/parser": "15.0.0", "@twemoji/parser": "15.0.0",
"accepts": "1.3.8", "accepts": "1.3.8",
"ajv": "8.12.0", "ajv": "8.12.0",

View file

@ -85,7 +85,7 @@ export class ExportCustomEmojisProcessorService {
}); });
for (const emoji of customEmojis) { for (const emoji of customEmojis) {
if (!/^[a-zA-Z0-9_]+$/.test(emoji.name)) { if (!/^[\p{Letter}\p{Number}\p{Mark}_+-]+$/u.test(emoji.name)) {
this.logger.error(`invalid emoji name: ${emoji.name}`); this.logger.error(`invalid emoji name: ${emoji.name}`);
continue; continue;
} }

View file

@ -79,13 +79,14 @@ export class ImportCustomEmojisProcessorService {
continue; continue;
} }
const emojiInfo = record.emoji; const emojiInfo = record.emoji;
if (!/^[a-zA-Z0-9_]+$/.test(emojiInfo.name)) { const nameNfc = emojiInfo.name.normalize('NFC');
this.logger.error(`invalid emojiname: ${emojiInfo.name}`); if (!/^[\p{Letter}\p{Number}\p{Mark}_+-]+$/u.test(nameNfc)) {
this.logger.error(`invalid emojiname: ${nameNfc}`);
continue; continue;
} }
const emojiPath = outputPath + '/' + record.fileName; const emojiPath = outputPath + '/' + record.fileName;
await this.emojisRepository.delete({ await this.emojisRepository.delete({
name: emojiInfo.name, name: nameNfc,
}); });
const driveFile = await this.driveService.addFile({ const driveFile = await this.driveService.addFile({
user: null, user: null,
@ -94,10 +95,10 @@ export class ImportCustomEmojisProcessorService {
force: true, force: true,
}); });
await this.customEmojiService.add({ await this.customEmojiService.add({
name: emojiInfo.name, name: nameNfc,
category: emojiInfo.category, category: emojiInfo.category?.normalize('NFC'),
host: null, host: null,
aliases: emojiInfo.aliases, aliases: emojiInfo.aliases?.map((a: string) => a.normalize('NFC')),
driveFile, driveFile,
license: emojiInfo.license, license: emojiInfo.license,
isSensitive: emojiInfo.isSensitive, isSensitive: emojiInfo.isSensitive,

View file

@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private customEmojiService: CustomEmojiService, private customEmojiService: CustomEmojiService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
await this.customEmojiService.addAliasesBulk(ps.ids, ps.aliases); await this.customEmojiService.addAliasesBulk(ps.ids, ps.aliases.map(a => a.normalize('NFC')));
}); });
} }
} }

View file

@ -40,7 +40,7 @@ export const meta = {
export const paramDef = { export const paramDef = {
type: 'object', type: 'object',
properties: { properties: {
name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, name: { type: 'string', pattern: '^[\\p{Letter}\\p{Number}\\p{Mark}_+-]+$' },
fileId: { type: 'string', format: 'misskey:id' }, fileId: { type: 'string', format: 'misskey:id' },
category: { category: {
type: 'string', type: 'string',
@ -73,18 +73,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private emojiEntityService: EmojiEntityService, private emojiEntityService: EmojiEntityService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const nameNfc = ps.name.normalize('NFC');
const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile);
const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); const isDuplicate = await this.customEmojiService.checkDuplicate(nameNfc);
if (isDuplicate) throw new ApiError(meta.errors.duplicateName); if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
if (driveFile.user !== null) await this.driveFilesRepository.update(driveFile.id, { user: null }); if (driveFile.user !== null) await this.driveFilesRepository.update(driveFile.id, { user: null });
const emoji = await this.customEmojiService.add({ const emoji = await this.customEmojiService.add({
driveFile, driveFile,
name: ps.name, name: nameNfc,
category: ps.category ?? null, category: ps.category?.normalize('NFC') ?? null,
aliases: ps.aliases ?? [], aliases: ps.aliases?.map(a => a.normalize('NFC')) ?? [],
host: null, host: null,
license: ps.license ?? null, license: ps.license ?? null,
isSensitive: ps.isSensitive ?? false, isSensitive: ps.isSensitive ?? false,

View file

@ -82,15 +82,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(); throw new ApiError();
} }
const nameNfc = emoji.name.normalize('NFC');
// Duplication Check // Duplication Check
const isDuplicate = await this.customEmojiService.checkDuplicate(emoji.name); const isDuplicate = await this.customEmojiService.checkDuplicate(nameNfc);
if (isDuplicate) throw new ApiError(meta.errors.duplicateName); if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
const addedEmoji = await this.customEmojiService.add({ const addedEmoji = await this.customEmojiService.add({
driveFile, driveFile,
name: emoji.name, name: nameNfc,
category: emoji.category, category: emoji.category?.normalize('NFC'),
aliases: emoji.aliases, aliases: emoji.aliases?.map(a => a.normalize('NFC')),
host: null, host: null,
license: emoji.license, license: emoji.license,
isSensitive: emoji.isSensitive, isSensitive: emoji.isSensitive,

View file

@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
if (ps.query) { if (ps.query) {
q.andWhere('emoji.name like :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }) q.andWhere('emoji.name like :query', { query: '%' + sqlLikeEscape(ps.query.normalize('NFC')) + '%' })
.orderBy('length(emoji.name)', 'ASC'); .orderBy('length(emoji.name)', 'ASC');
} }

View file

@ -92,17 +92,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
//const emojis = await q.limit(ps.limit).getMany(); //const emojis = await q.limit(ps.limit).getMany();
emojis = await q.orderBy('length(emoji.name)', 'ASC').getMany(); emojis = await q.orderBy('length(emoji.name)', 'ASC').getMany();
const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g); const queryarry = ps.query.match(/:([\p{Letter}\p{Number}\p{Mark}_+-]*):/ug);
if (queryarry) { if (queryarry) {
emojis = emojis.filter(emoji => emojis = emojis.filter(emoji =>
queryarry.includes(`:${emoji.name}:`), queryarry.includes(`:${emoji.name.normalize('NFC')}:`),
); );
} else { } else {
const queryNfc = ps.query!.normalize('NFC');
emojis = emojis.filter(emoji => emojis = emojis.filter(emoji =>
emoji.name.includes(ps.query!) || emoji.name.includes(queryNfc) ||
emoji.aliases.some(a => a.includes(ps.query!)) || emoji.aliases.some(a => a.includes(queryNfc)) ||
emoji.category?.includes(ps.query!)); emoji.category?.includes(queryNfc));
} }
emojis.splice(ps.limit + 1); emojis.splice(ps.limit + 1);
} else { } else {

View file

@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private customEmojiService: CustomEmojiService, private customEmojiService: CustomEmojiService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
await this.customEmojiService.removeAliasesBulk(ps.ids, ps.aliases); await this.customEmojiService.removeAliasesBulk(ps.ids, ps.aliases.map(a => a.normalize('NFC')));
}); });
} }
} }

View file

@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private customEmojiService: CustomEmojiService, private customEmojiService: CustomEmojiService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
await this.customEmojiService.setAliasesBulk(ps.ids, ps.aliases); await this.customEmojiService.setAliasesBulk(ps.ids, ps.aliases.map(a => a.normalize('NFC')));
}); });
} }
} }

View file

@ -36,7 +36,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private customEmojiService: CustomEmojiService, private customEmojiService: CustomEmojiService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
await this.customEmojiService.setCategoryBulk(ps.ids, ps.category ?? null); await this.customEmojiService.setCategoryBulk(ps.ids, ps.category?.normalize('NFC') ?? null);
}); });
} }
} }

View file

@ -40,7 +40,7 @@ export const paramDef = {
type: 'object', type: 'object',
properties: { properties: {
id: { type: 'string', format: 'misskey:id' }, id: { type: 'string', format: 'misskey:id' },
name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, name: { type: 'string', pattern: '^[\\p{Letter}\\p{Number}\\p{Mark}_+-]+$' },
fileId: { type: 'string', format: 'misskey:id' }, fileId: { type: 'string', format: 'misskey:id' },
category: { category: {
type: 'string', type: 'string',
@ -72,6 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private customEmojiService: CustomEmojiService, private customEmojiService: CustomEmojiService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const nameNfc = ps.name?.normalize('NFC');
let driveFile; let driveFile;
if (ps.fileId) { if (ps.fileId) {
driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
@ -83,22 +84,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
emojiId = ps.id; emojiId = ps.id;
const emoji = await this.customEmojiService.getEmojiById(ps.id); const emoji = await this.customEmojiService.getEmojiById(ps.id);
if (!emoji) throw new ApiError(meta.errors.noSuchEmoji); if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
if (ps.name && (ps.name !== emoji.name)) { if (nameNfc && (nameNfc !== emoji.name)) {
const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); const isDuplicate = await this.customEmojiService.checkDuplicate(nameNfc);
if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists);
} }
} else { } else {
if (!ps.name) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.'); if (!nameNfc) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.');
const emoji = await this.customEmojiService.getEmojiByName(ps.name); const emoji = await this.customEmojiService.getEmojiByName(nameNfc);
if (!emoji) throw new ApiError(meta.errors.noSuchEmoji); if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
emojiId = emoji.id; emojiId = emoji.id;
} }
await this.customEmojiService.update(emojiId, { await this.customEmojiService.update(emojiId, {
driveFile, driveFile,
name: ps.name, name: nameNfc,
category: ps.category, category: ps.category?.normalize('NFC'),
aliases: ps.aliases, aliases: ps.aliases?.map(a => a.normalize('NFC')),
license: ps.license, license: ps.license,
isSensitive: ps.isSensitive, isSensitive: ps.isSensitive,
localOnly: ps.localOnly, localOnly: ps.localOnly,

View file

@ -279,7 +279,8 @@ export class MastoConverters {
emoji_reactions: status.emoji_reactions, emoji_reactions: status.emoji_reactions,
bookmarked: false, bookmarked: false,
quote: isQuote ? await this.convertReblog(status.reblog) : false, quote: isQuote ? await this.convertReblog(status.reblog) : false,
edited_at: note.updatedAt?.toISOString(), // optional chaining cannot be used, as it evaluates to undefined, not null
edited_at: note.updatedAt ? note.updatedAt.toISOString() : null,
}); });
} }
} }

View file

@ -43,7 +43,6 @@ html
link(rel='stylesheet' href='/assets/phosphor-icons/bold/style.css') link(rel='stylesheet' href='/assets/phosphor-icons/bold/style.css')
link(rel='stylesheet' href='/static-assets/fonts/sharkey-icons/style.css') link(rel='stylesheet' href='/static-assets/fonts/sharkey-icons/style.css')
link(rel='modulepreload' href=`/vite/${clientEntry.file}`) link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
script(src='/client-assets/libopenmpt.js')
if !config.clientManifestExists if !config.clientManifestExists
script(type="module" src="/vite/@vite/client") script(type="module" src="/vite/@vite/client")
@ -73,7 +72,6 @@ html
script. script.
var VERSION = "#{version}"; var VERSION = "#{version}";
var CLIENT_ENTRY = "#{clientEntry.file}"; var CLIENT_ENTRY = "#{clientEntry.file}";
window.libopenmpt = window.Module;
script(type='application/json' id='misskey_meta' data-generated-at=now) script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson != metaJson

File diff suppressed because one or more lines are too long

View file

@ -21,12 +21,12 @@
"@github/webauthn-json": "2.1.1", "@github/webauthn-json": "2.1.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@misskey-dev/browser-image-resizer": "2024.1.0", "@misskey-dev/browser-image-resizer": "2024.1.0",
"@phosphor-icons/web": "^2.0.3",
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "5.0.5", "@rollup/plugin-replace": "5.0.5",
"@rollup/pluginutils": "5.1.0", "@rollup/pluginutils": "5.1.0",
"@transfem-org/sfm-js": "0.24.4",
"@syuilo/aiscript": "0.17.0", "@syuilo/aiscript": "0.17.0",
"@phosphor-icons/web": "^2.0.3", "@transfem-org/sfm-js": "0.24.5",
"@twemoji/parser": "15.0.0", "@twemoji/parser": "15.0.0",
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"@vue/compiler-sfc": "3.4.21", "@vue/compiler-sfc": "3.4.21",

View file

@ -238,7 +238,7 @@ function exec() {
return; return;
} }
emojis.value = searchEmoji(props.q.toLowerCase(), emojiDb.value); emojis.value = searchEmoji(props.q.normalize('NFC').toLowerCase(), emojiDb.value);
} else if (props.type === 'mfmTag') { } else if (props.type === 'mfmTag') {
if (!props.q || props.q === '') { if (!props.q || props.q === '') {
mfmTags.value = MFM_TAGS; mfmTags.value = MFM_TAGS;

View file

@ -205,7 +205,7 @@ watch(q, () => {
return; return;
} }
const newQ = q.value.replace(/:/g, '').toLowerCase(); const newQ = q.value.replace(/:/g, '').normalize('NFC').toLowerCase();
const searchCustom = () => { const searchCustom = () => {
const max = 100; const max = 100;

View file

@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</div> </div>
<MkButton rounded style="margin: 0 auto;" @click="changeImage">{{ i18n.ts.selectFile }}</MkButton> <MkButton rounded style="margin: 0 auto;" @click="changeImage">{{ i18n.ts.selectFile }}</MkButton>
<MkInput v-model="name" pattern="[a-z0-9_]" autocapitalize="off"> <MkInput v-model="name" autocapitalize="off">
<template #label>{{ i18n.ts.name }}</template> <template #label>{{ i18n.ts.name }}</template>
</MkInput> </MkInput>
<MkInput v-model="category" :datalist="customEmojiCategories"> <MkInput v-model="category" :datalist="customEmojiCategories">

View file

@ -99,7 +99,7 @@ export class Autocomplete {
const isHashtag = hashtagIndex !== -1; const isHashtag = hashtagIndex !== -1;
const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' '); const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' ');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam; const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); const isEmoji = emojiIndex !== -1 && text.split(/:[\p{Letter}\p{Number}\p{Mark}_+-]+:/u).pop()!.includes(':');
let opened = false; let opened = false;
@ -125,7 +125,7 @@ export class Autocomplete {
if (isEmoji && !opened && this.onlyType.includes('emoji')) { if (isEmoji && !opened && this.onlyType.includes('emoji')) {
const emoji = text.substring(emojiIndex + 1); const emoji = text.substring(emojiIndex + 1);
if (!emoji.includes(' ')) { if (!emoji.includes(' ')) {
this.open('emoji', emoji); this.open('emoji', emoji.normalize('NFC'));
opened = true; opened = true;
} }
} }

View file

@ -1,9 +1,12 @@
/* global libopenmpt UTF8ToString writeAsciiToMemory */ // @ts-nocheck
/* eslint-disable */ /* eslint-disable */
const ChiptuneAudioContext = window.AudioContext || window.webkitAudioContext; const ChiptuneAudioContext = window.AudioContext || window.webkitAudioContext;
export function ChiptuneJsConfig (repeatCount: number, context: AudioContext) { let libopenmpt
let libopenmptLoadPromise
export function ChiptuneJsConfig (repeatCount?: number, context?: AudioContext) {
this.repeatCount = repeatCount; this.repeatCount = repeatCount;
this.context = context; this.context = context;
} }
@ -20,6 +23,28 @@ export function ChiptuneJsPlayer (config: object) {
this.volume = 1; this.volume = 1;
} }
ChiptuneJsPlayer.prototype.initialize = function() {
if (libopenmptLoadPromise) return libopenmptLoadPromise;
if (libopenmpt) return Promise.resolve();
libopenmptLoadPromise = new Promise(async (resolve, reject) => {
try {
const { Module } = await import('./libopenmpt/libopenmpt.js');
await new Promise((resolve) => {
Module['onRuntimeInitialized'] = resolve;
})
libopenmpt = Module;
resolve()
} catch (e) {
reject(e)
} finally {
libopenmptLoadPromise = undefined;
}
})
return libopenmptLoadPromise;
}
ChiptuneJsPlayer.prototype.constructor = ChiptuneJsPlayer; ChiptuneJsPlayer.prototype.constructor = ChiptuneJsPlayer;
ChiptuneJsPlayer.prototype.fireEvent = function (eventName: string, response) { ChiptuneJsPlayer.prototype.fireEvent = function (eventName: string, response) {
@ -61,12 +86,12 @@ ChiptuneJsPlayer.prototype.seek = function (position: number) {
ChiptuneJsPlayer.prototype.metadata = function () { ChiptuneJsPlayer.prototype.metadata = function () {
const data = {}; const data = {};
const keys = UTF8ToString(libopenmpt._openmpt_module_get_metadata_keys(this.currentPlayingNode.modulePtr)).split(';'); const keys = libopenmpt.UTF8ToString(libopenmpt._openmpt_module_get_metadata_keys(this.currentPlayingNode.modulePtr)).split(';');
let keyNameBuffer = 0; let keyNameBuffer = 0;
for (const key of keys) { for (const key of keys) {
keyNameBuffer = libopenmpt._malloc(key.length + 1); keyNameBuffer = libopenmpt._malloc(key.length + 1);
writeAsciiToMemory(key, keyNameBuffer); libopenmpt.writeAsciiToMemory(key, keyNameBuffer);
data[key] = UTF8ToString(libopenmpt._openmpt_module_get_metadata(this.currentPlayingNode.modulePtr, keyNameBuffer)); data[key] = libopenmpt.UTF8ToString(libopenmpt._openmpt_module_get_metadata(this.currentPlayingNode.modulePtr, keyNameBuffer));
libopenmpt._free(keyNameBuffer); libopenmpt._free(keyNameBuffer);
} }
return data; return data;
@ -84,7 +109,7 @@ ChiptuneJsPlayer.prototype.unlock = function () {
}; };
ChiptuneJsPlayer.prototype.load = function (input) { ChiptuneJsPlayer.prototype.load = function (input) {
return new Promise((resolve, reject) => { return this.initialize().then(() => new Promise((resolve, reject) => {
if(this.touchLocked) { if(this.touchLocked) {
this.unlock(); this.unlock();
} }
@ -106,7 +131,7 @@ ChiptuneJsPlayer.prototype.load = function (input) {
reject(error); reject(error);
}); });
} }
}); }));
}; };
ChiptuneJsPlayer.prototype.play = function (buffer: ArrayBuffer) { ChiptuneJsPlayer.prototype.play = function (buffer: ArrayBuffer) {
@ -180,7 +205,7 @@ ChiptuneJsPlayer.prototype.getPatternNumRows = function (pattern: number) {
ChiptuneJsPlayer.prototype.getPatternRowChannel = function (pattern: number, row: number, channel: number) { ChiptuneJsPlayer.prototype.getPatternRowChannel = function (pattern: number, row: number, channel: number) {
if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) {
return UTF8ToString(libopenmpt._openmpt_module_format_pattern_row_channel(this.currentPlayingNode.modulePtr, pattern, row, channel, 0, true)); return libopenmpt.UTF8ToString(libopenmpt._openmpt_module_format_pattern_row_channel(this.currentPlayingNode.modulePtr, pattern, row, channel, 0, true));
} }
return ''; return '';
}; };

View file

@ -0,0 +1,25 @@
Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors
Copyright (c) 1997-2003, Olivier Lapicque
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the OpenMPT project nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,23 @@
modifications made to `libopenmpt.js` (can be taken from https://lib.openmpt.org/libopenmpt/download/):
at the beginning of the file:
```js
// @ts-nocheck
/* eslint-disable */
```
at the end of the file:
```js
Module.UTF8ToString = UTF8ToString;
Module.writeAsciiToMemory = writeAsciiToMemory;
export { Module }
```
replace
```
wasmBinaryFile="libopenmpt.wasm"
```
with
```
wasmBinaryFile=new URL("./libopenmpt.wasm", import.meta.url).href
```

View file

@ -8,7 +8,7 @@ import meta from '../../package.json';
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js'; import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
import pluginJson5 from './vite.json5.js'; import pluginJson5 from './vite.json5.js';
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue']; const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue', '.wasm'];
const hash = (str: string, seed = 0): number => { const hash = (str: string, seed = 0): number => {
let h1 = 0xdeadbeef ^ seed, let h1 = 0xdeadbeef ^ seed,

View file

@ -19,6 +19,7 @@ namespace Entity {
content: string content: string
plain_content?: string | null plain_content?: string | null
created_at: string created_at: string
edited_at: string | null
emojis: Emoji[] emojis: Emoji[]
replies_count: number replies_count: number
reblogs_count: number reblogs_count: number

View file

@ -725,6 +725,7 @@ namespace FriendicaAPI {
content: s.content, content: s.content,
plain_content: null, plain_content: null,
created_at: s.created_at, created_at: s.created_at,
edited_at: s.edited_at || null,
emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [], emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [],
replies_count: s.replies_count, replies_count: s.replies_count,
reblogs_count: s.reblogs_count, reblogs_count: s.reblogs_count,

View file

@ -17,6 +17,7 @@ namespace FriendicaEntity {
reblog: Status | null reblog: Status | null
content: string content: string
created_at: string created_at: string
edited_at?: string | null
emojis: Emoji[] emojis: Emoji[]
replies_count: number replies_count: number
reblogs_count: number reblogs_count: number

View file

@ -628,6 +628,7 @@ namespace MastodonAPI {
content: s.content, content: s.content,
plain_content: null, plain_content: null,
created_at: s.created_at, created_at: s.created_at,
edited_at: s.edited_at || null,
emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [], emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [],
replies_count: s.replies_count, replies_count: s.replies_count,
reblogs_count: s.reblogs_count, reblogs_count: s.reblogs_count,

View file

@ -18,6 +18,7 @@ namespace MastodonEntity {
reblog: Status | null reblog: Status | null
content: string content: string
created_at: string created_at: string
edited_at?: string | null
emojis: Emoji[] emojis: Emoji[]
replies_count: number replies_count: number
reblogs_count: number reblogs_count: number

View file

@ -283,6 +283,7 @@ namespace MisskeyAPI {
: '', : '',
plain_content: n.text ? n.text : null, plain_content: n.text ? n.text : null,
created_at: n.createdAt, created_at: n.createdAt,
edited_at: n.updatedAt || null,
emojis: mapEmojis(n.emojis).concat(mapReactionEmojis(n.reactionEmojis)), emojis: mapEmojis(n.emojis).concat(mapReactionEmojis(n.reactionEmojis)),
replies_count: n.repliesCount, replies_count: n.repliesCount,
reblogs_count: n.renoteCount, reblogs_count: n.renoteCount,

View file

@ -7,6 +7,7 @@ namespace MisskeyEntity {
export type Note = { export type Note = {
id: string id: string
createdAt: string createdAt: string
updatedAt?: string | null
userId: string userId: string
user: User user: User
text: string | null text: string | null

View file

@ -357,6 +357,7 @@ namespace PleromaAPI {
content: s.content, content: s.content,
plain_content: s.pleroma.content?.['text/plain'] ? s.pleroma.content['text/plain'] : null, plain_content: s.pleroma.content?.['text/plain'] ? s.pleroma.content['text/plain'] : null,
created_at: s.created_at, created_at: s.created_at,
edited_at: s.edited_at || null,
emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [], emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [],
replies_count: s.replies_count, replies_count: s.replies_count,
reblogs_count: s.reblogs_count, reblogs_count: s.reblogs_count,

View file

@ -18,6 +18,7 @@ namespace PleromaEntity {
reblog: Status | null reblog: Status | null
content: string content: string
created_at: string created_at: string
edited_at?: string | null
emojis: Emoji[] emojis: Emoji[]
replies_count: number replies_count: number
reblogs_count: number reblogs_count: number

View file

@ -49,6 +49,7 @@ const status: Entity.Status = {
content: 'hoge', content: 'hoge',
plain_content: null, plain_content: null,
created_at: '2019-03-26T21:40:32', created_at: '2019-03-26T21:40:32',
edited_at: null,
emojis: [], emojis: [],
replies_count: 0, replies_count: 0,
reblogs_count: 0, reblogs_count: 0,

View file

@ -38,6 +38,7 @@ const status: Entity.Status = {
content: 'hoge', content: 'hoge',
plain_content: 'hoge', plain_content: 'hoge',
created_at: '2019-03-26T21:40:32', created_at: '2019-03-26T21:40:32',
edited_at: null,
emojis: [], emojis: [],
replies_count: 0, replies_count: 0,
reblogs_count: 0, reblogs_count: 0,

View file

@ -37,6 +37,7 @@ const status: Entity.Status = {
content: 'hoge', content: 'hoge',
plain_content: 'hoge', plain_content: 'hoge',
created_at: '2019-03-26T21:40:32', created_at: '2019-03-26T21:40:32',
edited_at: null,
emojis: [], emojis: [],
replies_count: 0, replies_count: 0,
reblogs_count: 0, reblogs_count: 0,

View file

@ -140,8 +140,8 @@ importers:
specifier: 1.3.107 specifier: 1.3.107
version: 1.3.107 version: 1.3.107
'@transfem-org/sfm-js': '@transfem-org/sfm-js':
specifier: 0.24.4 specifier: 0.24.5
version: 0.24.4 version: 0.24.5
'@twemoji/parser': '@twemoji/parser':
specifier: 15.0.0 specifier: 15.0.0
version: 15.0.0 version: 15.0.0
@ -709,8 +709,8 @@ importers:
specifier: 0.17.0 specifier: 0.17.0
version: 0.17.0 version: 0.17.0
'@transfem-org/sfm-js': '@transfem-org/sfm-js':
specifier: 0.24.4 specifier: 0.24.5
version: 0.24.4 version: 0.24.5
'@twemoji/parser': '@twemoji/parser':
specifier: 15.0.0 specifier: 15.0.0
version: 15.0.0 version: 15.0.0
@ -7436,8 +7436,8 @@ packages:
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
dev: false dev: false
/@transfem-org/sfm-js@0.24.4: /@transfem-org/sfm-js@0.24.5:
resolution: {integrity: sha1-0wEXqL5UJseGFO4GGFRrES6NCDk=, tarball: https://activitypub.software/api/v4/projects/2/packages/npm/@transfem-org/sfm-js/-/@transfem-org/sfm-js-0.24.4.tgz} resolution: {integrity: sha1-c9qJO12lIG+kovDGKjZmK2qPqcw=, tarball: https://activitypub.software/api/v4/projects/2/packages/npm/@transfem-org/sfm-js/-/@transfem-org/sfm-js-0.24.5.tgz}
dependencies: dependencies:
'@twemoji/parser': 15.0.0 '@twemoji/parser': 15.0.0
dev: false dev: false