INL / corpus-frontend

BlackLab Frontend, a feature-rich corpus search interface for BlackLab.
16 stars 7 forks source link

Examples in "configure the displaying of hits and their surrounding words" #452

Closed stcoats closed 1 year ago

stcoats commented 1 year ago

Hello agian! I'm trying to get the example from near the end of the section "[Results/Hits] configure the displaying of hits and their surrounding words" to work:

  /** E.g. {word: ['words', 'in', 'the', 'hit'], lemma: [...], pos: [...]} */
  type SnippetPart = { [annotationId: string]: string[]; };
  type Snippet = {
    left: SnippetPart;
    match: SnippetPart;
    right: SnippetPart;
    // Warning: there might be more!
  };
  type TransformFunction = ((snippet: Snippet): void);
  vuexModules.ui.actions.results.shared.transformSnippets(snippet => {
    const transform = (...snippets) => snippets.forEach(v => v.word = v.word.map(word => {
      if (word === 'de') return `<span style="text-decoration: underline; text-shadow: 0 0 2px red;">${word}</span>`;
      return word;
    }))
    transform(snippet.left, snippet.match, snippet.right);
  });

This is TypeScript, and it doesn't work if I just put it in the custom.search.js file. It also doesn't work if I put it in a new file called custom.ts and point to it from the search.xml page. I suppose I need to convert it to js and compile it or something? Or create a new file for the code block? If so, where do I put it?

Thanks for any tips!

KCMertens commented 1 year ago

Ah, those types are just there to serve as a hint to you for what's actually contained in the snippet object. I'll fix that example up to use jsdoc.

Just comment out or remove everything except the function itself:

vuexModules.ui.actions.results.shared.transformSnippets(snippet => {
    const transform = (...snippets) => snippets.forEach(v => v.word = v.word.map(word => {
      if (word === 'de') return `<span style="text-decoration: underline; text-shadow: 0 0 2px red;">${word}</span>`;
      return word;
    }))
    transform(snippet.left, snippet.match, snippet.right);
  });
stcoats commented 1 year ago

Thanks! It is not rendering the html:

image

KCMertens commented 1 year ago

Have you called vuexModules.ui.actions.results.shared.concordanceAsHtml(true) ? Data is not displayed as html by default to prevent security issues.

stcoats commented 1 year ago

It works now, thank you again!

image

Is there a security issue I should be concerned about if I use this feature in publicly accessible webpage?

KCMertens commented 1 year ago

No, since you have full control over the data here. We host a setup where users can upload their own data where it would be an issue to do this.

stcoats commented 1 year ago

I see. Another quick question, this always shows in the console:

image

Do I need to do something about this?

KCMertens commented 1 year ago

It's something to do with the favicon, so not very important. There's something weird going on though, it should look at /img, not /corpus/static/img (unless you configured it this way?)

stcoats commented 1 year ago

Okay, I figured out that favicon folder I generated called the file site.webmanifest, so I changed that name to manifest.json and now it's okay!

But back to the highlighting code: Say I would like to like to highlight in the results view all of the words within a particular sentence, from an input xml structure like this:

<s n="95" id="example1">
<w xml:id="w.2248" pos="JJ" lemma="happy">happy</w>
<w xml:id="w.2249" pos="NNS" lemma="holiday">holidays</w>
<w xml:id="w.2250" pos="IN" lemma="from">from</w>
<w xml:id="w.2251" pos="ADJ" lemma="all">all</w>
</s>
<s n="96" id="example2">
(etc.)

How could I approach this? Maybe doing something in the format file, such as

valuePath: "../text()"?

KCMertens commented 1 year ago

The easiest is probably to add an annotation to the words containing the sentice number/id. It's a bit wasteful, since you're now duplicating the sentence info into every word, but the frontend works with individual words, not with sentences, so we need to use a workaround.

Something like

contents: 
  annotations: 
  - name: sentence_id
    valuePath: ../@n # grab the "n" from the parent <s> element

Then in the js code, something like this?


vuexModules.ui.actions.results.shared.transformSnippets(snippet => {
  const transform = (...snippets) => snippets.forEach(token => {
    let originalOutput = token.word; // what would normally be shown is just the "word"
    let newOutput = originalOutput; // what we want to show instead
    // if the word is in the sentence we want to highlight, add some HTML to it
    if (token.sentence_id === 'some_specific_id?') newWord = `<span style="background: red; color: white; display: inline-block;">${token.word}</span>`; 
    token.word = newOutput; // and set the new output
  })
});
stcoats commented 1 year ago

Thanks again. I got it to work the way I need it with this:

vuexModules.ui.actions.results.shared.transformSnippets(snippet => {
  const transform = (...snippets) => snippets.forEach(v => {
    for (let i = 0; i < v.word.length; i++) {
      const word = v.word[i];
      const audiofile = v.audiofile[i];
      if (audiofile !== "No Audio" && v.audiofile[i] === snippet.match.audiofile[0]) {
        v.word[i] = `<span style="strikethrough; text-shadow: 0 0 2px red;">${word}</span>`;
      }
    }
  });
  transform(snippet.left, snippet.match, snippet.right);
});

But now it is showing the html in the properties table below:

image

How do I prevent this?

stcoats commented 1 year ago

I ended up just doing transform(snippet.left, snippet.right);, the match word is in boldface anyway. This removes the html from the "Value" column.