137 lines
3.3 KiB
Vue
137 lines
3.3 KiB
Vue
|
<template>
|
||
|
<div
|
||
|
class="window-wrapper"
|
||
|
:style="{ top: windowData.y + 'px', left: windowData.x + 'px', width: windowData.width + 'px', height: windowData.height + 'px', zIndex: windowData.zIndex }"
|
||
|
@mousedown="bringToFront"
|
||
|
>
|
||
|
<div class="title-bar" @mousedown.prevent="startDrag">
|
||
|
<span class="title">{{ t(windowData.title) }}</span>
|
||
|
<div class="window-controls">
|
||
|
<button @click.stop="minimize">_</button>
|
||
|
<button @click.stop="close">X</button>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="content">
|
||
|
<div class="terminal-section">
|
||
|
<span class="prompt">guest@daniels-mac:~$</span>
|
||
|
<input
|
||
|
v-model="command"
|
||
|
type="text"
|
||
|
class="terminal-input"
|
||
|
@keydown.enter="runCommand"
|
||
|
placeholder="Terminal is for display only"
|
||
|
autofocus
|
||
|
/>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script setup>
|
||
|
import { ref } from 'vue';
|
||
|
import { useI18n } from 'vue-i18n';
|
||
|
|
||
|
const { t } = useI18n();
|
||
|
const props = defineProps({ windowData: Object });
|
||
|
const emit = defineEmits(['close', 'minimize', 'bring-to-front', 'update-position']);
|
||
|
|
||
|
const command = ref('');
|
||
|
|
||
|
const runCommand = () => {
|
||
|
console.log(`Command executed: ${command.value}`);
|
||
|
command.value = '';
|
||
|
};
|
||
|
|
||
|
const close = () => emit('close');
|
||
|
const minimize = () => emit('minimize');
|
||
|
const bringToFront = () => emit('bring-to-front');
|
||
|
|
||
|
const startDrag = (event) => {
|
||
|
const startX = event.clientX;
|
||
|
const startY = event.clientY;
|
||
|
const initialX = props.windowData.x;
|
||
|
const initialY = props.windowData.y;
|
||
|
|
||
|
const onMouseMove = (moveEvent) => {
|
||
|
const newX = initialX + (moveEvent.clientX - startX);
|
||
|
const newY = initialY + (moveEvent.clientY - startY);
|
||
|
emit('update-position', { x: newX, y: newY });
|
||
|
};
|
||
|
|
||
|
const onMouseUp = () => {
|
||
|
document.removeEventListener('mousemove', onMouseMove);
|
||
|
document.removeEventListener('mouseup', onMouseUp);
|
||
|
};
|
||
|
|
||
|
document.addEventListener('mousemove', onMouseMove);
|
||
|
document.addEventListener('mouseup', onMouseUp);
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
<style scoped>
|
||
|
.window-wrapper {
|
||
|
position: absolute;
|
||
|
background-color: #c0c0c0; /* Windows 95 gray */
|
||
|
border: 2px solid;
|
||
|
border-color: #ffffff #808080 #808080 #ffffff;
|
||
|
box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
}
|
||
|
|
||
|
.title-bar {
|
||
|
background-color: #000080; /* Windows 95 blue */
|
||
|
color: white;
|
||
|
padding: 4px;
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
align-items: center;
|
||
|
cursor: move;
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
|
||
|
.window-controls button {
|
||
|
background-color: #c0c0c0;
|
||
|
border: 1px solid;
|
||
|
border-color: #ffffff #808080 #808080 #ffffff;
|
||
|
font-weight: bold;
|
||
|
margin-left: 2px;
|
||
|
min-width: 20px;
|
||
|
}
|
||
|
|
||
|
.content {
|
||
|
padding: 10px;
|
||
|
flex-grow: 1;
|
||
|
background-color: black;
|
||
|
color: #34d399; /* green-400 */
|
||
|
font-family: 'Courier New', Courier, monospace;
|
||
|
}
|
||
|
|
||
|
.terminal-section {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
.prompt {
|
||
|
color: #fbbF24; /* yellow-400 */
|
||
|
margin-right: 8px;
|
||
|
white-space: nowrap;
|
||
|
}
|
||
|
|
||
|
.terminal-input {
|
||
|
background: transparent;
|
||
|
border: none;
|
||
|
color: #34d399; /* green-400 */
|
||
|
width: 100%;
|
||
|
font-family: 'Courier New', Courier, monospace;
|
||
|
}
|
||
|
|
||
|
.terminal-input:focus {
|
||
|
outline: none;
|
||
|
}
|
||
|
|
||
|
.terminal-input::placeholder {
|
||
|
color: #6b7280; /* gray-500 */
|
||
|
}
|
||
|
</style>
|