Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "yehyecoa-vue",
"type": "module",
"version": "0.1.0-beta",
"version": "0.2.0-beta",
"private": true,
"packageManager": "[email protected]",
"engines": {
Expand Down
10 changes: 10 additions & 0 deletions packages/vue-repl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
"import": "./dist/codemirror-editor.js",
"require": null
},
"./luna-console": {
"types": "./dist/luna-console.d.ts",
"import": "./dist/luna-console.js",
"require": null
},
"./core": {
"types": "./dist/core.d.ts",
"import": "./dist/core.js",
Expand Down Expand Up @@ -103,7 +108,12 @@
"eslint-plugin-vue": "^10.4.0",
"fflate": "^0.8.2",
"hash-sum": "^2.0.0",
"licia": "^1.48.0",
"lint-staged": "^16.1.4",
"luna-console": "^1.3.5",
"luna-data-grid": "^1.3.0",
"luna-dom-viewer": "^1.8.3",
"luna-object-viewer": "^0.3.1",
"monaco-editor-core": "0.52.2",
"prettier": "^3.6.2",
"shiki": "^3.9.2",
Expand Down
189 changes: 189 additions & 0 deletions packages/vue-repl/src/OutputSplitPane.vue
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brand new component that does not care about responsiveness. This only determines whether the container is in vertical or horizontal mode and provides resize draggers for either case.

Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<script setup lang="ts">
import { computed, inject, reactive, useTemplateRef } from 'vue'
import { injectKeyPreviewRef, injectKeyProps } from './types'

const props = defineProps<{ layout?: 'horizontal' | 'vertical' }>()
const isVertical = computed(() => props.layout === 'vertical')

const containerRef = useTemplateRef('container')
const previewRef = inject(injectKeyPreviewRef)!

const { store, layoutReverse, splitPaneOptions } = inject(injectKeyProps)!

const state = reactive({
dragging: false,
split: 50,
viewHeight: 0,
viewWidth: 0,
})

const boundSplit = computed(() => {
const { split } = state
return split < 20 ? 20 : split > 80 ? 80 : split
})

let startPosition = 0
let startSplit = 0

function dragStart(e: MouseEvent) {
state.dragging = true
startPosition = isVertical.value ? e.pageY : e.pageX
startSplit = boundSplit.value

changeViewSize()
}

function dragMove(e: MouseEvent) {
if (containerRef.value && state.dragging) {
const position = isVertical.value ? e.pageY : e.pageX
const totalSize = isVertical.value
? containerRef.value.offsetHeight
: containerRef.value.offsetWidth
const dp = position - startPosition
state.split = startSplit + +((dp / totalSize) * 100).toFixed(2)

changeViewSize()
}
}

function dragEnd() {
state.dragging = false
}

function changeViewSize() {
const el = previewRef.value
if (!el) return
state.viewHeight = el.offsetHeight
state.viewWidth = el.offsetWidth
}
</script>

<template>
<div
ref="container" class="output-split-pane" :class="{
'osp-dragging': state.dragging,
reverse: layoutReverse,
vertical: isVertical,
}" @mousemove="dragMove" @mouseup="dragEnd" @mouseleave="dragEnd"
>
<div class="first" :style="{ [isVertical ? 'height' : 'width']: boundSplit + '%' }">
<slot name="first" />
<div class="dragger" @mousedown.prevent="dragStart" />
</div>
<div class="second" :style="{ [isVertical ? 'height' : 'width']: 100 - boundSplit + '%' }">
<div v-show="state.dragging" class="view-size">
{{ `${state.viewWidth}px x ${state.viewHeight}px` }}
</div>
<slot name="second" />
</div>

<button class="toggler" @click="store.showOutput = !store.showOutput">
{{
store.showOutput
? splitPaneOptions?.codeTogglerText || '< Code' : splitPaneOptions?.outputTogglerText || 'Output >' }}
</button>
</div>
</template>

<style scoped>
.output-split-pane {
display: flex;
flex-direction: row;
height: 100%;
position: relative;
}

.output-split-pane.osp-dragging {
cursor: ew-resize;
}

.osp-dragging .first,
.osp-dragging .second {
pointer-events: none;
}

.first,
.second {
position: relative;
height: 100%;
}

.view-size {
position: absolute;
top: 40px;
left: 10px;
font-size: 12px;
color: var(--text-light);
z-index: 100;
background-color: var(--bg-soft);
padding: 0.5rem;
border-radius: 4px;
}

.first {
border-right: 1px solid var(--border);
}

.dragger {
position: absolute;
z-index: 3;
}

.vertical .dragger {
top: auto;
height: 10px;
width: 100%;
left: 0;
right: 0;
bottom: -5px;
cursor: ns-resize;
}

.toggler {
display: none;
z-index: 3;
font-family: var(--font-code);
color: var(--text-light);
position: absolute;
left: 50%;
bottom: 20px;
background-color: var(--bg);
padding: 8px 12px;
border-radius: 8px;
transform: translateX(-50%);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.25);
}

.dark .toggler {
background-color: var(--bg);
}


.output-split-pane:not(.vertical) .dragger {
top: 0;
bottom: 0;
left: auto;
right: -5px;
width: 10px;
height: 100%;
cursor: ew-resize;
}

.output-split-pane.vertical {
flex-direction: column;
}

.output-split-pane.vertical.osp-dragging {
cursor: ns-resize;
}

.vertical .first,
.vertical .second {
width: 100%;
}

.vertical .first {
border-right: none;
border-bottom: 1px solid var(--border);
}
</style>
26 changes: 25 additions & 1 deletion packages/vue-repl/src/Repl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@ import Output from './output/Output.vue'
import { type Store, useStore } from './store'
import { computed, provide, toRefs, useTemplateRef } from 'vue'
import {
type ConsoleComponentType,


type EditorComponentType,
injectKeyPreviewRef,
injectKeyProps,
} from './types'
import EditorContainer from './editor/EditorContainer.vue'
import type * as monaco from 'monaco-editor-core'
import LunaConsole from './output/luna-console.vue'

export interface Props {
theme?: 'dark' | 'light'
previewTheme?: boolean
editor: EditorComponentType
console?: ConsoleComponentType
store?: Store
autoResize?: boolean
showCompileOutput?: boolean
showOpenSourceMap?: boolean
showImportMap?: boolean
showSsrOutput?: boolean
showTsConfig?: boolean
showConsole?: boolean
clearConsole?: boolean
showOutput?: boolean
layout?: 'horizontal' | 'vertical'
outputLayout?: 'horizontal' | 'vertical'
layoutReverse?: boolean
ssr?: boolean
previewOptions?: {
Expand Down Expand Up @@ -53,16 +61,20 @@ const props = withDefaults(defineProps<Props>(), {
theme: 'light',
previewTheme: false,
store: () => useStore(),
console: LunaConsole,
autoResize: true,
showCompileOutput: true,
showOpenSourceMap: false,
showImportMap: true,
showSsrOutput: false,
showTsConfig: true,
showOutput: true,
showConsole: false,
clearConsole: true,
layoutReverse: false,
ssr: false,
layout: 'horizontal',
outputLayout: 'vertical',
previewOptions: () => ({}),
editorOptions: () => ({}),
splitPaneOptions: () => ({}),
Expand All @@ -72,6 +84,10 @@ if (!props.editor) {
throw new Error('The "editor" prop is now required.')
}

const consoleWrapper = computed<ConsoleComponentType>(
() => props.console ?? (() => ({})),
)

const outputRef = useTemplateRef('output')

props.store.init()
Expand All @@ -81,6 +97,9 @@ const outputSlotName = computed(() => (props.layoutReverse ? 'left' : 'right'))

provide(injectKeyProps, {
...toRefs(props),
console: consoleWrapper,


autoSave,
})
provide(
Expand All @@ -100,21 +119,26 @@ defineExpose({ reload })

<template>
<div class="vue-repl">
<SplitPane :layout="layout">
<SplitPane v-if="showOutput" :layout="layout">
<template #[editorSlotName]>
<EditorContainer :editor-component="editor" />
</template>
<template #[outputSlotName]>
<Output
ref="output"
:editor-component="editor"
:console-component="consoleWrapper"
:show-output="props.showOutput"
:layout="outputLayout"
:show-compile-output="props.showCompileOutput"
:show-open-source-map="props.showOpenSourceMap"
:show-ssr-output="props.showSsrOutput"
:ssr="!!props.ssr"
/>
</template>
</SplitPane>

<EditorContainer v-else :editor-component="editor" />
</div>
</template>

Expand Down
3 changes: 3 additions & 0 deletions packages/vue-repl/src/SplitPane.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ function changeViewSize() {
font-size: 12px;
color: var(--text-light);
z-index: 100;
background-color: var(--bg-soft);
padding: 0.5rem;
border-radius: 4px;
}
.left {
border-right: 1px solid var(--border);
Expand Down
5 changes: 4 additions & 1 deletion packages/vue-repl/src/editor/EditorContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ watch(showMessage, () => {
/>
<Message v-show="showMessage" :err="store.errors[0]" />

<div class="editor-floating">
<div
v-show="editorOptions?.showErrorText !== false || editorOptions?.autoSaveText !== false"
class="editor-floating"
Comment on lines +47 to +48
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little square would just hang in there because there wasn't any switches present, so removing if no options are provided during repl instantiation

>
<ToggleButton
v-if="editorOptions?.showErrorText !== false"
v-model="showMessage"
Expand Down
Loading