From 5a50fd8e12f234e77c718bc85bcb31c2c929dbce Mon Sep 17 00:00:00 2001 From: alan-wu Date: Fri, 5 Sep 2025 11:54:28 +1200 Subject: [PATCH 01/10] Merge in upstream changes. --- package-lock.json | 6 ++--- package.json | 2 +- src/App.vue | 25 ++++++++++++++++++- src/components.d.ts | 2 ++ src/components/SideBar.vue | 49 +++++++++++++++++++++++++++++++++++--- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index bdffafdb..7d3b787e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6328,9 +6328,9 @@ } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, "dependencies": { "esbuild": "^0.21.3", diff --git a/package.json b/package.json index cdf3e121..8102207c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@abi-software/map-side-bar", - "version": "2.10.4", + "version": "2.10.4-acupoints.0", "files": [ "dist/*", "src/*", diff --git a/src/App.vue b/src/App.vue index a1b07f15..16961bdf 100644 --- a/src/App.vue +++ b/src/App.vue @@ -18,6 +18,7 @@ keyword search Get facets Create Data/Annotation + Search Acupoints Connectivity Search @@ -42,6 +47,7 @@ + + diff --git a/src/components/AcupointsInfoSearch.vue b/src/components/AcupointsInfoSearch.vue new file mode 100644 index 00000000..ef4cdc47 --- /dev/null +++ b/src/components/AcupointsInfoSearch.vue @@ -0,0 +1,342 @@ + + + + + From 00fe3f9e6bb66b08ed00a306eb65a5ca4421c6e7 Mon Sep 17 00:00:00 2001 From: alan-wu Date: Wed, 29 Oct 2025 15:34:51 +1300 Subject: [PATCH 04/10] Add some minor changes. --- src/components.d.ts | 1 - src/components/AcupointsCard.vue | 3 ++- src/components/AcupointsInfoSearch.vue | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components.d.ts b/src/components.d.ts index c7249785..8f5cfa89 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -8,7 +8,6 @@ export {} declare module 'vue' { export interface GlobalComponents { AcupointsCard: typeof import('./components/AcupointsCard.vue')['default'] - 'AcupointsCard copy': typeof import('./components/AcupointsCard copy.vue')['default'] AcupointsInfoSearch: typeof import('./components/AcupointsInfoSearch.vue')['default'] AnnotationTool: typeof import('./components/AnnotationTool.vue')['default'] BadgesGroup: typeof import('./components/BadgesGroup.vue')['default'] diff --git a/src/components/AcupointsCard.vue b/src/components/AcupointsCard.vue index cd9e99d3..80064384 100644 --- a/src/components/AcupointsCard.vue +++ b/src/components/AcupointsCard.vue @@ -24,6 +24,7 @@ @blur="field['isEditing'] = false" @keyup.enter="field['isEditing'] = false" @vue:mounted="inputMounted" + type="textarea" /> @@ -72,7 +73,7 @@ export default { EventBus.emit('acupoints-hovered', data); }, inputMounted: function(event) { - event.el?.querySelector('input')?.focus(); + event.el?.querySelector('textarea')?.focus(); } } } diff --git a/src/components/AcupointsInfoSearch.vue b/src/components/AcupointsInfoSearch.vue index ef4cdc47..a7c03504 100644 --- a/src/components/AcupointsInfoSearch.vue +++ b/src/components/AcupointsInfoSearch.vue @@ -87,7 +87,7 @@ export default { props: { entry: { type: Object, - default: () => initial_state, + default: () => {}, }, }, data: function () { @@ -143,7 +143,8 @@ export default { value["Indications"], value["Acupuncture Method"], value["Vasculature"], - value["Innervation"] + value["Innervation"], + value["Note"], ]; const allstrings = searchFields.join(); const myJSON = allstrings.toLowerCase(); @@ -158,7 +159,6 @@ export default { this.numberOfHits = this.results.length this.searchInput = input this.lastSearch = input - console.log(this.numberOfHits) }, numberPerPageUpdate: function (val) { this.numberPerPage = val From f3ab74b9138385b3de9dc6eb440e7eed35826b62 Mon Sep 17 00:00:00 2001 From: alan-wu Date: Thu, 18 Dec 2025 16:11:48 +1300 Subject: [PATCH 05/10] Support expand collapse of acupoint details. --- src/components/AcupointsCard.vue | 67 ++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/components/AcupointsCard.vue b/src/components/AcupointsCard.vue index 80064384..c931b7f8 100644 --- a/src/components/AcupointsCard.vue +++ b/src/components/AcupointsCard.vue @@ -8,26 +8,30 @@ @mouseleave="cardHovered(undefined)" >
-
Acupoint: {{ entry.Acupoint }}
- +
{{ entry.Acupoint }}
+ + + + +
@@ -41,6 +45,7 @@ import EventBus from './EventBus.js' export default { data() { return { + expanded: [], displayFields: [ {name: "Synonym", isEditing: false}, {name: "Chinese Name", isEditing: false}, @@ -55,6 +60,15 @@ export default { } }, name: 'AcupointsCard', + computed: { + showDetailsText: function() { + if (this.expanded.length > 0) { + return "Click here to hide information" + } else { + return "Click here to show more information" + } + } + }, props: { /** * Object containing information for @@ -66,6 +80,11 @@ export default { }, }, methods: { + expandedChanged: function() { + if (this.expanded.length > 0) { + EventBus.emit('acupoints-clicked', this.entry); + } + }, cardClicked: function (data) { EventBus.emit('acupoints-clicked', data); }, @@ -80,11 +99,17 @@ export default { diff --git a/src/services/flatmapKnowledge.js b/src/services/flatmapKnowledge.js new file mode 100644 index 00000000..e5116292 --- /dev/null +++ b/src/services/flatmapKnowledge.js @@ -0,0 +1,94 @@ +async function getReferenceConnectivitiesFromStorage(resource) { + const flatmapKnowledgeRaw = sessionStorage.getItem('flatmap-knowledge'); + + if (flatmapKnowledgeRaw) { + const flatmapKnowledge = JSON.parse(flatmapKnowledgeRaw); + const dataWithRefs = flatmapKnowledge.filter((x) => x.references && x.references.length); + const foundData = dataWithRefs.filter((x) => x.references.includes(resource)); + + if (foundData.length) { + const featureIds = foundData.map((x) => x.id); + return featureIds; + } + } + return []; +} + +async function getReferenceConnectivitiesByAPI(mapImp, resource, flatmapQueries) { + const knowledgeSource = getKnowledgeSource(mapImp); + const sql = `select knowledge from knowledge + where source="${knowledgeSource}" and + knowledge like "%${resource}%" order by source desc`; + console.log(sql) + const response = await flatmapQueries.flatmapQuery(sql); + const mappedData = response.values.map((x) => x[0]); + const parsedData = mappedData.map((x) => JSON.parse(x)); + const featureIds = parsedData.map((x) => x.id); + return featureIds; +} + +function getKnowledgeSource(mapImp) { + let mapKnowledgeSource = ''; + if (mapImp.provenance?.connectivity) { + const sckanProvenance = mapImp.provenance.connectivity; + if ('knowledge-source' in sckanProvenance) { + mapKnowledgeSource = sckanProvenance['knowledge-source']; + } else if ('npo' in sckanProvenance) { + mapKnowledgeSource = `${sckanProvenance.npo.release}-npo`; + } + } + + return mapKnowledgeSource; +} + +function loadAndStoreKnowledge(mapImp, flatmapQueries) { + const knowledgeSource = getKnowledgeSource(mapImp); + const sql = `select knowledge from knowledge + where source="${knowledgeSource}" + order by source desc`; + const flatmapKnowledge = sessionStorage.getItem('flatmap-knowledge'); + + if (!flatmapKnowledge) { + flatmapQueries.flatmapQuery(sql).then((response) => { + const mappedData = response.values.map(x => x[0]); + const parsedData = mappedData.map(x => JSON.parse(x)); + sessionStorage.setItem('flatmap-knowledge', JSON.stringify(parsedData)); + updateFlatmapKnowledgeCache(); + }); + } +} + +function updateFlatmapKnowledgeCache() { + const CACHE_LIFETIME = 24 * 60 * 60 * 1000; // One day + const now = new Date(); + const expiry = now.getTime() + CACHE_LIFETIME; + + sessionStorage.setItem('flatmap-knowledge-expiry', expiry); +} + +function removeFlatmapKnowledgeCache() { + const keys = [ + 'flatmap-knowledge', + 'flatmap-knowledge-expiry', + ]; + keys.forEach((key) => { + sessionStorage.removeItem(key); + }); +} + +function refreshFlatmapKnowledgeCache() { + const expiry = sessionStorage.getItem('flatmap-knowledge-expiry'); + const now = new Date(); + + if (now.getTime() > expiry) { + removeFlatmapKnowledgeCache(); + } +} + +export { + getReferenceConnectivitiesFromStorage, + getReferenceConnectivitiesByAPI, + loadAndStoreKnowledge, + getKnowledgeSource, + refreshFlatmapKnowledgeCache, +} diff --git a/src/services/flatmapQueries.js b/src/services/flatmapQueries.js new file mode 100644 index 00000000..b343a043 --- /dev/null +++ b/src/services/flatmapQueries.js @@ -0,0 +1,498 @@ +/* eslint-disable no-alert, no-console */ +// remove duplicates by stringifying the objects +const removeDuplicates = function (arrayOfAnything) { + if (!arrayOfAnything) return [] + return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) => + JSON.parse(e) + ) +} + +const cachedLabels = {} +const cachedTaxonLabels = []; + +const findTaxonomyLabel = async function (flatmapAPI, taxonomy) { + if (cachedLabels && cachedLabels.hasOwnProperty(taxonomy)) { + return cachedLabels[taxonomy] + } + + return new Promise((resolve) => { + fetch(`${flatmapAPI}knowledge/label/${taxonomy}`, { + method: 'GET', + }) + .then((response) => response.json()) + .then((data) => { + let label = data.label + if (label === 'Mammalia') { + label = 'Mammalia not otherwise specified' + } + cachedLabels[taxonomy] = label + resolve(label) + }) + .catch((error) => { + console.error('Error:', error) + cachedLabels[taxonomy] = taxonomy + resolve(taxonomy) + }) + }) +} + +const findTaxonomyLabels = async function (mapImp, taxonomies) { + const intersectionTaxonomies = taxonomies.filter((taxonomy) => + cachedTaxonLabels.some((obj) => obj.taxon === taxonomy) + ); + + const foundCachedTaxonLabels = cachedTaxonLabels.filter((obj) => + intersectionTaxonomies.includes(obj.taxon) + ); + + const leftoverTaxonomies = taxonomies.filter((taxonomy) => + !intersectionTaxonomies.includes(taxonomy) + ); + + if (!leftoverTaxonomies.length) { + return foundCachedTaxonLabels; + } else { + const entityLabels = await mapImp.queryLabels(leftoverTaxonomies); + if (entityLabels.length) { + entityLabels.forEach((entityLabel) => { + let { entity: taxon, label } = entityLabel; + if (label === 'Mammalia') { + label = 'Mammalia not otherwise specified' + } + const item = { taxon, label }; + foundCachedTaxonLabels.push(item); + cachedTaxonLabels.push(item); + }); + return foundCachedTaxonLabels; + } + } +} + +const inArray = function (ar1, ar2) { + if (!ar1 || !ar2) return false + let as1 = JSON.stringify(ar1) + let as2 = JSON.stringify(ar2) + return as1.indexOf(as2) !== -1 +} + +let FlatmapQueries = function () { + this.initialise = function (flatmapApi) { + this.flatmapApi = flatmapApi + this.destinations = [] + this.origins = [] + this.components = [] + this.rawURLs = [] + this.controller = undefined + this.uberons = [] + this.lookUp = [] + } + + this.createTooltipData = async function (mapImp, eventData) { + let hyperlinks = [] + if ( + eventData.feature.hyperlinks && + eventData.feature.hyperlinks.length > 0 + ) { + hyperlinks = eventData.feature.hyperlinks + } else { + hyperlinks = this.rawURLs; + } + let taxonomyLabel = undefined + if (eventData.provenanceTaxonomy) { + taxonomyLabel = [] + const entityLabels = await findTaxonomyLabels(mapImp, eventData.provenanceTaxonomy); + if (entityLabels.length) { + entityLabels.forEach((entityLabel) => { + const { label } = entityLabel; + taxonomyLabel.push(label); + }); + } + } + + let tooltipData = { + destinations: this.destinations, + origins: this.origins, + components: this.components, + destinationsWithDatasets: this.destinationsWithDatasets, + originsWithDatasets: this.originsWithDatasets, + componentsWithDatasets: this.componentsWithDatasets, + title: eventData.label, + featureId: eventData.resource, + hyperlinks: hyperlinks, + provenanceTaxonomy: eventData.provenanceTaxonomy, + provenanceTaxonomyLabel: taxonomyLabel, + } + return tooltipData + } + + this.createComponentsLabelList = function (components, lookUp) { + let labelList = [] + components.forEach((n) => { + labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp) + if (n.length === 2) { + labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp) + } + }) + return labelList + } + + this.createLabelLookup = function (mapImp, uberons) { + return new Promise(async (resolve) => { + let uberonMap = {} + this.uberons = [] + const entityLabels = await findTaxonomyLabels(mapImp, uberons); + if (entityLabels.length) { + entityLabels.forEach((entityLabel) => { + const { taxon: entity, label } = entityLabel; + uberonMap[entity] = label; + this.uberons.push({ + id: entity, + name: label, + }) + }); + resolve(uberonMap) + } + }) + } + + this.buildConnectivitySqlStatement = function (keastIds) { + let sql = 'select knowledge from knowledge where entity in (' + if (keastIds.length === 1) { + sql += `'${keastIds[0]}')` + } else if (keastIds.length > 1) { + for (let i in keastIds) { + sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} ` + } + } + return sql + } + + this.buildLabelSqlStatement = function (uberons) { + let sql = 'select entity, label from labels where entity in (' + if (uberons.length === 1) { + sql += `'${uberons[0]}')` + } else if (uberons.length > 1) { + for (let i in uberons) { + sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} ` + } + } + return sql + } + + this.findAllIdsFromConnectivity = function (connectivity) { + let dnodes = connectivity.connectivity.flat() // get nodes from edgelist + let nodes = [...new Set(dnodes)] // remove duplicates + let found = [] + nodes.forEach((n) => { + if (Array.isArray(n)) { + found.push(n.flat()) + } else { + found.push(n) + } + }) + return [...new Set(found.flat())] + } + + this.flattenConntectivity = function (connectivity) { + let dnodes = connectivity.flat() // get nodes from edgelist + let nodes = [...new Set(dnodes)] // remove duplicates + let found = [] + nodes.forEach((n) => { + if (Array.isArray(n)) { + found.push(n.flat()) + } else { + found.push(n) + } + }) + return found.flat() + } + + this.findComponents = function (connectivity) { + let dnodes = connectivity.connectivity.flat() // get nodes from edgelist + let nodes = removeDuplicates(dnodes) + + let found = [] + let terminal = false + nodes.forEach((node) => { + terminal = false + // Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination) + if (inArray(connectivity.axons, node)) { + terminal = true + } + if (connectivity.somas && inArray(connectivity.somas, node)) { + terminal = true + } + if (inArray(connectivity.dendrites, node)) { + terminal = true + } + if (!terminal) { + found.push(node) + } + }) + + return found + } + + this.retrieveFlatmapKnowledgeForEvent = async function (mapImp, eventData) { + // check if there is an existing query + if (this.controller) this.controller.abort() + + // set up the abort controller + this.controller = new AbortController() + const signal = this.controller.signal + + const keastIds = eventData.resource + this.destinations = [] + this.origins = [] + this.components = [] + this.rawURLs = [] + if (!keastIds || keastIds.length == 0 || !keastIds[0]) return + + let prom1 = this.queryForConnectivityNew(mapImp, keastIds, signal) // This on returns a promise so dont need 'await' + let results = await Promise.all([prom1]) + return results + } + + this.queryForConnectivityNew = function (mapImp, keastIds, signal, processConnectivity=true) { + return new Promise((resolve) => { + mapImp.queryKnowledge(keastIds[0]) + .then((response) => { + if (this.checkConnectivityExists(response)) { + let connectivity = response; + if (processConnectivity) { + this.processConnectivity(mapImp, connectivity).then((processedConnectivity) => { + // response.references is publication urls + if (response.references) { + // with publications from both PubMed and Others + this.rawURLs = [...response.references]; + resolve(processedConnectivity) + } else { + // without publications + resolve(processedConnectivity) + } + }) + } + else resolve(connectivity) + } else { + resolve(false) + } + }) + .catch((error) => { + if (error.name === 'AbortError') { + // This error is from AbortController's abort method. + } else { + // console.error('Error:', error) + // TODO: to update after queryKnowledge method update + console.warn(`Unable to get the knowledge for the entity ${keastIds[0]}.`) + } + resolve(false) + }) + }) + } + + this.queryForConnectivity = function (mapImp, keastIds, signal, processConnectivity=true) { + const data = { sql: this.buildConnectivitySqlStatement(keastIds) } + const headers = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + ...(signal ? { signal: signal } : {}), // add signal to header if it exists + } + return new Promise((resolve) => { + fetch(`${this.flatmapApi}knowledge/query/`, headers) + .then((response) => response.json()) + .then((data) => { + if (this.connectivityExists(data)) { + let connectivity = JSON.parse(data.values[0][0]) + if (processConnectivity) { + this.processConnectivity(mapImp, connectivity).then((processedConnectivity) => { + resolve(processedConnectivity) + }) + } + else resolve(connectivity) + } else { + resolve(false) + } + }) + .catch((error) => { + if (error.name === 'AbortError') { + // This error is from AbortController's abort method. + } else { + console.error('Error:', error) + } + resolve(false) + }) + }) + } + + this.checkConnectivityExists = function (data) { + return data && data.connectivity?.length; + }; + + this.connectivityExists = function (data) { + if ( + data.values && + data.values.length > 0 && + JSON.parse(data.values[0][0]).connectivity && + JSON.parse(data.values[0][0]).connectivity.length > 0 + ) { + return true + } else { + return false + } + } + + // This function is used to determine if a node is a single node or a node with multiple children + // Returns the id of the node if it is a single node, otherwise returns false + this.findIfNodeIsSingle = function (node) { + if (node.length === 1) { // If the node is in the form [id] + console.error("Server returns a single node", node) + return node[0] + } else { + if (node.length === 2 && node[1].length === 0) { // If the node is in the form [id, []] + return node[0] + } else { + return false // If the node is in the form [id, [id1, id2]] + } + } + } + + this.createLabelFromNeuralNode = function (node, lookUp) { + + // Check if the node is a single node or a node with multiple children + let nodeIsSingle = this.findIfNodeIsSingle(node) + + // Case where node is in the form [id] + if (nodeIsSingle) { + return lookUp[nodeIsSingle] + } + + // Case where node is in the form [id, [id1 (,id2)]] + let label = lookUp[node[0]] + if (node.length === 2 && node[1].length > 0) { + node[1].forEach((n) => { + if (lookUp[n] == undefined) { + label += `, ${n}` + } else { + label += `, ${lookUp[n]}` + } + }) + } + return label + } + + this.flattenAndFindDatasets = function (components, axons, dendrites) { + // process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button) + let componentsFlat = this.flattenConntectivity(components) + let axonsFlat = this.flattenConntectivity(axons) + let dendritesFlat = this.flattenConntectivity(dendrites) + + // Filter for the anatomy which is annotated on datasets + this.destinationsWithDatasets = this.uberons.filter( + (ub) => axonsFlat.indexOf(ub.id) !== -1 + ) + this.originsWithDatasets = this.uberons.filter( + (ub) => dendritesFlat.indexOf(ub.id) !== -1 + ) + this.componentsWithDatasets = this.uberons.filter( + (ub) => componentsFlat.indexOf(ub.id) !== -1 + ) + } + + this.processConnectivity = function (mapImp, connectivity) { + return new Promise((resolve) => { + // Filter the origin and destinations from components + let components = this.findComponents(connectivity) + + // Remove duplicates + let axons = removeDuplicates(connectivity.axons) + //Somas will become part of origins, support this for future proof + let dendrites = [] + if (connectivity.somas && connectivity.somas.length > 0) { + dendrites.push(...connectivity.somas) + } + if (connectivity.dendrites && connectivity.dendrites.length > 0) { + dendrites.push(...connectivity.dendrites) + } + dendrites = removeDuplicates(dendrites) + + // Create list of ids to get labels for + let conIds = this.findAllIdsFromConnectivity(connectivity) + + // Create readable labels from the nodes. Setting this to 'this.origins' updates the display + this.createLabelLookup(mapImp, conIds).then((lookUp) => { + this.destinations = axons.map((a) => + this.createLabelFromNeuralNode(a, lookUp) + ) + this.origins = dendrites.map((d) => + this.createLabelFromNeuralNode(d, lookUp) + ) + this.components = components.map((c) => + this.createLabelFromNeuralNode(c, lookUp) + ) + this.flattenAndFindDatasets(components, axons, dendrites) + resolve({ + ids: { + axons: axons, + dendrites: dendrites, + components: components, + }, + labels: { + destinations: this.destinations, + origins: this.origins, + components: this.components, + } + }) + }) + }) + } + + this.flattenConntectivity = function (connectivity) { + let dnodes = connectivity.flat() // get nodes from edgelist + let nodes = [...new Set(dnodes)] // remove duplicates + let found = [] + nodes.forEach((n) => { + if (Array.isArray(n)) { + found.push(n.flat()) + } else { + found.push(n) + } + }) + return found.flat() + } + + this.buildPubmedSqlStatement = function (keastIds) { + let sql = 'select distinct publication from publications where entity in (' + if (keastIds.length === 1) { + sql += `'${keastIds[0]}')` + } else if (keastIds.length > 1) { + for (let i in keastIds) { + sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} ` + } + } + return sql + } + + this.buildPubmedSqlStatementForModels = function (model) { + return `select distinct publication from publications where entity = '${model}'` + } + + this.flatmapQuery = function (sql) { + const data = { sql: sql } + return fetch(`${this.flatmapApi}knowledge/query/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then((response) => response.json()) + .catch((error) => { + console.error('Error:', error) + }) + } +} + +export { FlatmapQueries, findTaxonomyLabel, findTaxonomyLabels } From c3f9e0620d678d89480667dc0f33015101317d74 Mon Sep 17 00:00:00 2001 From: alan-wu Date: Thu, 18 Dec 2025 16:12:32 +1300 Subject: [PATCH 07/10] Increment beta version. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ce3de7a0..d8915ebb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@abi-software/map-side-bar", - "version": "2.11.4-acupoints.0", + "version": "2.11.4-acupoints.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@abi-software/map-side-bar", - "version": "2.11.4-acupoints.0", + "version": "2.11.4-acupoints.1", "license": "Apache-2.0", "dependencies": { "@abi-software/gallery": "^1.2.0", diff --git a/package.json b/package.json index 12a0352b..6f0bd90a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@abi-software/map-side-bar", - "version": "2.11.4-acupoints.0", + "version": "2.11.4-acupoints.1", "license": "Apache-2.0", "repository": { "type": "git", From c453382d5d77f98276635112eb44e3b0b52a0930 Mon Sep 17 00:00:00 2001 From: alan-wu Date: Wed, 14 Jan 2026 11:42:19 +1300 Subject: [PATCH 08/10] Improve the style. --- src/components/AcupointsCard.vue | 27 ++++++++++++++------------ src/components/AcupointsInfoSearch.vue | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/AcupointsCard.vue b/src/components/AcupointsCard.vue index c931b7f8..c6a55b2f 100644 --- a/src/components/AcupointsCard.vue +++ b/src/components/AcupointsCard.vue @@ -8,9 +8,8 @@ @mouseleave="cardHovered(undefined)" >
-
{{ entry.Acupoint }}
- +