Skip to content

Commit

Permalink
Merge pull request #462 from polymorpheuz/dev
Browse files Browse the repository at this point in the history
feat: Annotated Text component
  • Loading branch information
ramedina86 authored Aug 14, 2024
2 parents ce99061 + 0f4e1cb commit ede9c75
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/ui/src/core/templateMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import CoreLink from "../core_components/content/CoreLink.vue";
import CoreChatbot from "../core_components/content/CoreChatbot.vue";
import CoreTags from "../core_components/content/CoreTags.vue";
import CoreAvatar from "../core_components/content/CoreAvatar.vue";
import CoreAnnotatedText from "../core_components/content/CoreAnnotatedText.vue";
import CoreJsonViewer from "../core_components/content/CoreJsonViewer.vue";

// input
import CoreCheckboxInput from "../core_components/input/CoreCheckboxInput.vue";
import CoreColorInput from "../core_components/input/CoreColorInput.vue";
Expand Down Expand Up @@ -118,6 +120,7 @@ const templateMap = {
switchinput: CoreSwitchInput,
reuse: CoreReuse,
avatar: CoreAvatar,
annotatedtext: CoreAnnotatedText,
jsonviewer: CoreJsonViewer,
};

Expand Down
60 changes: 60 additions & 0 deletions src/ui/src/core_components/base/BaseControlBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<div class="BaseControlBar">
<button
v-if="props.copyStructuredContent"
class="control-button"
@click="copyToClipboard({ text: props.copyStructuredContent })"
>
Copy JSON
</button>
<button
v-if="props.copyRawContent"
class="control-button"
@click="copyToClipboard({ text: props.copyRawContent })"
>
Copy
</button>
</div>
</template>

