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
17 changes: 17 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,20 @@ jobs:

- name: Run Tests
run: npx vitest run

build-cloudflare:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc

- name: Install Dependencies
run: npm ci

- name: Build for Cloudflare Pages
run: npm run build
env:
NITRO_PRESET: cloudflare-pages
NODE_OPTIONS: --max-old-space-size=4096
57 changes: 16 additions & 41 deletions app/components/ButtonTheme.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@
</template>

<script setup>
import useShiki from '@/composables/useShiki';
import { debounce, defaults, cloneDeep } from 'lodash';
import { ref, watch, reactive, toRefs, computed, onMounted } from 'vue';
import useHighlightedCode from '@/composables/useHighlightedCode';
import { defaults, cloneDeep } from 'lodash';
import { ref, watch, reactive, toRefs, computed } from 'vue';

defineOptions({ inheritAttrs: false });

Expand All @@ -77,15 +77,20 @@ const props = defineProps({

const { code, theme, settings, languages } = toRefs(props);

const { $shiki } = useNuxtApp();
const { buildCodeBlocks } = useShiki();

const blocks = ref(null);
const visible = ref(false);
const rendered = ref(false);
const rendering = ref(true);
const themeSettings = reactive({});
const previouslyRendered = ref(null);
const { result: highlighted, loading: rendering } = useHighlightedCode(
() => ({ code: code.value, theme: theme.value, languages: languages.value }),
{ enabled: visible, delay: 750, limit: 5 }
);
const blocks = computed(() => highlighted.value?.blocks ?? null);
const rendered = computed(() => highlighted.value !== null);

watch(highlighted, (value) => {
themeSettings.themeType = value.themeType;
themeSettings.themeBackground = value.themeBackground;
});

const previewWidth = 640;
const previewHeight = 480;
const previewScale = 0.4;
Expand All @@ -101,42 +106,12 @@ const settingOverrides = {

const hasScene = computed(() => themeSettings.scene && themeSettings.scene !== 'none');

function generateTokens() {
rendering.value = true;

return buildCodeBlocks(
{ code: code.value, theme: theme.value, languages: languages.value },
({ blocks: code, themeType: type, themeBackground: background }) => {
themeSettings.themeType = type;
themeSettings.themeBackground = background;
blocks.value = code;
},
5
).then(() => {
rendering.value = false;
previouslyRendered.value = code.value;
});
}

watch(
settings,
(values) => {
const { themeType, themeBackground, ...rest } = cloneDeep(values);
Object.assign(themeSettings, defaults(settingOverrides, rest));
Object.assign(themeSettings, defaults({}, settingOverrides, rest));
},
{ immediate: true, deep: true }
);

onMounted(() => {
watch(visible, (vis) => {
if (vis && code.value != previouslyRendered.value) {
generateTokens().then(() => (rendered.value = true));
}
});

watch(
code,
debounce(() => visible.value && generateTokens(), 750)
);
});
</script>
57 changes: 49 additions & 8 deletions app/components/Canvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ const props = defineProps({
themeType: { type: String, default: 'dark' },
});

const emit = defineEmits(['update:width', 'update:height']);
const emit = defineEmits(['update:width', 'update:height', 'resize-start', 'resize-end']);

const x = ref(null);
const y = ref(null);
Expand All @@ -159,6 +159,10 @@ const { zoom, aspectRatio } = toRefs(props);
let resizeObserver = null;
let observedSceneWindow = null;
let sceneMetricsRequest = null;
let resizeRequest = null;
let pendingResize = null;
let resizeScaleX = 1;
let resizeScaleY = 1;

const ratio = computed(() => {
if (aspectRatio.value) {
Expand Down Expand Up @@ -186,22 +190,55 @@ function onResizeStart(event) {

resizingWidth.value = hasWidth || !!ratio.value;
resizingHeight.value = hasHeight || !!ratio.value;

const container = event.target.parentNode;
const containerRect = container.getBoundingClientRect();

resizeScaleX = containerRect.width / container.offsetWidth || 1;
resizeScaleY = containerRect.height / container.offsetHeight || 1;

emit('resize-start');
}

function onResizeEnd() {
flushResize();

resizingWidth.value = false;
resizingHeight.value = false;

emit('resize-end');
}

function onResize(event) {
const container = event.target.parentNode;
if (event.rect.width) {
const scaleX = container.getBoundingClientRect().width / container.offsetWidth;
emit('update:width', event.rect.width / scaleX);
pendingResize = {
width: event.rect.width,
height: event.rect.height,
};

if (!resizeRequest) {
resizeRequest = requestAnimationFrame(flushResize);
}
}

function flushResize() {
if (resizeRequest) {
cancelAnimationFrame(resizeRequest);
resizeRequest = null;
}

if (!pendingResize) {
return;
}

const { width, height } = pendingResize;
pendingResize = null;

if (width) {
emit('update:width', width / resizeScaleX);
}
if (event.rect.height) {
const scaleY = container.getBoundingClientRect().height / container.offsetHeight;
emit('update:height', event.rect.height / scaleY);

if (height) {
emit('update:height', height / resizeScaleY);
}
}

Expand Down Expand Up @@ -275,6 +312,10 @@ onBeforeUnmount(() => {
cancelAnimationFrame(sceneMetricsRequest);
}

if (resizeRequest) {
cancelAnimationFrame(resizeRequest);
}

resizeObserver?.disconnect();
});
</script>
22 changes: 1 addition & 21 deletions app/components/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,6 @@
<Monaco
ref="monaco"
class="size-full"
:width="width"
:height="height"
:value="modelValue"
:added="added"
:removed="removed"
Expand All @@ -174,8 +172,7 @@ import {
ArrowDownIcon,
CreditCardIcon,
} from 'lucide-vue-next';
import { ref, watch, toRefs, computed, onMounted, onUnmounted } from 'vue';
import { useResizeObserver } from '@vueuse/core';
import { ref, toRefs, computed, onUnmounted } from 'vue';

const props = defineProps({
id: { type: String, required: true },
Expand Down Expand Up @@ -211,8 +208,6 @@ const { sizes, orientation, language } = toRefs(props);
const { $bus, $shiki } = useNuxtApp();
const { options: languageOptions } = useLanguages();

const width = ref(0);
const height = ref(0);
const root = ref(null);
const toolbar = ref(null);

Expand All @@ -225,20 +220,5 @@ const languageAlias = computed(
language.value
);

function updateMonacoDimensions() {
if (root.value && root.value.offsetParent) {
width.value = root.value.clientWidth;
height.value = root.value.clientHeight;
}
}

useResizeObserver(root, updateMonacoDimensions);

onMounted(() => {
updateMonacoDimensions();
watch([sizes, orientation], updateMonacoDimensions);
$bus.$on('editors:refresh', updateMonacoDimensions);
});

onUnmounted(() => $bus.$emit('editors:refresh'));
</script>
Loading
Loading