marketplace.vue

 1<template>
 2  <div class="marketplace">
 3    <div v-if="loading" class="loading">Loading plugins...</div>
 4    <div v-if="error" class="error">Error: {{ error }}</div>
 5    <div v-if="!loading && !error" class="grid">
 6      <PluginCard v-for="plugin in plugins" :key="plugin.name" :plugin="plugin" />
 7    </div>
 8  </div>
 9</template>
10
11<script setup>
12import { ref, onMounted } from "vue";
13import PluginCard from "./PluginCard.vue";
14
15const REGISTRY_URL =
16  "https://raw.githubusercontent.com/floatpane/matcha/master/plugins/registry.json";
17
18const plugins = ref([]);
19const loading = ref(true);
20const error = ref(null);
21
22onMounted(() => {
23  fetch(REGISTRY_URL)
24    .then((res) => {
25      if (!res.ok) throw new Error(`Failed to fetch registry (${res.status})`);
26      return res.json();
27    })
28    .then((data) => {
29      plugins.value = data;
30      loading.value = false;
31    })
32    .catch((err) => {
33      error.value = err.message;
34      loading.value = false;
35    });
36});
37</script>
38
39<style scoped>
40.marketplace {
41  max-width: 1200px;
42  margin: 0 auto;
43  padding: 0 1rem 3rem;
44}
45
46.grid {
47  display: grid;
48  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
49  gap: 1.25rem;
50}
51
52.loading,
53.error {
54  text-align: center;
55  color: var(--muted);
56  padding: 3rem 0;
57  font-size: 1.1rem;
58}
59
60.error {
61  color: var(--danger);
62}
63</style>