<script setup lang="ts">
const props = defineProps<{
copyRawContent?: string;
copyStructuredContent?: string;
}>();
function copyToClipboard({ text = "" }: { text?: string }) {
try {
navigator.clipboard.writeText(text);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
}
</script>

<style scoped>
@import "../../renderer/sharedStyles.css";
.BaseControlBar {
margin: 10px 0;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.control-button {
background-color: var(--buttonColor);
border: none;
border-radius: 8px;
color: white;
cursor: pointer;
font-size: 11px;
margin-right: 10px;
padding: 4px 8px;
&:hover {
opacity: 0.9;
}
}
</style>
250 changes: 250 additions & 0 deletions src/ui/src/core_components/content/CoreAnnotatedText.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
<template>
<div class="CoreAnnotatedText">
<span
v-for="(content, i) in fields.text.value"
:key="String(content) + i"
>
<template v-if="typeof content === 'string'">{{
content
}}</template>
<span
v-if="Array.isArray(content)"
class="annotation"
:style="{ background: content[2] || generateColor(content[1]) }"
>
{{ content[0] }}
<span v-if="content[1]" class="annotation-subject">
{{ content[1] }}
</span>
</span>
</span>
<template v-if="fields.copyButtons.value === 'yes'">
<BaseControlBar
:copy-raw-content="textToString(fields.text.value)"
:copy-structured-content="stringifyData(fields.text.value)"
/>
</template>
</div>
</template>

<script lang="ts">
import { cssClasses, primaryTextColor } from "../../renderer/sharedStyleFields";
export default {
writer: {
name: "Annotated text",
description: "Shows text with annotations",
category: "Content",
fields: {
text: {
name: "KeyValue",
type: FieldType.Object,
desc: "Value array with text/annotations. Must be a JSON string or a state reference to an array.",
init: `["This ",["is", "Verb"]," some ",["annotated", "Adjective"], ["text", "Noun"]," for those of ",["you", "Pronoun"]," who ",["like", "Verb"]," this sort of ",["thing", "Noun"],". ","And here's a ",["word", "", "#faf"]," with a fancy background but no label."]`,
},
referenceColor: {
name: "Reference",
desc: "The colour to be used as reference for chroma and luminance, and as the starting point for hue rotation.",
type: FieldType.Color,
default: "#5551FF",
category: FieldCategory.Style,
},
seed: {
name: "Seed value",
desc: "Choose a different value to reshuffle colours.",
type: FieldType.Number,
default: "1",
category: FieldCategory.Style,
},
rotateHue: {
name: "Rotate hue",
desc: "If active, rotates the hue depending on the content of the string. If turned off, the reference colour is always used.",
type: FieldType.Text,
options: {
yes: "yes",
no: "no",
},
default: "yes",
category: FieldCategory.Style,
},
copyButtons: {
name: "Copy buttons",
desc: "If active, adds a control bar with both copy text and JSON buttons.",
type: FieldType.Text,
options: {
yes: "yes",
no: "no",
},
default: "no",
category: FieldCategory.Style,
},
primaryTextColor,
cssClasses,
},
previewField: "data",
},
};
</script>
<script setup lang="ts">
import { FieldCategory, FieldType } from "../../writerTypes";
import injectionKeys from "../../injectionKeys";
import { inject } from "vue";
import chroma, { Color } from "chroma-js";
const fields = inject(injectionKeys.evaluatedFields);
const COLOR_STEPS = [
{ h: -78, s: -34, l: 16 },
{ h: -61, s: -37, l: 16 },
{ h: -2, s: 0, l: 24 },
{ h: -12, s: 0, l: 29 },
{ h: 28, s: -20, l: 24 },
{ h: -61, s: -95, l: 25 },
{ h: -173, s: 0, l: 16 },
{ h: -228, s: 0, l: 22 },
{ h: 69, s: 0, l: 25 },
{ h: 70, s: 0, l: 29 },
];
let currentSteps = [...COLOR_STEPS];
let subjectColorCache = {};
let lastSeed = fields.seed.value;
function generateColorCss(
baseColor: Color,
colorData: { h: number; s: number; l: number },
) {
let genColor = baseColor
.set(
"hsl.h",
`${Math.sign(colorData.h) == -1 ? "-" : "+"}${Math.abs(colorData.h)}`,
)
.set(
"hsl.s",
`${Math.sign(colorData.s) == -1 ? "-" : "+"}${Math.abs(colorData.s / 100.0)}`,
)
.set(
"hsl.l",
`${Math.sign(colorData.l) == -1 ? "-" : "+"}${Math.abs(colorData.l / 100.0)}`,
);
return genColor.css();
}
function calculateColorStep(s: string, stepsLength = COLOR_STEPS.length) {
let hash = (fields.seed.value * 52673) & 0xffffffff;
for (let i = 0; i < s.length; i++) {
hash = ((i + 1) * s.charCodeAt(i)) ^ (hash & 0xffffffff);
}
const step = Math.abs(hash) % stepsLength;
return step;
}
function generateColor(s: string) {
if (fields.rotateHue.value == "no") {
return fields.referenceColor.value;
}
const baseColor = chroma(fields.referenceColor.value);
if (lastSeed !== fields.seed.value) {
currentSteps = [...COLOR_STEPS];
subjectColorCache = {};
lastSeed = fields.seed.value;
}
if (subjectColorCache[s]) {
return generateColorCss(baseColor, subjectColorCache[s]);
}
// If we run out of colors, reset the list
if (currentSteps.length === 0) {
currentSteps = [...COLOR_STEPS];
}
const colorStep = calculateColorStep(s, currentSteps.length);
const colorData = currentSteps[colorStep];
subjectColorCache[s] = colorData;
currentSteps.splice(colorStep, 1);
return generateColorCss(baseColor, colorData);
}
function textToString(text: string[]) {
return text.reduce((acc, val) => {
if (typeof val === "string") {
return acc + val;
}
return acc + val[0];
}, "");
}
function stringifyData(arr: string[]) {
try {
return JSON.stringify(arr);
} catch (e) {
return arr.join("");
}
}
</script>

<style scoped>
.CoreAnnotatedText {
color: var(--primaryTextColor);
line-height: 1.8;
}
.annotation {
flex-direction: row;
align-items: center;
background: rgba(255, 164, 33, 0.4);
border-radius: 8px;
padding: 2px 8px;
overflow: hidden;
line-height: 1;
vertical-align: middle;
}
.annotation-subject {
display: inline-flex;
font-size: 10px;
margin-left: 14px;
opacity: 0.5;
position: relative;
vertical-align: middle;
&::after {
content: "";
border-left: 1px solid;
opacity: 0.1;
position: absolute;
top: 0px;
left: -9px;
height: 10px;
}
}
.controls {
margin: 10px 0;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.control-button {
background-color: var(--buttonColor);
border: none;
border-radius: 8px;
color: white;
cursor: pointer;
font-size: 11px;
margin-right: 10px;
padding: 4px 8px;
&:hover {
opacity: 0.9;
}
}
</style>

0 comments on commit ede9c75

Please sign in to comment.