Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 2bc63631a4 | ||
|   | 5215721942 | ||
|   | d02e14cb94 | ||
|   | fa75b40dfd | ||
|   | f32d8b7069 | ||
|   | 90e8527556 | ||
|   | 66377d3f27 | ||
|   | c6ae93df80 | ||
|   | 55e9099091 | ||
|   | c6ace29446 | ||
|   | b0b885aacd | 
							
								
								
									
										54
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,6 +1,60 @@ | ||||
| ChangeLog | ||||
| ========= | ||||
|  | ||||
| 12.32.0 (2020/4/16) | ||||
| ------------------- | ||||
| ### ✨Improvements | ||||
| * Pagesで画像を描画できるように | ||||
| * AiScriptのバージョンアップ | ||||
| * 0以下のリアクションは送らないように | ||||
|  | ||||
| ### 🐛Fixes | ||||
| * リアクションの修正 | ||||
| * Fix Media List in CW Content | ||||
|  | ||||
| 12.31.0 (2020/4/14) | ||||
| ------------------- | ||||
| ### ✨Improvements | ||||
| * プロキシの除外ホスト指定とオブジェクトストレージへの適用を除外するオプション | ||||
| * AiScript | ||||
| * モデレーション関連機能 | ||||
| * sensitiveではないメディアも非表示にできるように | ||||
| * 投稿のURLプレビューポップアップを改良 | ||||
| * リモートのカスタム絵文字リアクションを表示できるように | ||||
|  | ||||
| ### 🐛Fixes | ||||
| * リアクションカウントがおかしくなることがあるのを修正 | ||||
|  | ||||
| 12.30.0 (2020/4/11) | ||||
| ------------------- | ||||
| ### ✨Improvements | ||||
| * リクエストライブラリをrequestからnode-fetchに変更 | ||||
| * オブジェクトストレージのhttpスキーマリクエストでもProxyが適用されるように | ||||
| * DNSキャッシュとKeep-Alive適用箇所を増やす | ||||
| * ドイツ語と中国語(繁体)を有効に | ||||
| * NSFWを再度隠せるように | ||||
| * Implement AiScript scratchpad (/scratchpad) | ||||
|  | ||||
| ### 🐛Fixes | ||||
| * APのurl処理の修正 | ||||
|  | ||||
| 12.29.0 (2020/4/5) | ||||
| ------------------- | ||||
| ### ✨Improvements | ||||
| * トークン系の乱数ソースではcryptoを使うように | ||||
| * broadcast stream が追加され emojiAdded イベントをサポート | ||||
| * APIリファレンスの高速化等 | ||||
| * Ability to set header image for a Page | ||||
| * ログの改善 | ||||
|  | ||||
| ### 🐛Fixes | ||||
| * アプリ一覧に1回も使用していないアプリが表示されないのを修正 | ||||
| * admin/accounts/createで一般ユーザーがアカウントを作成し放題なのを修正 | ||||
| * 翻訳の未適用箇所を修正 | ||||
| * APIの権限設定漏れを修正 | ||||
| * インストール直後にアクティビティが飛んで来たりするともう初期管理者セットアップがができなくなるのを修正 | ||||
| * リモート投稿でurlがあればそちらをリンクするように修正 | ||||
|  | ||||
| 12.28.0 (2020/3/29) | ||||
| ------------------- | ||||
| ### ✨Improvements | ||||
|   | ||||
| @@ -41,8 +41,8 @@ addToList: "Zur Liste hinzufügen" | ||||
| sendMessage: "Nachricht senden" | ||||
| copyUsername: "Benutzernamen kopieren" | ||||
| reply: "Antworten" | ||||
| loadMore: "Zeige mehr" | ||||
| youGotNewFollower: "Sie haben einen neuen Follower" | ||||
| loadMore: "Mehr anzeigen" | ||||
| youGotNewFollower: "Du hast einen neuen Follower" | ||||
| receiveFollowRequest: "Follow-Anfrage erhalten." | ||||
| followRequestAccepted: "Follow-Anfrage akzeptiert" | ||||
| mentions: "Erwähnungen" | ||||
| @@ -265,6 +265,7 @@ watch: "Beobachten" | ||||
| unwatch: "Nicht mehr beobachten" | ||||
| accept: "Akzeptieren" | ||||
| reject: "Ablehnen" | ||||
| normal: "Normal" | ||||
| instanceName: "Name der Instanz" | ||||
| instanceDescription: "Beschreibung der Instanz" | ||||
| maintainerName: "Betreiber" | ||||
| @@ -319,6 +320,7 @@ notesAndReplies: "Notizen und Antworten" | ||||
| withFiles: "Dateien beinhalten" | ||||
| silence: "Instanzweit stummschalten" | ||||
| silenceConfirm: "Möchtest du diesen Benutzer wirklich instanzweit stummschalten?" | ||||
| unsilence: "Instanzweite Stummschaltung aufheben" | ||||
| unsilenceConfirm: "Möchtest du die instanzweite Stummschaltung dieses Benutzers wirklich aufheben?" | ||||
| popularUsers: "Beliebte Benutzer" | ||||
| recentlyUpdatedUsers: "Vor kurzem aktive Benutzer" | ||||
| @@ -331,7 +333,7 @@ userList: "Listen" | ||||
| about: "Über" | ||||
| aboutMisskey: "Über Misskey" | ||||
| aboutMisskeyText: "Misskey ist Open-Source-Software die von syuilo seit 2014 entwickelt wird." | ||||
| misskeyMembers: "Sie wird momentan von den unten aufgelisteten Mitgliedern weiterentwickelt und instand gehalten:" | ||||
| misskeyMembers: "Misskey wird momentan von den unten aufgelisteten Mitgliedern weiterentwickelt und instand gehalten:" | ||||
| misskeySource: "Der Quelltext ist hier verfügbar:" | ||||
| misskeyTranslation: "Hilf dabei, Misskey zu übersetzen:" | ||||
| misskeyDonate: "Spende an Misskey, um die Weiterentwicklung zu unterstützen:" | ||||
| @@ -353,7 +355,7 @@ newPasswordIs: "Das neue Passwort ist \"{password}\"" | ||||
| post: "Beitrag" | ||||
| posted: "Gesendet" | ||||
| autoReloadWhenDisconnected: "Automatisch aktualisieren wenn die Serververbindung getrennt wird" | ||||
| autoNoteWatch: "Notiz automatisch beobachten" | ||||
| autoNoteWatch: "Notizen automatisch beobachten" | ||||
| autoNoteWatchDescription: "Werde über Notizen, auf die du reagiert oder geantwortet hast, informiert" | ||||
| reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren" | ||||
| share: "Teilen" | ||||
| @@ -454,6 +456,8 @@ objectStorageRegion: "Region" | ||||
| objectStorageRegionDesc: "Gib eine Region (wie z.B. \"xx-east-1\") an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib \"us-east-1\" an." | ||||
| objectStorageUseSSL: "SSL verwenden" | ||||
| objectStorageUseSSLDesc: "Deaktiviere dies falls du für die API-Verbindungen kein HTTPS verwenden wirst" | ||||
| objectStorageUseProxy: "Über Proxy verbinden" | ||||
| objectStorageUseProxyDesc: "Deaktiviere dies falls du keinen Proxy für den Objektspeicher verwenden wirst" | ||||
| serverLogs: "Serverprotokolle" | ||||
| deleteAll: "Alle löschen" | ||||
| showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen" | ||||
| @@ -476,7 +480,18 @@ state: "Status" | ||||
| sort: "Sortieren" | ||||
| ascendingOrder: "Aufsteigende Reihenfolge" | ||||
| descendingOrder: "Absteigende Reihenfolge" | ||||
| scratchpad: "Testumgebung" | ||||
| scratchpadDescription: "Die Testumgebung bietet eine experimentale Umgebung für AiScript. Dort kannst du AiScript schreiben, ausführen sowie dessen Auswirkungen auf Misskey überprüfen." | ||||
| output: "Ausgabe" | ||||
| script: "Skript" | ||||
| disablePagesScript: "AiScript auf Seiten deaktivieren" | ||||
| updateRemoteUser: "Informationen über den Benutzer der fremder Instanz aktualisieren" | ||||
| deleteAllFiles: "Alle Dateien löschen" | ||||
| deleteAllFilesConfirm: "Möchtest du wirklich alle Dateien löschen?" | ||||
| removeAllFollowing: "Allen gefolgten Benutzern entfolgen" | ||||
| removeAllFollowingDescription: "Allen Benutzerkonten von {host} entfolgen. Bitte führe dies durch, falls diese Instanz nicht mehr existiert." | ||||
| userSuspended: "Dieser Benutzer wurde gesperrt." | ||||
| userSilenced: "Dieser Benutzer wurde instanzweit stummgeschaltet." | ||||
| _theme: | ||||
|   explore: "Themen erforschen" | ||||
|   install: "Thema installieren" | ||||
| @@ -600,7 +615,7 @@ _widgets: | ||||
|   photos: "Fotos" | ||||
| _cw: | ||||
|   hide: "Ausblenden" | ||||
|   show: "Zeige mehr" | ||||
|   show: "Mehr anzeigen" | ||||
|   chars: "{count} Zeichen" | ||||
|   files: "{count} Dateien" | ||||
|   poll: "Umfrage" | ||||
| @@ -656,9 +671,9 @@ _profile: | ||||
|   metadataContent: "Inhalt" | ||||
| _exportOrImport: | ||||
|   allNotes: "Alle Notizen" | ||||
|   followingList: "Folgen" | ||||
|   muteList: "Stummschalten" | ||||
|   blockingList: "Blockieren" | ||||
|   followingList: "Gefolgte Benutzer" | ||||
|   muteList: "Stummschaltungen" | ||||
|   blockingList: "Blockierungen" | ||||
|   userLists: "Listen" | ||||
| _charts: | ||||
|   federationInstancesIncDec: "Unterschied in der Anzahl von förderierenden Instanzen" | ||||
| @@ -780,6 +795,9 @@ _pages: | ||||
|           message: "Nachricht, die bei Aktivierung gezeigt werden soll" | ||||
|           variable: "Variable, die gesendet werden soll" | ||||
|           no-variable: "Keine" | ||||
|         callAiScript: "AiScript ausführen" | ||||
|         _callAiScript: | ||||
|           functionName: "Funktionsname" | ||||
|     radioButton: "Optionsfeld" | ||||
|     _radioButton: | ||||
|       name: "Variablenname" | ||||
| @@ -940,6 +958,7 @@ _pages: | ||||
|       _splitStrByLine: | ||||
|         arg1: "Text" | ||||
|       ref: "Variablen" | ||||
|       aiScriptVar: "AiScript Variablen" | ||||
|       fn: "Funktionen" | ||||
|       _fn: | ||||
|         slots: "Slots" | ||||
|   | ||||
| @@ -770,6 +770,11 @@ _pages: | ||||
|       name: "Variable name" | ||||
|       text: "Title" | ||||
|       default: "Default value" | ||||
|     canvas: "Canvas" | ||||
|     _canvas: | ||||
|       id: "Canvas ID" | ||||
|       width: "Width" | ||||
|       height: "Height" | ||||
|     switch: "Switch" | ||||
|     _switch: | ||||
|       name: "Variable name" | ||||
|   | ||||
| @@ -60,7 +60,7 @@ lists: "Listas" | ||||
| noLists: "No tiene listas" | ||||
| note: "Notas" | ||||
| notes: "Notas" | ||||
| following: "Sigue" | ||||
| following: "Siguiendo" | ||||
| followers: "Seguidores" | ||||
| followsYou: "Te sigue" | ||||
| createList: "Crear lista" | ||||
| @@ -488,6 +488,10 @@ disablePagesScript: "Deshabilitar AiScript en Páginas" | ||||
| updateRemoteUser: "Actualizar información de usuario remoto" | ||||
| deleteAllFiles: "Borrar todos los archivos" | ||||
| deleteAllFilesConfirm: "¿Desea borrar todos los archivos?" | ||||
| removeAllFollowing: "Retener todos los siguientes" | ||||
| removeAllFollowingDescription: "Cancelar todos los siguientes del servidor {host}. Ejecutar en caso de que esta instancia haya dejado de existir" | ||||
| userSuspended: "Este usuario ha sido suspendido." | ||||
| userSilenced: "Este usuario ha sido silenciado." | ||||
| _theme: | ||||
|   explore: "Explorar temas" | ||||
|   install: "Instalar tema" | ||||
| @@ -766,6 +770,11 @@ _pages: | ||||
|       name: "Nombre de variable" | ||||
|       text: "Título" | ||||
|       default: "Valor predeterminado" | ||||
|     canvas: "Lienzo" | ||||
|     _canvas: | ||||
|       id: "Lienzo ID" | ||||
|       width: "Ancho" | ||||
|       height: "Altura" | ||||
|     switch: "Interruptor" | ||||
|     _switch: | ||||
|       name: "Nombre de variable" | ||||
|   | ||||
| @@ -491,7 +491,7 @@ deleteAllFilesConfirm: "Êtes vous surs de vouloir supprimer tous les fichiers ? | ||||
| removeAllFollowing: "Retenir tous les abonnements" | ||||
| removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Exécutez cette commande si l'instance n'existe plus." | ||||
| userSuspended: "Cette utilisateur·trice a été suspendue." | ||||
| userSilenced: "Cette utilisateur·trice a été masquer.\n" | ||||
| userSilenced: "Cette utilisateur·trice a été masquer." | ||||
| _theme: | ||||
|   explore: "Explorer les thèmes" | ||||
|   install: "Installer un thème" | ||||
| @@ -770,6 +770,11 @@ _pages: | ||||
|       name: "Nom de la variable" | ||||
|       text: "Titre" | ||||
|       default: "Valeur par défaut" | ||||
|     canvas: "Toile" | ||||
|     _canvas: | ||||
|       id: "Toile ID" | ||||
|       width: "Largeur" | ||||
|       height: "Hauteur" | ||||
|     switch: "Basculer" | ||||
|     _switch: | ||||
|       name: "Nom de la variable" | ||||
|   | ||||
| @@ -797,6 +797,12 @@ _pages: | ||||
|       text: "タイトル" | ||||
|       default: "デフォルト値" | ||||
|  | ||||
|     canvas: "キャンバス" | ||||
|     _canvas: | ||||
|       id: "キャンバスID" | ||||
|       width: "幅" | ||||
|       height: "高さ" | ||||
|  | ||||
|     switch: "スイッチ" | ||||
|     _switch: | ||||
|       name: "変数名" | ||||
|   | ||||
| @@ -481,7 +481,7 @@ sort: "정렬" | ||||
| ascendingOrder: "오름차순" | ||||
| descendingOrder: "내림차순" | ||||
| scratchpad: "스크래치 패드" | ||||
| scratchpadDescription: "스크래치 패드는 AiScript 의 테스트 환경을 제공합니다.\nMisskey 와 상호 작용하는 코드를 작성, 실행 및 결과를 확인할 수 있습니다." | ||||
| scratchpadDescription: "스크래치 패드는 AiScript 의 테스트 환경을 제공합니다. Misskey 와 상호 작용하는 코드를 작성, 실행 및 결과를 확인할 수 있습니다." | ||||
| output: "출력" | ||||
| script: "스크립트" | ||||
| disablePagesScript: "Pages 에서 AiScript 를 사용하지 않음" | ||||
| @@ -770,6 +770,11 @@ _pages: | ||||
|       name: "변수명" | ||||
|       text: "제목" | ||||
|       default: "기본값" | ||||
|     canvas: "캔버스" | ||||
|     _canvas: | ||||
|       id: "캔버스 ID" | ||||
|       width: "폭" | ||||
|       height: "높이" | ||||
|     switch: "스위치" | ||||
|     _switch: | ||||
|       name: "변수명" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
| 	"name": "misskey", | ||||
| 	"author": "syuilo <syuilotan@yahoo.co.jp>", | ||||
| 	"version": "12.31.0", | ||||
| 	"version": "12.32.0", | ||||
| 	"codename": "indigo", | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
| @@ -42,7 +42,7 @@ | ||||
| 		"@koa/cors": "3.0.0", | ||||
| 		"@koa/multer": "2.0.2", | ||||
| 		"@koa/router": "8.0.8", | ||||
| 		"@syuilo/aiscript": "0.2.0", | ||||
| 		"@syuilo/aiscript": "0.3.0", | ||||
| 		"@types/bcryptjs": "2.4.2", | ||||
| 		"@types/bull": "3.12.1", | ||||
| 		"@types/cbor": "5.0.0", | ||||
|   | ||||
| @@ -34,9 +34,7 @@ export default Vue.extend({ | ||||
| 			default: false | ||||
| 		}, | ||||
| 		// specify the parent element | ||||
| 		parentElement: { | ||||
| 			type: Object | ||||
| 		} | ||||
| 		parentElement: {} | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| @@ -69,7 +67,7 @@ export default Vue.extend({ | ||||
|  | ||||
| 				if (this.$refs.gridOuter) { | ||||
| 					let height = 287; | ||||
| 					const parent = this.$props.parentElement || this.$parent.$el; | ||||
| 					const parent = this.parentElement || this.$parent.$el; | ||||
|  | ||||
| 					if (this.$refs.gridOuter.clientHeight) { | ||||
| 						height = this.$refs.gridOuter.clientHeight; | ||||
| @@ -83,6 +81,11 @@ export default Vue.extend({ | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| 	}, | ||||
| 	watch: { | ||||
| 		parentElement() { | ||||
| 			this.size(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|   | ||||
| @@ -33,7 +33,7 @@ | ||||
| 		<mk-avatar class="avatar" :user="appearNote.user"/> | ||||
| 		<div class="main"> | ||||
| 			<x-note-header class="header" :note="appearNote" :mini="true"/> | ||||
| 			<div class="body" v-if="appearNote.deletedAt == null"> | ||||
| 			<div class="body" v-if="appearNote.deletedAt == null" ref="noteBody"> | ||||
| 				<p v-if="appearNote.cw != null" class="cw"> | ||||
| 				<mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis" /> | ||||
| 					<x-cw-button v-model="showContent" :note="appearNote"/> | ||||
| @@ -46,7 +46,7 @@ | ||||
| 						<a class="rp" v-if="appearNote.renote != null">RN:</a> | ||||
| 					</div> | ||||
| 					<div class="files" v-if="appearNote.files.length > 0"> | ||||
| 						<x-media-list :media-list="appearNote.files"/> | ||||
| 						<x-media-list :media-list="appearNote.files" :parent-element="noteBody"/> | ||||
| 					</div> | ||||
| 					<x-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/> | ||||
| 					<mk-url-preview v-for="url in urls" :url="url" :key="url" :compact="true" class="url-preview"/> | ||||
| @@ -142,6 +142,7 @@ export default Vue.extend({ | ||||
| 			replies: [], | ||||
| 			showContent: false, | ||||
| 			hideThisNote: false, | ||||
| 			noteBody: this.$refs.noteBody, | ||||
| 			faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan | ||||
| 		}; | ||||
| 	}, | ||||
| @@ -254,6 +255,8 @@ export default Vue.extend({ | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 			this.connection.on('_connected_', this.onStreamConnected); | ||||
| 		} | ||||
|  | ||||
| 		this.noteBody = this.$refs.noteBody | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
|   | ||||
| @@ -17,10 +17,11 @@ import XTextarea from './page.textarea.vue'; | ||||
| import XPost from './page.post.vue'; | ||||
| import XCounter from './page.counter.vue'; | ||||
| import XRadioButton from './page.radio-button.vue'; | ||||
| import XCanvas from './page.canvas.vue'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| 		XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton | ||||
| 		XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas | ||||
| 	}, | ||||
| 	props: { | ||||
| 		value: { | ||||
|   | ||||
							
								
								
									
										29
									
								
								src/client/components/page/page.canvas.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/client/components/page/page.canvas.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| <template> | ||||
| <div> | ||||
| 	<canvas ref="canvas" class="ysrxegms" :width="value.width" :height="value.height"/> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	props: { | ||||
| 		value: { | ||||
| 			required: true | ||||
| 		}, | ||||
| 		script: { | ||||
| 			required: true | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		this.script.aoiScript.registerCanvas(this.value.name, this.$refs.canvas); | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .ysrxegms { | ||||
| 	display: block; | ||||
| } | ||||
| </style> | ||||
| @@ -21,39 +21,11 @@ class Script { | ||||
| 	public vars: Record<string, any>; | ||||
| 	public page: Record<string, any>; | ||||
|  | ||||
| 	constructor(page, aoiScript, onError, cb) { | ||||
| 	constructor(page, aoiScript, onError) { | ||||
| 		this.page = page; | ||||
| 		this.aoiScript = aoiScript; | ||||
| 		this.onError = onError; | ||||
|  | ||||
| 		if (this.page.script && this.aoiScript.aiscript) { | ||||
| 			let ast; | ||||
| 			try { | ||||
| 				ast = parse(this.page.script); | ||||
| 			} catch (e) { | ||||
| 				console.error(e); | ||||
| 				/*this.$root.dialog({ | ||||
| 					type: 'error', | ||||
| 					text: 'Syntax error :(' | ||||
| 				});*/ | ||||
| 				return; | ||||
| 			} | ||||
| 			this.aoiScript.aiscript.exec(ast).then(() => { | ||||
| 				this.eval(); | ||||
| 				cb(); | ||||
| 			}).catch(e => { | ||||
| 				console.error(e); | ||||
| 				/*this.$root.dialog({ | ||||
| 					type: 'error', | ||||
| 					text: e | ||||
| 				});*/ | ||||
| 			}); | ||||
| 		} else { | ||||
| 			setTimeout(() => { | ||||
| 				this.eval(); | ||||
| 				cb(); | ||||
| 			}, 1); | ||||
| 		} | ||||
| 		this.eval(); | ||||
| 	} | ||||
|  | ||||
| 	public eval() { | ||||
| @@ -67,13 +39,15 @@ class Script { | ||||
| 	public interpolate(str: string) { | ||||
| 		if (str == null) return null; | ||||
| 		return str.replace(/{(.+?)}/g, match => { | ||||
| 			const v = this.vars[match.slice(1, -1).trim()]; | ||||
| 			const v = this.vars ? this.vars[match.slice(1, -1).trim()] : null; | ||||
| 			return v == null ? 'NULL' : v.toString(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public callAiScript(fn: string) { | ||||
| 		if (this.aoiScript.aiscript) this.aoiScript.aiscript.execFn(this.aoiScript.aiscript.scope.get(fn), []); | ||||
| 		try { | ||||
| 			if (this.aoiScript.aiscript) this.aoiScript.aiscript.execFn(this.aoiScript.aiscript.scope.get(fn), []); | ||||
| 		} catch (e) {} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -101,7 +75,7 @@ export default Vue.extend({ | ||||
| 	created() { | ||||
| 		const pageVars = this.getPageVars(); | ||||
| 		 | ||||
| 		const s = new Script(this.page, new ASEvaluator(this, this.page.variables, pageVars, { | ||||
| 		this.script = new Script(this.page, new ASEvaluator(this, this.page.variables, pageVars, { | ||||
| 			randomSeed: Math.random(), | ||||
| 			visitor: this.$store.state.i, | ||||
| 			page: this.page, | ||||
| @@ -109,15 +83,42 @@ export default Vue.extend({ | ||||
| 			enableAiScript: !this.$store.state.device.disablePagesScript | ||||
| 		}), e => { | ||||
| 			console.dir(e); | ||||
| 		}, () => { | ||||
| 			this.script = s; | ||||
| 		}); | ||||
|  | ||||
| 		if (s.aoiScript.aiscript) s.aoiScript.aiscript.scope.opts.onUpdated = (name, value) => { | ||||
| 			s.eval(); | ||||
| 		if (this.script.aoiScript.aiscript) this.script.aoiScript.aiscript.scope.opts.onUpdated = (name, value) => { | ||||
| 			this.script.eval(); | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.$nextTick(() => { | ||||
| 			if (this.script.page.script && this.script.aoiScript.aiscript) { | ||||
| 				let ast; | ||||
| 				try { | ||||
| 					ast = parse(this.script.page.script); | ||||
| 				} catch (e) { | ||||
| 					console.error(e); | ||||
| 					/*this.$root.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: 'Syntax error :(' | ||||
| 					});*/ | ||||
| 					return; | ||||
| 				} | ||||
| 				this.script.aoiScript.aiscript.exec(ast).then(() => { | ||||
| 					this.script.eval(); | ||||
| 				}).catch(e => { | ||||
| 					console.error(e); | ||||
| 					/*this.$root.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: e | ||||
| 					});*/ | ||||
| 				}); | ||||
| 			} else { | ||||
| 				this.script.eval(); | ||||
| 			} | ||||
| 		}); | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		if (this.script.aoiScript.aiscript) this.script.aoiScript.aiscript.abort(); | ||||
| 	}, | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
| <button | ||||
| 	class="hkzvhatu _button" | ||||
| 	:class="{ reacted: note.myReaction == reaction }" | ||||
| 	:class="{ reacted: note.myReaction == reaction, canToggle }" | ||||
| 	@click="toggleReaction(reaction)" | ||||
| 	v-if="count > 0" | ||||
| 	@mouseover="onMouseover" | ||||
| @@ -40,11 +40,6 @@ export default Vue.extend({ | ||||
| 			type: Object, | ||||
| 			required: true, | ||||
| 		}, | ||||
| 		canToggle: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: true, | ||||
| 		}, | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| @@ -57,6 +52,9 @@ export default Vue.extend({ | ||||
| 		isMe(): boolean { | ||||
| 			return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId; | ||||
| 		}, | ||||
| 		canToggle(): boolean { | ||||
| 			return !this.reaction.match(/@\w/); | ||||
| 		}, | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (!this.isInitial) this.anime(); | ||||
| @@ -144,6 +142,18 @@ export default Vue.extend({ | ||||
| 	padding: 0 6px; | ||||
| 	border-radius: 4px; | ||||
|  | ||||
| 	&.canToggle { | ||||
| 		background: rgba(0, 0, 0, 0.05); | ||||
|  | ||||
| 		&:hover { | ||||
| 			background: rgba(0, 0, 0, 0.1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	&:not(.canToggle) { | ||||
| 		cursor: default; | ||||
| 	} | ||||
|  | ||||
| 	&.reacted { | ||||
| 		background: var(--accent); | ||||
|  | ||||
| @@ -152,14 +162,6 @@ export default Vue.extend({ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	&:not(.reacted) { | ||||
| 		background: rgba(0, 0, 0, 0.05); | ||||
|  | ||||
| 		&:hover { | ||||
| 			background: rgba(0, 0, 0, 0.1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> span { | ||||
| 		font-size: 0.9em; | ||||
| 		line-height: 32px; | ||||
|   | ||||
							
								
								
									
										45
									
								
								src/client/pages/page-editor/els/page-editor.el.canvas.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/client/pages/page-editor/els/page-editor.el.canvas.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| <template> | ||||
| <x-container @remove="() => $emit('remove')" :draggable="true"> | ||||
| 	<template #header><fa :icon="faPaintBrush"/> {{ $t('_pages.blocks.canvas') }}</template> | ||||
|  | ||||
| 	<section style="padding: 0 16px 0 16px;"> | ||||
| 		<mk-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._canvas.id') }}</span></mk-input> | ||||
| 		<mk-input v-model="value.width" type="number"><span>{{ $t('_pages.blocks._canvas.width') }}</span><template #suffix>px</template></mk-input> | ||||
| 		<mk-input v-model="value.height" type="number"><span>{{ $t('_pages.blocks._canvas.height') }}</span><template #suffix>px</template></mk-input> | ||||
| 	</section> | ||||
| </x-container> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { faPaintBrush, faMagic } from '@fortawesome/free-solid-svg-icons'; | ||||
| import i18n from '../../../i18n'; | ||||
| import XContainer from '../page-editor.container.vue'; | ||||
| import MkInput from '../../../components/ui/input.vue'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	i18n, | ||||
|  | ||||
| 	components: { | ||||
| 		XContainer, MkInput | ||||
| 	}, | ||||
|  | ||||
| 	props: { | ||||
| 		value: { | ||||
| 			required: true | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			faPaintBrush, faMagic | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	created() { | ||||
| 		if (this.value.name == null) Vue.set(this.value, 'name', ''); | ||||
| 		if (this.value.width == null) Vue.set(this.value, 'width', 300); | ||||
| 		if (this.value.height == null) Vue.set(this.value, 'height', 200); | ||||
| 	}, | ||||
| }); | ||||
| </script> | ||||
| @@ -20,10 +20,11 @@ import XIf from './els/page-editor.el.if.vue'; | ||||
| import XPost from './els/page-editor.el.post.vue'; | ||||
| import XCounter from './els/page-editor.el.counter.vue'; | ||||
| import XRadioButton from './els/page-editor.el.radio-button.vue'; | ||||
| import XCanvas from './els/page-editor.el.canvas.vue'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| 		XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton | ||||
| 		XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton, XCanvas | ||||
| 	}, | ||||
|  | ||||
| 	props: { | ||||
|   | ||||
| @@ -351,6 +351,7 @@ export default Vue.extend({ | ||||
| 					{ value: 'text', text: this.$t('_pages.blocks.text') }, | ||||
| 					{ value: 'image', text: this.$t('_pages.blocks.image') }, | ||||
| 					{ value: 'textarea', text: this.$t('_pages.blocks.textarea') }, | ||||
| 					{ value: 'canvas', text: this.$t('_pages.blocks.canvas') }, | ||||
| 				] | ||||
| 			}, { | ||||
| 				label: this.$t('_pages.inputBlocks'), | ||||
| @@ -428,8 +429,6 @@ export default Vue.extend({ | ||||
| 	margin-bottom: var(--margin); | ||||
|  | ||||
| 	> header { | ||||
| 		background: var(--faceHeader); | ||||
|  | ||||
| 		> .title { | ||||
| 			z-index: 1; | ||||
| 			margin: 0; | ||||
| @@ -437,8 +436,7 @@ export default Vue.extend({ | ||||
| 			line-height: 42px; | ||||
| 			font-size: 0.9em; | ||||
| 			font-weight: bold; | ||||
| 			color: var(--faceHeaderText); | ||||
| 			box-shadow: 0 var(--lineWidth) rgba(#000, 0.07); | ||||
| 			box-shadow: 0 1px rgba(#000, 0.07); | ||||
|  | ||||
| 			> [data-icon] { | ||||
| 				margin-right: 6px; | ||||
|   | ||||
| @@ -17,8 +17,9 @@ export class ASEvaluator { | ||||
| 	private variables: Variable[]; | ||||
| 	private pageVars: PageVar[]; | ||||
| 	private envVars: Record<keyof typeof envVarsDef, any>; | ||||
| 	public aiscript: AiScript | undefined; | ||||
| 	public aiscript?: AiScript; | ||||
| 	private pageVarUpdatedCallback; | ||||
| 	private canvases: Record<string, HTMLCanvasElement> = {}; | ||||
|  | ||||
| 	private opts: { | ||||
| 		randomSeed: string; visitor?: any; page?: any; url?: string; | ||||
| @@ -36,6 +37,28 @@ export class ASEvaluator { | ||||
| 			}), ...{ | ||||
| 				'MkPages:updated': values.FN_NATIVE(([callback]) => { | ||||
| 					this.pageVarUpdatedCallback = callback; | ||||
| 				}), | ||||
| 				'MkPages:get_canvas': values.FN_NATIVE(([id]) => { | ||||
| 					utils.assertString(id); | ||||
| 					const canvas = this.canvases[id.value]; | ||||
| 					const ctx = canvas.getContext('2d'); | ||||
| 					return values.OBJ(new Map([ | ||||
| 						['clear_rect', values.FN_NATIVE(([x, y, width, height]) => { ctx.clearRect(x.value, y.value, width.value, height.value) })], | ||||
| 						['fill_rect', values.FN_NATIVE(([x, y, width, height]) => { ctx.fillRect(x.value, y.value, width.value, height.value) })], | ||||
| 						['stroke_rect', values.FN_NATIVE(([x, y, width, height]) => { ctx.strokeRect(x.value, y.value, width.value, height.value) })], | ||||
| 						['fill_text', values.FN_NATIVE(([text, x, y, width]) => { ctx.fillText(text.value, x.value, y.value, width ? width.value : undefined) })], | ||||
| 						['stroke_text', values.FN_NATIVE(([text, x, y, width]) => { ctx.strokeText(text.value, x.value, y.value, width ? width.value : undefined) })], | ||||
| 						['set_line_width', values.FN_NATIVE(([width]) => { ctx.lineWidth = width.value })], | ||||
| 						['set_font', values.FN_NATIVE(([font]) => { ctx.font = font.value })], | ||||
| 						['set_fill_style', values.FN_NATIVE(([style]) => { ctx.fillStyle = style.value })], | ||||
| 						['set_stroke_style', values.FN_NATIVE(([style]) => { ctx.strokeStyle = style.value })], | ||||
| 						['begin_path', values.FN_NATIVE(() => { ctx.beginPath() })], | ||||
| 						['close_path', values.FN_NATIVE(() => { ctx.closePath() })], | ||||
| 						['move_to', values.FN_NATIVE(([x, y]) => { ctx.moveTo(x.value, y.value) })], | ||||
| 						['line_to', values.FN_NATIVE(([x, y]) => { ctx.lineTo(x.value, y.value) })], | ||||
| 						['fill', values.FN_NATIVE(() => { ctx.fill() })], | ||||
| 						['stroke', values.FN_NATIVE(() => { ctx.stroke() })], | ||||
| 					])); | ||||
| 				}) | ||||
| 			}}, { | ||||
| 				in: (q) => { | ||||
| @@ -73,10 +96,15 @@ export class ASEvaluator { | ||||
| 			IS_CAT: opts.visitor ? opts.visitor.isCat : false, | ||||
| 			SEED: opts.randomSeed ? opts.randomSeed : '', | ||||
| 			YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`, | ||||
| 			AISCRIPT_DISABLED: !this.opts.enableAiScript, | ||||
| 			NULL: null | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	public registerCanvas(id: string, canvas: any) { | ||||
| 		this.canvases[id] = canvas; | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public updatePageVar(name: string, value: any) { | ||||
| 		const pageVar = this.pageVars.find(v => v.name === name); | ||||
| @@ -147,7 +175,11 @@ export class ASEvaluator { | ||||
|  | ||||
| 		if (block.type === 'aiScriptVar') { | ||||
| 			if (this.aiscript) { | ||||
| 				return utils.valToJs(this.aiscript.scope.get(block.value)); | ||||
| 				try { | ||||
| 					return utils.valToJs(this.aiscript.scope.get(block.value)); | ||||
| 				} catch (e) { | ||||
| 					return null; | ||||
| 				} | ||||
| 			} else { | ||||
| 				return null; | ||||
| 			} | ||||
|   | ||||
| @@ -128,6 +128,7 @@ export const envVarsDef: Record<string, Type> = { | ||||
| 	IS_CAT: 'boolean', | ||||
| 	SEED: null, | ||||
| 	YMD: 'string', | ||||
| 	AISCRIPT_DISABLED: 'boolean', | ||||
| 	NULL: null, | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -26,6 +26,8 @@ export function convertLegacyReactions(reactions: Record<string, number>) { | ||||
| 	const _reactions = {} as Record<string, number>; | ||||
|  | ||||
| 	for (const reaction of Object.keys(reactions)) { | ||||
| 		if (reactions[reaction] <= 0) continue; | ||||
|  | ||||
| 		if (Object.keys(legacies).includes(reaction)) { | ||||
| 			if (_reactions[legacies[reaction]]) { | ||||
| 				_reactions[legacies[reaction]] += reactions[reaction]; | ||||
| @@ -68,7 +70,7 @@ export async function toDbReaction(reaction?: string | null, reacterHost?: strin | ||||
| 		return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, ''); | ||||
| 	} | ||||
|  | ||||
| 	const custom = reaction.match(/^:([\w+-]+):$/); | ||||
| 	const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/); | ||||
| 	if (custom) { | ||||
| 		const name = custom[1]; | ||||
| 		const emoji = await Emojis.findOne({ | ||||
|   | ||||
| @@ -72,13 +72,13 @@ export default async (user: User, note: Note, reaction?: string) => { | ||||
|  | ||||
| 	if (emoji) { | ||||
| 		emoji = { | ||||
| 			name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}`, | ||||
| 			name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}@.`, | ||||
| 			url: emoji.url | ||||
| 		} as any; | ||||
| 	} | ||||
|  | ||||
| 	publishNoteStream(note.id, 'reacted', { | ||||
| 		reaction: reaction, | ||||
| 		reaction: decodedReaction.reaction, | ||||
| 		emoji: emoji, | ||||
| 		userId: user.id | ||||
| 	}); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import { IdentifiableError } from '../../../misc/identifiable-error'; | ||||
| import { User, IRemoteUser } from '../../../models/entities/user'; | ||||
| import { Note } from '../../../models/entities/note'; | ||||
| import { NoteReactions, Users, Notes } from '../../../models'; | ||||
| import { decodeReaction } from '../../../misc/reaction-lib'; | ||||
|  | ||||
| export default async (user: User, note: Note) => { | ||||
| 	// if already unreacted | ||||
| @@ -38,7 +39,7 @@ export default async (user: User, note: Note) => { | ||||
| 	Notes.decrement({ id: note.id }, 'score', 1); | ||||
|  | ||||
| 	publishNoteStream(note.id, 'unreacted', { | ||||
| 		reaction: exist.reaction, | ||||
| 		reaction: decodeReaction(exist.reaction).reaction, | ||||
| 		userId: user.id | ||||
| 	}); | ||||
|  | ||||
|   | ||||
| @@ -144,10 +144,10 @@ | ||||
|   dependencies: | ||||
|     type-detect "4.0.8" | ||||
|  | ||||
| "@syuilo/aiscript@0.2.0": | ||||
|   version "0.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.2.0.tgz#dcb489bca13f6d965ac86034a45fd46514b1487a" | ||||
|   integrity sha512-N9fYchn3zjtniG9fNmZ81PwYZFdulk+RSBcjDZWBgHsFJQc1wxOCr9hZux/vSXrZ/ZWEzK0loNz1dorl2jJLeA== | ||||
| "@syuilo/aiscript@0.3.0": | ||||
|   version "0.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.3.0.tgz#cb0645df40ae97a54eb7e318abef2ccb8045aa14" | ||||
|   integrity sha512-jjtcFqnp5ryzAU3mxP25YJEJH/FmIrMycnFwSer/q1BVsAIqHOIhnRTWjxjVI3n1YHIO5DSD4yG/Em6I3bxJow== | ||||
|   dependencies: | ||||
|     "@types/seedrandom" "2.4.28" | ||||
|     autobind-decorator "2.4.0" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user