Improve task manager etc
This commit is contained in:
		
							parent
							
								
									21b6e23e98
								
							
						
					
					
						commit
						7060625adf
					
				
					 7 changed files with 108 additions and 23 deletions
				
			
		|  | @ -169,15 +169,15 @@ export default defineComponent({ | |||
| 		font-size: 32px; | ||||
| 
 | ||||
| 		&.success { | ||||
| 			color: var(--accent); | ||||
| 			color: var(--success); | ||||
| 		} | ||||
| 
 | ||||
| 		&.error { | ||||
| 			color: #ec4137; | ||||
| 			color: var(--error); | ||||
| 		} | ||||
| 
 | ||||
| 		&.warning { | ||||
| 			color: #ecb637; | ||||
| 			color: var(--warn); | ||||
| 		} | ||||
| 
 | ||||
| 		> * { | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| 		<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/> | ||||
| 
 | ||||
| 		<div class="content"> | ||||
| 			<div v-if="tab === 'windows'" class="windows"> | ||||
| 			<div v-if="tab === 'windows'" class="windows" v-follow> | ||||
| 				<div class="header"> | ||||
| 					<div>#ID</div> | ||||
| 					<div>Component</div> | ||||
|  | @ -19,7 +19,7 @@ | |||
| 					<div><button class="_textButton" @click="killPopup(p)">Kill</button></div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div v-if="tab === 'stream'" class="stream"> | ||||
| 			<div v-if="tab === 'stream'" class="stream" v-follow> | ||||
| 				<div class="header"> | ||||
| 					<div>#ID</div> | ||||
| 					<div>Ch</div> | ||||
|  | @ -36,7 +36,7 @@ | |||
| 					<div>{{ c.out }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div v-if="tab === 'streamPool'" class="streamPool"> | ||||
| 			<div v-if="tab === 'streamPool'" class="streamPool" v-follow> | ||||
| 				<div class="header"> | ||||
| 					<div>#ID</div> | ||||
| 					<div>Ch</div> | ||||
|  | @ -48,6 +48,18 @@ | |||
| 					<div>{{ p.users }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div v-if="tab === 'api'" class="api" v-follow> | ||||
| 				<div class="header"> | ||||
| 					<div>#ID</div> | ||||
| 					<div>Endpoint</div> | ||||
| 					<div>State</div> | ||||
| 				</div> | ||||
| 				<div v-for="req in apiRequests"> | ||||
| 					<div>#{{ req.id }}</div> | ||||
| 					<div>{{ req.endpoint }}</div> | ||||
| 					<div class="state" :class="req.state">{{ req.state }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<footer> | ||||
|  | @ -65,6 +77,7 @@ import { faTerminal } from '@fortawesome/free-solid-svg-icons'; | |||
| import XWindow from '@/components/ui/window.vue'; | ||||
| import MkTab from '@/components/tab.vue'; | ||||
| import MkButton from '@/components/ui/button.vue'; | ||||
| import follow from '@/directives/follow-append'; | ||||
| import * as os from '@/os'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|  | @ -74,6 +87,10 @@ export default defineComponent({ | |||
| 		MkButton, | ||||
| 	}, | ||||
| 
 | ||||
| 	directives: { | ||||
| 		follow | ||||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -105,6 +122,7 @@ export default defineComponent({ | |||
| 		return { | ||||
| 			tab: ref('stream'), | ||||
| 			popups: os.popups, | ||||
| 			apiRequests: os.apiRequests, | ||||
| 			connections, | ||||
| 			pools, | ||||
| 			killPopup, | ||||
|  | @ -125,9 +143,7 @@ export default defineComponent({ | |||
| 		flex: 1; | ||||
| 		overflow: auto; | ||||
| 
 | ||||
| 		> .windows, | ||||
| 		> .stream, | ||||
| 		> .streamPool { | ||||
| 		> div { | ||||
| 			display: table; | ||||
| 			width: 100%; | ||||
| 			padding: 16px; | ||||
|  | @ -140,8 +156,31 @@ export default defineComponent({ | |||
| 					opacity: 0.7; | ||||
| 				} | ||||
| 
 | ||||
| 				> * { | ||||
| 				> div { | ||||
| 					display: table-cell; | ||||
| 					white-space: nowrap; | ||||
| 
 | ||||
| 					&:not(:last-child) { | ||||
| 						padding-right: 8px; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			&.api { | ||||
| 				> div { | ||||
| 					> .state { | ||||
| 						&.pending { | ||||
| 							color: var(--warn); | ||||
| 						} | ||||
| 
 | ||||
| 						&.success { | ||||
| 							color: var(--success); | ||||
| 						} | ||||
| 
 | ||||
| 						&.failed { | ||||
| 							color: var(--error); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
							
								
								
									
										25
									
								
								src/client/directives/follow-append.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/client/directives/follow-append.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| import { Directive } from 'vue'; | ||||
| import { getScrollContainer, getScrollPosition } from '@/scripts/scroll'; | ||||
| 
 | ||||
| export default { | ||||
| 	mounted(src, binding, vn) { | ||||
| 		const ro = new ResizeObserver((entries, observer) => { | ||||
| 			const pos = getScrollPosition(src); | ||||
| 			const container = getScrollContainer(src); | ||||
| 			const viewHeight = container.clientHeight; | ||||
| 			const height = container.scrollHeight; | ||||
| 			if (pos + viewHeight > height - 32) { | ||||
| 				container.scrollTop = height; | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		ro.observe(src); | ||||
| 
 | ||||
| 		// TODO: 新たにプロパティを作るのをやめMapを使う
 | ||||
| 		src._ro_ = ro; | ||||
| 	}, | ||||
| 
 | ||||
| 	unmounted(src, binding, vn) { | ||||
| 		src._ro_.unobserve(src); | ||||
| 	} | ||||
| } as Directive; | ||||
|  | @ -2,7 +2,7 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vu | |||
| import { EventEmitter } from 'eventemitter3'; | ||||
| import Stream from '@/scripts/stream'; | ||||
| import { store } from '@/store'; | ||||
| import { apiUrl } from '@/config'; | ||||
| import { apiUrl, debug } from '@/config'; | ||||
| import MkPostFormDialog from '@/components/post-form-dialog.vue'; | ||||
| import MkWaitingDialog from '@/components/waiting-dialog.vue'; | ||||
| import { resolve } from '@/router'; | ||||
|  | @ -13,28 +13,26 @@ export const isMobile = /mobile|iphone|ipad|android/.test(ua); | |||
| export const stream = markRaw(new Stream()); | ||||
| 
 | ||||
| export const pendingApiRequestsCount = ref(0); | ||||
| export const apiRequests = ref([]); // for debug
 | ||||
| 
 | ||||
| export const windows = new Map(); | ||||
| 
 | ||||
| export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) { | ||||
| 	pendingApiRequestsCount.value++; | ||||
| 
 | ||||
| 	if (_DEV_) { | ||||
| 		performance.mark(_PERF_PREFIX_ + 'api:begin'); | ||||
| 	} | ||||
| 
 | ||||
| 	const onFinally = () => { | ||||
| 		pendingApiRequestsCount.value--; | ||||
| 
 | ||||
| 		if (_DEV_) { | ||||
| 			performance.mark(_PERF_PREFIX_ + 'api:end'); | ||||
| 
 | ||||
| 			performance.measure(_PERF_PREFIX_ + 'api', | ||||
| 				_PERF_PREFIX_ + 'api:begin', | ||||
| 				_PERF_PREFIX_ + 'api:end'); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const log = debug ? reactive({ | ||||
| 		id: apiRequests.value.length, | ||||
| 		endpoint, | ||||
| 		state: 'pending' | ||||
| 	}) : null; | ||||
| 	if (debug) { | ||||
| 		apiRequests.value.push(log); | ||||
| 	} | ||||
| 
 | ||||
| 	const promise = new Promise((resolve, reject) => { | ||||
| 		// Append a credential
 | ||||
| 		if (store.getters.isSignedIn) (data as any).i = store.state.i.token; | ||||
|  | @ -51,10 +49,19 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st | |||
| 
 | ||||
| 			if (res.status === 200) { | ||||
| 				resolve(body); | ||||
| 				if (debug) { | ||||
| 					log.state = 'success'; | ||||
| 				} | ||||
| 			} else if (res.status === 204) { | ||||
| 				resolve(); | ||||
| 				if (debug) { | ||||
| 					log.state = 'success'; | ||||
| 				} | ||||
| 			} else { | ||||
| 				reject(body.error); | ||||
| 				if (debug) { | ||||
| 					log.state = 'failed'; | ||||
| 				} | ||||
| 			} | ||||
| 		}).catch(reject); | ||||
| 	}); | ||||
|  |  | |||
|  | @ -10,6 +10,10 @@ | |||
| 				<MkInput v-model:value="dialogBody"> | ||||
| 					<span>Body</span> | ||||
| 				</MkInput> | ||||
| 				<MkRadio v-model="dialogType" value="info">Info</MkRadio> | ||||
| 				<MkRadio v-model="dialogType" value="success">Success</MkRadio> | ||||
| 				<MkRadio v-model="dialogType" value="warning">Warn</MkRadio> | ||||
| 				<MkRadio v-model="dialogType" value="error">Error</MkRadio> | ||||
| 				<MkSwitch v-model:value="dialogCancel"> | ||||
| 					<span>With cancel button</span> | ||||
| 				</MkSwitch> | ||||
|  | @ -133,6 +137,7 @@ import MkButton from '@/components/ui/button.vue'; | |||
| import MkInput from '@/components/ui/input.vue'; | ||||
| import MkSwitch from '@/components/ui/switch.vue'; | ||||
| import MkTextarea from '@/components/ui/textarea.vue'; | ||||
| import MkRadio from '@/components/ui/radio.vue'; | ||||
| import * as os from '@/os'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|  | @ -141,6 +146,7 @@ export default defineComponent({ | |||
| 		MkInput, | ||||
| 		MkSwitch, | ||||
| 		MkTextarea, | ||||
| 		MkRadio, | ||||
| 	}, | ||||
| 
 | ||||
| 	data() { | ||||
|  | @ -153,6 +159,7 @@ export default defineComponent({ | |||
| 			}, | ||||
| 			dialogTitle: 'Hello', | ||||
| 			dialogBody: 'World!', | ||||
| 			dialogType: 'info', | ||||
| 			dialogCancel: false, | ||||
| 			dialogCancelByBgClick: true, | ||||
| 			dialogInput: false, | ||||
|  | @ -192,6 +199,7 @@ export default defineComponent({ | |||
| 		async showDialog() { | ||||
| 			this.dialogResult = null; | ||||
| 			this.dialogResult = await os.dialog({ | ||||
| 				type: this.dialogType, | ||||
| 				title: this.dialogTitle, | ||||
| 				text: this.dialogBody, | ||||
| 				showCancelButton: this.dialogCancel, | ||||
|  |  | |||
|  | @ -56,6 +56,9 @@ | |||
| 		wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', | ||||
| 		badge: '#31b1ce', | ||||
| 		messageBg: ':lighten<5<@bg', | ||||
| 		success: '#86b300', | ||||
| 		error: '#ec4137', | ||||
| 		warn: '#ecb637', | ||||
| 		htmlThemeColor: '@bg', | ||||
| 		X1: ':alpha<0<@bg', | ||||
| 		X2: ':darken<2<@panel', | ||||
|  |  | |||
|  | @ -56,6 +56,9 @@ | |||
| 		wallpaperOverlay: 'rgba(255, 255, 255, 0.5)', | ||||
| 		badge: '#31b1ce', | ||||
| 		messageBg: '@panel', | ||||
| 		success: '#86b300', | ||||
| 		error: '#ec4137', | ||||
| 		warn: '#ecb637', | ||||
| 		htmlThemeColor: '@bg', | ||||
| 		X1: ':alpha<0<@bg', | ||||
| 		X2: ':darken<2<@panel', | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue