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
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export default defineConfig({
collapsed: true,
items: [
{ text: 'Timers', link: '/advanced/timers' },
{ text: 'Interactivity', link: '/advanced/interactivity'}
{ text: 'Interactivity', link: '/advanced/interactivity'},
{ text: 'Pan and Zoom', link: '/advanced/pan-and-zoom' }
]
},
{
Expand Down
19 changes: 19 additions & 0 deletions docs/advanced/pan-and-zoom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup>
import Block from '../components/Block.vue'
</script>
# Pan and Zoom
You can pan and zoom Limn renderer allowing to use it as infinite canvas.

Note: Limn renders all elements, even if they are off-screen. We plan to include ability for partial rendering in the future using QuadTrees but for now it's not available.

The renderer starts with zoom=1 and origin at (0, 0) (top, left). You can modify these values like all other parameters using signals.

## Zoom
You can set zoom value

<Block name="zoomBasic" />

## Panning
You can pan your canvas by updating `.topLeft` property

<Block name="panBasic" />
33 changes: 33 additions & 0 deletions docs/demos/circles-colors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
/**
Limn Explorer

Here you can experiment with Limn library.
CTRL+Enter - execute code
CTRL+p - pause/resume timer

On mac it's CMD instead of CTRL

CTRL+` - toggle code editor

Variables:
r - LimnRenderer setup for the background canvas


by hypersphere.
Check out tutorials on https://hypersphere.blog
**/

import { Circle, GenerativeCollection, RArc } from 'limn'


const t = r.timer.infiniteForward(40000, i => i)

const circles = new GenerativeCollection(
50,
i => new Circle({ center: r.center, radius: i * 10 }))
.map((c, i) => c.segment(0.5).rotate(i * t.value))
.map((c, i) => new RArc(c, { stroke: `hsl(${i*10+200*t.value}, 50%, ${10+i*2}%)`, width: 10 }))

r.add(circles)
*/
35 changes: 35 additions & 0 deletions docs/exampleCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,41 @@ export const getStarted = (r: LimnRenderer) => {
})
}

export const zoomBasic = (r: LimnRenderer) => {
const rect = new Rectangle({ p1: r.center, p2: r.center.add(100, 100) })
r.add(rect, {
fill: 'red',
stroke: 'orange',
width: 5
})

r.zoom = r.timer.infinite(1000, i => 0.5 + i)
}

export const panBasic = (r: LimnRenderer) => {
const rect = new Rectangle({ p1: r.center, p2: r.center.add(100, 100) })
r.add(rect, {
fill: 'red',
stroke: 'orange',
width: 5
})

const circle = new Circle({
center: r.center,
radius: 100
})

r.add(circle, {
fill: 'rgba(0, 0, 200, 0.5)'
})

const x = r.timer.infinite(1000, i => 100 * i)

const p = new Point(x, 0)
r.topLeft = p

}

export const pointAbsolute = (r: LimnRenderer) => {
const p = new Point(50, 50)
r.add(p, {
Expand Down
8 changes: 7 additions & 1 deletion packages/limn-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,27 @@
"@tailwindcss/vite": "^4.1.11",
"@vee-validate/zod": "^4.15.1",
"@vueuse/core": "^13.4.0",
"@y/websocket-server": "^0.1.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"limn": "workspace:*",
"lucide-vue-next": "^0.525.0",
"monaco-editor": "^0.52.2",
"monaco-editor": "^0.44.0",
"reka-ui": "^2.3.1",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"tw-animate-css": "^1.3.4",
"vee-validate": "^4.15.1",
"vue": "^3.5.17",
"ws": "^8.18.3",
"y-monaco": "^0.1.6",
"y-websocket": "^3.0.0",
"yjs": "^13.6.27",
"zod": "^3.25.67"
},
"devDependencies": {
"@types/node": "^24.0.7",
"@types/ws": "^8.18.1",
"@vitejs/plugin-vue": "^6.0.0",
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.8.3",
Expand Down
8 changes: 7 additions & 1 deletion packages/limn-explorer/src/components/AppSidebar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { BookOpenText, Cog, Play, Waves } from 'lucide-vue-next'
import { BookOpenText, Cog, Play, Waves, MonitorUp } from 'lucide-vue-next'
import Logo from './sidebar/limn-icon-explorer.svg?url'
import { h, ref } from 'vue'
import {
Expand All @@ -21,6 +21,7 @@ import Demos from './sidebarContent/demos.vue'
import Config from './sidebarContent/config.vue'
import Filesystem from './sidebarContent/filesystem.vue'
import { cn } from '../lib/utils'
import Collaborate from './sidebarContent/collaborate.vue'

const props = withDefaults(defineProps<SidebarProps>(), {
collapsible: 'icon',
Expand All @@ -41,6 +42,10 @@ const data = {
title: 'Examples',
icon: BookOpenText
},
{
title: 'Collaborate',
icon: MonitorUp
},
{
title: 'Config',
url: '#',
Expand Down Expand Up @@ -132,6 +137,7 @@ setOpen(false)
<Demos v-if="activeItem.title === 'Examples'" />
<Config v-if="activeItem.title === 'Config'" />
<Filesystem v-if="activeItem.title === 'Playground'" />
<Collaborate v-if="activeItem.title === 'Collaborate'" />
</SidebarContent>
</Sidebar>
</Sidebar>
Expand Down
77 changes: 77 additions & 0 deletions packages/limn-explorer/src/components/dialog/connectToSession.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod'

import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'

const formSchema = toTypedSchema(z.object({
sessionId: z.string().min(2).max(50),
}))

function onSubmit(values: any) {
emit('submit', values)
}

const emit = defineEmits(['submit'])

</script>

<template>
<Form v-slot="{ handleSubmit }" as="" keep-values :validation-schema="formSchema">
<Dialog>
<DialogTrigger as-child>
<Button variant="outline">
Connect to Session
</Button>
</DialogTrigger>
<DialogContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Connect to the session</DialogTitle>
<DialogDescription>
Connect to collaborative session.
</DialogDescription>
</DialogHeader>

<form id="dialogForm" @submit="handleSubmit($event, onSubmit)">
<FormField v-slot="{ componentField }" name="sessionId">
<FormItem>
<FormLabel>Session Id</FormLabel>
<FormControl>
<Input type="text" placeholder="XXX-XXX-XXX" v-bind="componentField" />
</FormControl>
<FormDescription>
This is session ID you should recieve from your collaborator
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</form>

<DialogFooter>
<Button type="submit" form="dialogForm">
Connect
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</Form>
</template>
5 changes: 5 additions & 0 deletions packages/limn-explorer/src/components/editor/editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { start } from '../../demos';
import { setupEditor } from './editor';
import { config } from '../../state/config';
import { debounce } from '../../lib/utils';
import { monacoEditor } from './editorRef';

const editorRef = ref()
let editor: monaco.editor.IStandaloneCodeEditor
let internalCompileTs = (a:string) => Promise.resolve(a)
onMounted(async () => {
const { editor: internalEditor, compileTypeScript } = await setupEditor(editorRef.value, start)
editor = internalEditor
monacoEditor.value = editor

window.LIMN_EDITOR = editor

internalCompileTs = compileTypeScript

editor.onDidChangeModelContent(debounce(contentChange => {
Expand Down
3 changes: 3 additions & 0 deletions packages/limn-explorer/src/components/editor/editorRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ref } from "vue";

export const monacoEditor = ref()
Loading
Loading