This commit is contained in:
2024-11-24 18:03:06 +00:00
parent 641938f7e4
commit 16ba22d90b
39 changed files with 76 additions and 254 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ dist-ssr
*.local
maps
public/maps/*
public/soundfx/*
# Editor directories and files
.vscode/*

View File

@@ -41,14 +41,14 @@
<div class="a-card__popover" popover ref="popover">
<DeviceVolume
class="a-card__popover-volumes"
:deviceSource="(device) => node.getDeviceSource(device).gainNode"
:deviceSource="(device) => node.getDeviceSource(device as any).gainNode"
/>
<slot name="popover"></slot>
</div>
</div>
</template>
<script setup lang="ts">
import {mergeProps, ref} from 'vue';
import {ref, UnwrapNestedRefs} from 'vue';
import {SoundGroupNode} from '../../model/Audio/SoundGroupNode';
import Play from '../Icons/Play.vue';
@@ -71,7 +71,7 @@ const emits = defineEmits<{
type Supports = 'next' | 'prev' | 'togglePlay' | 'stop' | 'play';
const props = withDefaults(defineProps<{
node: SoundGroupNode<any>
node: UnwrapNestedRefs<SoundGroupNode<any>>
supports?: Supports[]
label?: string,
noDetail?: boolean

View File

@@ -31,7 +31,7 @@ const props = withDefaults(defineProps<{
const devicesWithSink = computed(() => {
return deviceManager.audioDevices.map((device) => ({
device,
audioSink: props.deviceSource(device)
audioSink: props.deviceSource(device as any)
}));
});

View File

@@ -49,9 +49,10 @@ import AudioCard from './AudioCard.vue';
import {PlaylistGroupNode} from '../../model/Audio/PlaylistGroupNode';
import Play from '../Icons/Play.vue';
import {SoundNode} from '../../model/Audio/SoundNode';
import {UnwrapNestedRefs} from 'vue';
const props = defineProps<{
node: PlaylistGroupNode
node: UnwrapNestedRefs<PlaylistGroupNode>
}>();
const emit = defineEmits<{

View File

@@ -28,7 +28,7 @@
</AudioCard>
</template>
<script setup lang="ts">
import {computed} from 'vue';
import {computed, UnwrapNestedRefs} from 'vue';
import {SfxGroupNode} from '../../model/Audio/SfxGroupNode';
import AudioCard from './AudioCard.vue';
import {groupBy} from '../../utils/groupBy';
@@ -36,7 +36,7 @@ import SfxSound from './SfxSound.vue';
const props = defineProps<{
node: SfxGroupNode,
node: UnwrapNestedRefs<SfxGroupNode>,
}>();
const emit = defineEmits<{

View File

@@ -39,20 +39,17 @@
</div>
</template>
<script setup lang="ts">
import {inject, reactive, ref, watch} from 'vue';
import {inject, reactive, watch} from 'vue';
import Playlist from './Playlist.vue';
import {PlaylistGroupNode} from '../../model/Audio/PlaylistGroupNode';
import {AudioContextSymbol, MountedFsSymbol, RemoteDeviceManagerSymbol} from '../../model/injectionSymbols';
import {SfxGroupNode} from '../../model/Audio/SfxGroupNode';
import Sfx from './Sfx.vue';
import Modal from './modal.vue';
import {TabSourceNode} from '../../model/Audio/TabSourceNode';
import TabSource from './TabSource.vue';
import DeviceVolume from './DeviceVolume.vue';
import {AudioNodeManager} from '../../model/Audio/AudioNodesManager';
import {debounce} from '../../utils/debounce';
import IconPlus from '../Icons/IconPlus.vue';
import {SoundNode} from '../../model/Audio/SoundNode';
const fs = inject(MountedFsSymbol)!;
const audioContext = inject(AudioContextSymbol) as AudioContext;
@@ -94,15 +91,16 @@ function addNode(name: string) {
}
}
const deleteNode = (node: SoundNode) => {
const deleteNode = (node: any) => {
node.stop?.();
node.disconnect();
// @ts-ignore
nodes.splice(nodes.indexOf(node), 1);
}
// @ts-ignore
watch(() => nodes, debounce((newNodes) => {
console.log('NEW_NODES', newNodes);
// @ts-ignore
audioNodesManager.store(newNodes);
}, 500), {
deep: true,

View File

@@ -13,6 +13,7 @@
</template>
<script setup lang="ts">
import {UnwrapNestedRefs} from 'vue';
import {TabSourceNode} from '../../model/Audio/TabSourceNode';
import AudioCard from './AudioCard.vue';
@@ -20,7 +21,7 @@ const emit = defineEmits<{
'delete': [],
}>();
const props = defineProps<{
node: TabSourceNode,
defineProps<{
node: UnwrapNestedRefs<TabSourceNode>,
}>();
</script>

View File

@@ -29,7 +29,7 @@ function onCancel(event: Event) {
}
}
function onClose(event: Event) {
function onClose() {
emit('update:open', false);
}

View File

@@ -62,6 +62,7 @@ function switchMain(section: string){
}
onMounted(() => {
//@ts-ignore
window.addEventListener('reveal-screen', (event: CustomEvent) => {
switchMain('map');
});

View File

@@ -9,7 +9,7 @@
<script setup lang="ts">
import {ref} from 'vue';
const props = defineProps<{
defineProps<{
name: string
}>()

View File

@@ -18,21 +18,19 @@ const emit = defineEmits<{
dropFile: [VirtualFile],
}>();
const props = defineProps<{
defineProps<{
accept?: string,
}>();
const slot = defineSlots<{
default(props: {isDragOver: boolean}): any
}>()
// const slot = defineSlots<{
// default(props: {isDragOver: boolean}): any
// }>()
function onDragEnter(event: DragEvent) {
console.log('drag enter');
function onDragEnter() {
isDragOver.value = true;
}
function onDragLeave(event: DragEvent) {
console.log('drag leave');
function onDragLeave() {
isDragOver.value = false;
}

View File

@@ -44,8 +44,10 @@ watch(()=> props.file, async (file)=>{
currentPreviewUrl.value = null;
try {
console.log('downloading thumbnail');
// @ts-ignore
const blob = await fs.downloadThumbnail(file, {abortSignal: abort.signal});
currentPreviewUrl.value = URL.createObjectURL(blob);
// @ts-ignore
currentPrevewType.value = file.mimeType;
console.log('currentPreviewUrl', currentPreviewUrl.value);
abort = undefined;

View File

@@ -27,7 +27,6 @@
<script setup lang="ts">
import {onUnmounted, ref} from 'vue';
import FileManager from '../FileManager/FileManager.vue';
import FilePreview from '../FileManager/FilePreview.vue';
import {VirtualFile} from '../../model/FileSystem/types';
const popover = ref<HTMLDivElement>()
@@ -49,7 +48,7 @@ function onDragStart(){
dragging.value = true;
}
function onDragLeave(event) {
function onDragLeave(event: any) {
if (!content.value?.contains(event.relatedTarget)) {
popover.value?.hidePopover();
}

View File

@@ -1,10 +1,6 @@
<template>
<Dropzone
v-slot="{isDragOver}"
class="map-control"
:class="{
'map-control--over': isDragOver
}"
@dropFile="onDrop"
@mousewheel="onScroll"
@mousedown="onMouseDown"
@@ -44,22 +40,15 @@
</template>
<script setup lang="ts">
import {computed, inject, onBeforeUnmount, onMounted, reactive, ref} from 'vue';
import {inject, onBeforeUnmount, onMounted, ref} from 'vue';
import Dropzone from '../Dropzone/Dropzone.vue';
import MapRenderer from './MapRenderer.vue';
import {MountedFsSymbol, RemoteDeviceManagerSymbol} from '../../model/injectionSymbols';
import {closerToZero, MapDrawable, MapImage} from './helpers';
import {MountedFsSymbol} from '../../model/injectionSymbols';
import {closerToZero} from './helpers';
import {VirtualFile} from '../../model/FileSystem/types';
import {CanvasDisplay, MapLayerDrawable} from './object';
import {Devices} from '../../model/Remote/RemoteDeviceManager';
const fs = inject(MountedFsSymbol)!;
const remoteManager = inject(RemoteDeviceManagerSymbol)!;
const screens = computed(() => {
return remoteManager.devices
.filter(device => device.hasImageSink)
})
const canvas = ref<HTMLCanvasElement>();
const selectedScreen = ref<Devices>();
@@ -131,7 +120,7 @@ function onMouseDown(event: MouseEvent) {
window.addEventListener('mouseup', onMouseUp);
}
function onMouseUp(event: MouseEvent) {
function onMouseUp() {
dragging.value = false;
window.removeEventListener('mouseup', onMouseUp);
@@ -148,7 +137,7 @@ function onMouseMove(event: MouseEvent) {
}
if (event.buttons === 0) {
onMouseUp(event);
onMouseUp();
return;
}
@@ -193,6 +182,7 @@ function onKeyup(event: KeyboardEvent) {
onMounted(() => {
onLoad();
canvasDisplay?.connectTarget(canvas.value!);
// @ts-ignore
window.addEventListener('reveal-screen', (event: CustomEvent) => {
selectScreen(event.detail);
});
@@ -219,7 +209,6 @@ async function onLoad(file?: VirtualFile | File) {
} else {
return;
}
console.log('onLoad', file, meta?.map?.width);
// @ts-ignore
const layer = new MapLayerDrawable(file, meta?.map?.width);
await layer.preloadFile();

View File

@@ -7,7 +7,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue';
import {MapContext, MapDrawable} from './helpers';
import {MapDrawable} from './helpers';
const map = ref<HTMLCanvasElement>();

View File

@@ -30,7 +30,7 @@
<button v-else @click.prevent="device.disconnect()">Disconnect</button>
</td>
<td>
<button @click.prevent="manager.removeDevice(device)">Delete</button>
<button @click.prevent="manager.removeDevice(device as any)">Delete</button>
</td>
</tr>
</tbody>

View File

@@ -49,22 +49,15 @@ const canvas = ref<HTMLCanvasElement>();
const isDragOver = ref(false);
const scale = ref(100);
const {isLoading, withLoading} = useLoading();
const {isLoading} = useLoading();
const {onMouseDown, x, y} = useMouseDragging()
const {onMouseDown} = useMouseDragging()
function onWheel(event: WheelEvent) {
scale.value = Math.max(10, Math.min(1000, scale.value + event.deltaY / 100));
console.log('scroll', event.deltaY, scale.value);
}
watch(() => [x.value, y.value, scale.value], () => {
imageSink.value?.transform?.({
x: x.value,
y: y.value,
scale: scale.value / 100,
});
})
watch(() => imageSink.value?.display, ()=> {
if (imageSink.value?.display) {
@@ -72,48 +65,14 @@ watch(() => imageSink.value?.display, ()=> {
}
});
async function drawImage(blob: Blob) {
if (blob.type.startsWith('image/')) {
const url = URL.createObjectURL(blob);
const img = new Image();
img.onload = () => {
const ctx = canvas.value!.getContext('2d')!;
canvas.value!.width = img.width;
canvas.value!.height = img.height;
ctx.drawImage(img, 0, 0);
}
img.src = url;
} else if (blob.type.startsWith('video/')) {
const url = URL.createObjectURL(blob);
const video = document.createElement('video');
video.muted = true;
video.autoplay = true;
video.src = url;
video.oncanplaythrough = () => {
const ctx = canvas.value!.getContext('2d')!;
canvas.value!.width = video.videoWidth;
canvas.value!.height = video.videoHeight;
ctx.drawImage(video, 0, 0);
video.pause();
}
}
}
async function loadFile(meta: VirtualFile, file: File) {
if (imageSink.value?.display) {
const map = new MapLayerDrawable(file, meta.map?.width);
// @ts-ignore
const map = new MapLayerDrawable(file, meta?.map?.width);
await map.preloadFile();
imageSink.value.display.mapLayer = map
console.log(imageSink.value?.display);
}
// await withLoading(async () => {
// await Promise.all([
// imageSink.value?.transferFile(meta, file),
// drawImage(file),
// ])
// await imageSink.value?.show(meta);
// })
}
async function onDrop(event: DragEvent) {

View File

@@ -1,6 +1,6 @@
<template>
<div class="screens">
<RemoteScreen :screen="screen" v-for="screen of screens" @click="revealScreen(screen)" />
<RemoteScreen :screen="(screen as any)" v-for="screen of screens" @click="revealScreen(screen as any)" />
<div>
<IconButton name="Chromecast" @click="remoteManager.connectChromeCast()">
<IconChromecast />
@@ -12,7 +12,6 @@
import {computed, inject} from 'vue';
import {RemoteDeviceManagerSymbol} from '../../model/injectionSymbols';
import RemoteScreen from './RemoteScreen.vue';
import {ImageSink} from '../../model/Remote/types';
import IconButton from '../Common/IconButton.vue';
import IconChromecast from '../Icons/IconChromecast.vue';
import {Devices} from '../../model/Remote/RemoteDeviceManager';

View File

@@ -9,7 +9,7 @@
{{ tab.label }}
</div>
</div>
<div v-for="slot, name in slots" v-show="selectedTab === name">
<div v-for="_, name in slots" v-show="selectedTab === name">
<slot :name="name"></slot>
</div>
</div>

View File

@@ -6,7 +6,7 @@ export const useMouseDragging = () => {
const y = ref(0);
const initialPosition = { x: 0, y: 0 };
function onMouseDown(event) {
function onMouseDown(event: any) {
dragging.value = true;
initialPosition.x = event.clientX - x.value;
initialPosition.y = event.clientY - y.value;
@@ -16,7 +16,7 @@ export const useMouseDragging = () => {
}, { once: true });
}
function onMouseMove(event) {
function onMouseMove(event: any) {
if (dragging.value) {
x.value = event.clientX - initialPosition.x;
y.value = event.clientY - initialPosition.y;

View File

@@ -6,7 +6,6 @@ import { AudioContextSymbol, MountedFsSymbol, RemoteDeviceManagerSymbol } from '
import { MountedFs } from './model/FileSystem/MountedFs'
import { RemoteDeviceManager } from './model/Remote/RemoteDeviceManager'
import { ChromecastChannelCaster } from './model/Remote/Channels/ChromcastCaster'
import { WsSignalingCaster } from './model/Remote/Channels/WsSignaling'
const app = createApp(ControlApp);

View File

@@ -1,7 +1,7 @@
import { PlaylistGroupNode, PlaylistGroupNodeOptions } from "./PlaylistGroupNode";
import { SfxGroupNode, SfxGroupNodeOptions } from "./SfxGroupNode";
import { PlaylistGroupNode } from "./PlaylistGroupNode";
import { SfxGroupNode } from "./SfxGroupNode";
import { SoundGroupNode } from "./SoundGroupNode";
import { TabSourceNode, TabSourceNodeOptions } from "./TabSourceNode";
import { TabSourceNode } from "./TabSourceNode";
import { SoundboardContext } from "./types";

View File

@@ -1,5 +1,4 @@
import { VirtualFile } from "../FileSystem/types";
import { RemoteDevice } from "../Remote/types";
import { SoundGroupNode, SoundGroupNodeOptions } from "./SoundGroupNode";
import { MediaElementSoundNode } from "./SoundNode";

View File

@@ -1,6 +1,5 @@
import { VirtualFile } from "../FileSystem/types";
import { SoundGroupNode, SoundGroupNodeOptions } from "./SoundGroupNode";
import { BufferedSoundNode } from "./SoundNode";
export interface SfxGroupNodeOptions extends SoundGroupNodeOptions {

View File

@@ -48,7 +48,7 @@ export abstract class SoundGroupNode<T extends SoundGroupNodeOptions> {
this.addSoundNode(sound);
}
}
// @ts-ignore
addSound(vFile: VirtualFile) {
// implement in subclass
}

View File

@@ -1,5 +1,5 @@
import { MountedFs } from "../FileSystem/MountedFs";
import { FileSystemSource, VirtualFile } from "../FileSystem/types";
import { VirtualFile } from "../FileSystem/types";
import { SoundboardContext } from "./types";
@@ -145,10 +145,6 @@ export class MediaElementSoundNode extends SoundNode {
return this._isPlaying;
}
get currentTime() {
return this.mediaElement!.currentTime;
}
get duration() {
return this.mediaElement!.duration;
}

View File

@@ -26,6 +26,7 @@ export class TabSourceNode extends SoundGroupNode<TabSourceNodeOptions> {
frameRate: {ideal: 1}
},
audio: {
// @ts-ignore
suppressLocalAudioPlayback: false,
echoCancellation: false,
noiseSuppression: false,

View File

@@ -1,5 +1,4 @@
import { MountedFs } from "../FileSystem/MountedFs";
import { FileSystemSource } from "../FileSystem/types";
export type SoundboardContext = {
audioContext: AudioContext;

View File

@@ -1,4 +1,3 @@
import { VirtualFile } from "../FileSystem/types";
export function extensionMimeType(fileName: string) {
if (fileName.endsWith('.jpg') || fileName.endsWith('.jpeg') || fileName.endsWith('.jpe')) {

View File

@@ -12,6 +12,7 @@ function extractSize(file: VirtualFile): MapMeta {
const match = [...file.path, file.name].join('/').match(/(\d+)x(\d+)/);
if (match) {
return {
valid: true,
width: parseInt(match[1]),
height: parseInt(match[2]),
}
@@ -22,6 +23,7 @@ function extractSize(file: VirtualFile): MapMeta {
const match = [...file.path, file.name].join('/').match(/(\d+)w/);
if (match) {
return {
valid: true,
width: parseInt(match[1]),
height: undefined,
}
@@ -29,6 +31,7 @@ function extractSize(file: VirtualFile): MapMeta {
}
return {
valid: false,
width: undefined,
height: undefined,
}

View File

@@ -1,4 +1,3 @@
import { CancellationToken } from "typescript";
import { extensionMimeType } from "../FileMeta/ExtensionMimeType";
import { CachePolicy, FsCache } from "./FsCache";
import { VirtualDirectory, VirtualFile, FileSystemSource, ListDirOptions } from "./types";
@@ -65,7 +64,7 @@ export class FileStashFs implements FileSystemSource<FileStashFsConfig> {
if (data.status !== 'ok') {
throw new Error(`Failed to list directory ${dir.path.join('/')}`);
}
return data.results.map((result) => {
const result = data.results.map((result) => {
if (result.type === 'file') {
return <VirtualFile>{
type: 'file',
@@ -75,7 +74,7 @@ export class FileStashFs implements FileSystemSource<FileStashFsConfig> {
path: [...dir.path, result.name],
}
} else if (result.type === 'directory') {
return {
return <VirtualDirectory>{
type: 'directory',
source: this.options.name,
name: result.name,
@@ -84,7 +83,9 @@ export class FileStashFs implements FileSystemSource<FileStashFsConfig> {
} else {
throw new Error(`Unknown type ${result.type}`);
}
}).sort((a, b) => {
})
result.sort((a, b) => {
if (a.type === 'file' && b.type === 'directory') {
return 1;
} else if (a.type === 'directory' && b.type === 'file') {
@@ -96,6 +97,8 @@ export class FileStashFs implements FileSystemSource<FileStashFsConfig> {
}
return 0;
});
return result;
}
async download(file: VirtualFile, {abortSignal}: {abortSignal?: AbortSignal} = {}): Promise<File> {
@@ -122,6 +125,8 @@ export class FileStashFs implements FileSystemSource<FileStashFsConfig> {
} catch (err) {
if (err !== 'User canceled') {
return this.download(file, {abortSignal});
} else {
throw err;
}
}
}

View File

@@ -5,7 +5,6 @@ import { CachePolicy, NoCacheEntryError } from "./FsCache";
type Config<T> = T extends FileSystemSource<any> ? ReturnType<T['getConfig']> : never;
const CACHE = Symbol('Cache');
export interface Fs {
get root(): VirtualDirectory[];
download(file: VirtualFile): Promise<File>;
@@ -104,35 +103,3 @@ export class MountedFs implements Fs {
}
}
// export class SearchableFilesystem implements Fs {
// public allFiles: VirtualFile[] = [];
// constructor(private fs: MountedFs) {}
// async precacheDir(dir: VirtualDirectory) {
// for (const child of await this.fs.listDir(dir, {cache: CachePolicy.useCache})) {
// if (child.type === 'file') {
// this.allFiles.push(child);
// } else {
// await this.precacheDir(child as VirtualDirectory);
// }
// }
// }
// async search(query: string) {
// await Promise.all(this.fs.root.map((root) => this.precacheDir(root)));
// }
// get root() {
// return this.fs.root;
// }
// async listDir(dir: VirtualDirectory) {
// return this.fs.listDir(dir);
// }
// async download(file: VirtualFile) {
// return this.fs.download(file);
// }
// }

View File

@@ -1,7 +1,7 @@
import { loadScript } from "../../../helpers/loadScript";
import { JsonChannelCaster, Message, MessageEmitter } from "./type";
import { JsonChannelSummon, Message, MessageEmitter } from "./type";
// @ts-ignore
import type {CastReceiverContext, CastReceiverOptions} from '@types/chromecast-caf-receiver/cast.framework';
import { WsSignalingSummon } from "./WsSignaling";
const CHANNEL = 'urn:x-cast:eu.fisoft.battlecaster.custom';

View File

@@ -13,6 +13,7 @@ export class WebRtcChannelCaster extends MessageEmitter implements JsonChannelCa
constructor(
private channel: JsonChannelCaster,
{onOpen}:Partial<{onOpen: Function}> = {},
// @ts-ignore
private ctx: RemoteDeviceManagerCtx
) {
super();
@@ -144,6 +145,7 @@ export class WebRtcChannelSummon extends MessageEmitter implements JsonChannelSu
};
this.peer.ondatachannel = (event) => {
if (event.channel.label === 'video') {
// @ts-ignore
this.videoChannel = event.channel;
} else if (event.channel.label === 'json') {
this.dataChannel = event.channel;

View File

@@ -1,11 +1,10 @@
import { CanvasDisplay } from "../../components/Map/object";
import { createPersistentKeyValStore, createPersistentValue, PersistentKeyVal } from "../../helpers/persistentKeyValue";
import { createPersistentValue, PersistentKeyVal } from "../../helpers/persistentKeyValue";
import { VirtualFile } from "../FileSystem/types";
import { PostMessageChannel } from "./Channels/PostMessage";
import { WebRtcChannelCaster } from "./Channels/WebRTCChannel";
import { WsSignalingCaster } from "./Channels/WsSignaling";
import { RemoteDeviceManagerCtx } from "./RemoteDeviceManager";
import { ImageSink, RemoteDevice } from "./types";
import { ImageSink } from "./types";
export class ChromecastDevice {
@@ -106,11 +105,9 @@ class ChromecastImageSink implements ImageSink {
console.log('show', file);
}
// @ts-ignore
async transform({x, y, scale}: {x: number, y: number, scale: number}) {
await this.device.webRtcChannel?.send({
type: 'transform',
x, y, scale
});
}
get diameter() {

View File

@@ -78,7 +78,7 @@ export class FullscreenWindowDevice implements RemoteDevice {
static async getDevices(): Promise<FullscreenWindowConfig[]> {
// @ts-ignore
const screenDetails = await window.getScreenDetails()
return screenDetails.screens.map((screen: any, index: number) => ({
return screenDetails.screens.map((_: any, index: number) => ({
name: `Screen ${index + 1}`,
type: "fullscreen-window",
screen: index
@@ -149,10 +149,8 @@ class FullscreenWindowImageSink implements ImageSink {
await this.device.webRtcChannel?.send({type: 'showFile', file});
}
// @ts-ignore
async transform({x, y, scale}: {x: number, y: number, scale: number}) {
await this.device.webRtcChannel?.send({
type: 'transform',
x, y, scale
});
}
}

View File

@@ -32,20 +32,14 @@
</AppLayout>
</template>
<script setup lang="ts">
import FileManager from '../../components/FileManager/FileManager.vue';
import type { FileWithMeta } from '../../model/FileManager';
import {ref, inject, watch} from 'vue';
import {SenderSymbol} from '../../model/injectionSymbols';
import Tabs from '../../components/Tabs.vue';
import {VirtualFile} from '../../model/FileSystem/types';
import ScreenManager from '../../components/ScreenManager/ScreenManager.vue';
import RemoteDevices from '../../components/RemoteDevices/RemoteDevices.vue';
import Soundboard from '../../components/Audio/Soundboard.vue';
import FilesAside from '../../components/Files/FilesAside.vue';
import MapControl from '../../components/Map/MapControl.vue';
import AppLayout from '../../components/Common/AppLayout.vue';
const openedFile = ref<FileWithMeta>()
const sender = inject(SenderSymbol);
const imageWidth = ref(20);
@@ -78,66 +72,6 @@ watch(() => [positionX.value, positionY.value, flipped.value], () => {
})
});
function openFile(file: VirtualFile) {
console.log('openFile', file);
// sender?.sendMessage({
// type: 'showFile',
// file,
// })
// imageWidth.value = file.width ?? 20;
// openedFile.value = file;
}
function openMapWindow() {
window.open('/map/', 'map');
}
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
function onStreamImage() {
if (!openedFile.value?.path) {
return;
}
const image = new Image()
image.onload = async () => {
const canvas = document.createElement('canvas');
canvas.setAttribute('willReadFrequently', 'true');
canvas.width = image.width;
canvas.height = image.height;
const [maxWidth, maxHeight] = (image.width > image.height) ? [3840, 2160] : [2160, 3840];
if (image.width > maxWidth || image.height > maxHeight) {
const ratio = Math.min(maxWidth / image.width, maxHeight / image.height);
canvas.width = image.width * ratio;
canvas.height = image.height * ratio;
}
const ctx = canvas.getContext('2d')!;
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
const chunkSize = {width: 128, height: 128};
for (let x = 0; x < canvas.width; x += chunkSize.width) {
for (let y = 0; y < canvas.height; y += chunkSize.height) {
const imageData = ctx.getImageData(x, y, chunkSize.width, chunkSize.height);
sender?.sendMessage({
type: 'streamImage',
imageData: {
data: [...new Uint32Array(imageData.data.buffer)],
width: imageData.width,
height: imageData.height,
},
canvas: {
width: canvas.width,
height: canvas.height,
},
x,
y,
}, {webRTC: true});
await delay(100);
}
}
};
image.src = openedFile.value?.path;
}
</script>
<style>
.aside {

View File

@@ -6,10 +6,8 @@
</div>
</template>
<script setup lang="ts">
import {inject, ref} from 'vue';
import Debug from './Debug.vue';
import {inject} from 'vue';
import {WebRtcChannelSymbol} from '../../model/injectionSymbols';
import {VirtualFile} from '../../model/FileSystem/types';
const webRtc = inject(WebRtcChannelSymbol)!;
@@ -27,6 +25,7 @@ function sendScreenInfo() {
function sendAudioInfo() {
const ac = new AudioContext();
webRtc.send({
// @ts-ignore
type: 'audioInfo',
channels: ac.destination.channelCount,
channelInterpretation: ac.destination.channelInterpretation,
@@ -45,28 +44,7 @@ webRtc.on((message) => {
} else if (message.type === 'connected') {
sendScreenInfo();
sendAudioInfo();
} else if (message.type === 'fileTransfered') {
/*debugger;
const key = fileToKey(message.meta);
cachedFiles.set(key, message.blob);*/
} else if (message.type === 'showFile') {
/*debugger;
const key = fileToKey(message.file);
const blob = cachedFiles.get(key);
if (blob) {
const url = URL.createObjectURL(blob);
const img = new Image();
img.onload = () => {
const ctx = canvas.value!.getContext('2d')!;
canvas.value!.width = img.width;
canvas.value!.height = img.height;
ctx.drawImage(img, 0, 0);
}
img.src = url;
}*/
} else if (message.type === 'transform') {
// canvas.value.style = `max-width: 100%;transform: scale(${message.scale}) translate(${message.x}%, ${message.y}%);transition: transform 100ms;`;
}
}
});
</script>

View File

@@ -3,9 +3,7 @@ import './style.css';
import { JsonChannelSymbol, WebRtcChannelSymbol } from './model/injectionSymbols';
import { PostMessageChannel } from './model/Remote/Channels/PostMessage';
import SummonApp from './modules/summon/SummonApp.vue';
import { immediate } from './helpers/immediate';
import { WebRtcChannelSummon } from './model/Remote/Channels/WebRTCChannel';
import { loadScript } from './helpers/loadScript';
import { ChromecastChannelSummon } from './model/Remote/Channels/ChromcastSummon';
import { WsSignalingSummon } from './model/Remote/Channels/WsSignaling';