Skip to content

[WIP] Update ABCRenderer to re-render on abcNotation change#2

Draft
loctran016 with Copilot wants to merge 1 commit into
mainfrom
copilot/update-abcrenderer-re-render
Draft

[WIP] Update ABCRenderer to re-render on abcNotation change#2
loctran016 with Copilot wants to merge 1 commit into
mainfrom
copilot/update-abcrenderer-re-render

Conversation

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.

Update the Vue component ABCRenderer.vue so that ABC notation re-renders whenever the abcNotation prop changes, not only on the first mount. The current implementation imports abcjs and calls renderAbc() inside onMounted(), which means selecting a different tune in the parent component does not update the rendered sheet music.

Requirements:

  • Keep client-side-only loading of abcjs to avoid SSR issues.
  • Re-render the sheet music whenever props.abcNotation changes.
  • Clear any previous render before drawing the new tune.
  • Preserve the existing responsive rendering behavior.
  • Keep the solution idiomatic for Vue 3 <script setup> and TypeScript if possible.

The parent component uses <ABCRenderer v-if="tuneAbc" :abcNotation="tuneAbc" /> and expects the sheet music to update when a new tune is selected.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: ```

  <ComboboxContent
    class="absolute z-10 w-full mt-1 min-w-[160px] bg-white overflow-hidden rounded-lg shadow-sm border border-stone-800/40 will-change-[opacity,transform]"
  >
    <ComboboxViewport class="p-[5px]">
      <ComboboxEmpty class="text-xs font-medium text-center py-2 text-stone-400" />

      <ComboboxItem
        v-for="tune in tuneList"
        :key="tune.path"
        :value="tune"
        :textValue="tune.title"
        class="text-sm leading-none rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-stone-100"
      >
        <!-- <ComboboxItemIndicator
          class="absolute left-0 w-[25px] inline-flex items-center justify-center"
        >
          <Icon icon="radix-icons:check" />
        </ComboboxItemIndicator> -->
        <span>{{ tune.title }}</span>
      </ComboboxItem>
    </ComboboxViewport>
  </ComboboxContent>
</ComboboxRoot>

<div class="w-3/4 mx-auto mt-10">
  <ABCRenderer v-if="tuneAbc" :abcNotation="tuneAbc" />
</div>
<script setup lang="ts"> import { ComboboxAnchor, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxRoot, ComboboxTrigger, ComboboxViewport, } from 'reka-ui' type Tune = { path: string title: string } const tuneModules = import.meta.glob('~/assets/data/tunes/*.abc', { query: '?raw' }) const tuneList: Tune[] = Object.keys(tuneModules).map((path) => { const filename = path.split('/').pop() ?? '' const base = filename.replace(/\.abc$/i, '') const title = base .replace(/[-_]+/g, ' ') .split(' ') .filter(Boolean) .map((word) => word.charAt(0).toLocaleUpperCase('vi-VN') + word.slice(1)) .join(' ') return { path, title } }) const selectedTune = ref(tuneList[0] ?? null) const tuneAbc = ref(null) const tuneCache = new Map() async function loadTune(path: string) { if (!path) return const cached = tuneCache.get(path) if (cached) { tuneAbc.value = cached return } const mod = (await tuneModules[path]()) as { default: string } tuneCache.set(path, mod.default) tuneAbc.value = mod.default } watch( selectedTune, (tune) => { if (tune) loadTune(tune.path) }, { immediate: true }, ) definePageMeta({ colorMode: 'light', }) </script>

<script setup> import { ref, onMounted } from 'vue'; const props = defineProps({ abcNotation: { type: String, required: true } }); const sheetMusic = ref(null); onMounted(async () => { // Import abcjs only on the client side to avoid SSR errors const abcjs = await import('abcjs'); // Render the music into the DOM ref if (sheetMusic.value) { abcjs.renderAbc(sheetMusic.value, props.abcNotation, { responsive: 'resize', // selectionColor: 'oklch(71.4% 0.203 305.504)' selectionColor: 'oklch(55.8% 0.288 302.321)' }); } }); </script>
<ABCRenderer v-if="tuneAbc" :abcNotation="tuneAbc" /> only render the first time it's loaded.

Here is the ABCRenderer.vue:

Assistant: `ABCRenderer` only renders once because `abcjs.renderAbc(...)` is called only in `onMounted()`. When `abcNotation` changes later, the component stays mounted, so nothing re-renders.

Fix it by watching `props.abcNotation` and re-rendering whenever it changes.

```vue name=ABCRenderer.vue
<template>
  <div>
    <div ref="sheetMusic"></div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, nextTick } from 'vue'

const props = defineProps<{
  abcNotation: string
}>()

const sheetMusic = ref<HTMLElement | null>(null)
let abcjs: typeof import('abcjs') | null = null

async function render() {
  if (!sheetMusic.value || !props.abcNotation) return
  if (!abcjs) abcjs = await import('abcjs')

  sheetMusic.value.innerHTML = ''
  abcjs.renderAbc(sheetMusic.value, props.abcNotation, {
    responsive: 'resize',
    selectionColor: 'oklch(55.8% 0.288 302.321)',
  })
}

onMounted(() => {
  render()
})

watch(
  () => props.abcNotation,
  async () => {
    await nextTick()
    render()
  },
)
</script>

A few notes:

  • sheetMusic.value.innerHTML = '' clears the previous render.
  • watch(() => props.abcNotation, ...) makes it update whenever the selected tune changes.
  • Caching the abcjs import avoids importing it every time.

If you want, I can also show a version that uses watchEffect() or handles cleanup more safely for repeated renders.

User: Yes, do it for me

Assistant: I can do it, but I need the repository path first.

Please send the GitHub repo in owner/repo form, and I’ll update ABCRenderer.vue for you.

User: Here

Assistant: I’m waiting for your confirmation to continue.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hobby Ready Ready Preview, Comment Jul 1, 2026 9:05am

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants