Skip to content

Commit

Permalink
fix: [63] copy html to clipboard (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
basecode authored Nov 21, 2023
1 parent b2935d6 commit 2ab8e0b
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 32 deletions.
5 changes: 3 additions & 2 deletions web-src/src/components/FavoriteCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Copy from '@spectrum-icons/workflow/Copy';
import Delete from '@spectrum-icons/workflow/Delete';
import { motion } from 'framer-motion';
import { useToggleFavorite } from '../state/ToggleFavoriteHook.js';
import { toClipboard, toHTML } from '../helpers/ExportPrompt.js';

const styles = {
card: css`
Expand All @@ -42,7 +43,7 @@ export function FavoriteCard({ variant, ...props }) {
<View
{...props}
UNSAFE_className={styles.card}>
<div className={styles.variant}>{variant.content}</div>
<div className={styles.variant} dangerouslySetInnerHTML={{ __html: toHTML(variant.content) }} />
<View
borderRadius="regular"
paddingRight="24px">
Expand All @@ -51,7 +52,7 @@ export function FavoriteCard({ variant, ...props }) {
<ActionButton
isQuiet
UNSAFE_className="hover-cursor-pointer"
onPress={() => navigator.clipboard.writeText(variant.content)}>
onPress={() => navigator.clipboard.write(toClipboard(toHTML(variant.content)))}>
<Copy />
</ActionButton>
<Tooltip>Copy</Tooltip>
Expand Down
29 changes: 2 additions & 27 deletions web-src/src/components/GenerateButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,7 @@ import { generationInProgressState } from '../state/GenerationInProgressState.js
import { parametersState } from '../state/ParametersState.js';
import { LegalTermsLink } from './LegalTermsLink.js';
import { useSaveSession } from '../state/SaveSessionHook.js';

function objectToString(obj) {
return String(obj).replace(/<\/?[^>]+(>|$)/g, '');
}

function jsonToString(json) {
if (json === null || typeof json !== 'object') {
return objectToString(json);
}
return Object.entries(json).map(([key, value]) => {
return `<b>${key}</b>: ${objectToString(value)}`;
}).join('<br/>');
}

function createVariants(response) {
try {
const json = JSON.parse(response);
if (Array.isArray(json)) {
return json.map((item) => ({ id: uuid(), content: jsonToString(item) }));
} else {
return [{ id: uuid(), content: String(response) }];
}
} catch (error) {
return [{ id: uuid(), content: String(response) }];
}
}
import { createVariants } from '../helpers/CreateVariants.js';

export function GenerateButton() {
const { firefallService } = useApplicationContext();
Expand All @@ -69,7 +44,7 @@ export function GenerateButton() {
const { queryId, response } = await firefallService.complete(finalPrompt, temperature, imsToken);
setResults((results) => [...results, {
resultId: queryId,
variants: createVariants(response),
variants: createVariants(uuid, response),
prompt: finalPrompt,
promptTemplate: prompt,
parameters,
Expand Down
7 changes: 4 additions & 3 deletions web-src/src/components/ResultCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { promptState } from '../state/PromptState.js';
import { parametersState } from '../state/ParametersState.js';
import { resultsState } from '../state/ResultsState.js';
import { useSaveSession } from '../state/SaveSessionHook.js';
import { toClipboard, toHTML } from '../helpers/ExportPrompt.js';

const styles = {
card: css`
Expand Down Expand Up @@ -185,13 +186,13 @@ export function ResultCard({ result, ...props }) {
${variant.id === selectedVariant.id && styles.variantSelected};
${isFavorite(variant) && styles.variantFavorite};
`}
dangerouslySetInnerHTML={{ __html: variant.content }} />
dangerouslySetInnerHTML={{ __html: toHTML(variant.content) }} />
</a>
);
})
}
</div>
<div className={styles.resultContent} dangerouslySetInnerHTML={{ __html: selectedVariant.content }}/>
<div className={styles.resultContent} dangerouslySetInnerHTML={{ __html: toHTML(selectedVariant.content) }}/>
<div className={styles.resultActions}>
<TooltipTrigger delay={0}>
<ActionButton
Expand All @@ -206,7 +207,7 @@ export function ResultCard({ result, ...props }) {
<ActionButton
isQuiet
UNSAFE_className="hover-cursor-pointer"
onPress={() => navigator.clipboard.writeText(selectedVariant.content)}>
onPress={() => navigator.clipboard.write(toClipboard(toHTML(selectedVariant.content)))}>
<Copy/>
</ActionButton>
<Tooltip>Copy</Tooltip>
Expand Down
28 changes: 28 additions & 0 deletions web-src/src/helpers/CreateVariants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2023 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

function objectToString(obj) {
return String(obj).replace(/<\/?[^>]+(>|$)/g, '');
}

export function createVariants(uuid, response) {
try {
const json = JSON.parse(response);
if (Array.isArray(json)) {
return json.map((content) => ({ id: uuid(), content: content === null || typeof content !== 'object' ? objectToString(content) : content }) );
} else {
return [{ id: uuid(), content: json }];
}
} catch (error) {
return [{ id: uuid(), content: String(response) }];
}
}
32 changes: 32 additions & 0 deletions web-src/src/helpers/CreateVariants.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { createVariants } from './CreateVariants.js';

const uuid = () => 123;

describe('createVariants', () => {
test('response is a JSON string: an array of json objects', () => {
const response = [
{key1: 'value1', key2: 'value2'},
{key3: 'value3', key4: 'value4'},
null,
'Hello!',
'<tag>how</tag> is it going?'
];
expect(createVariants(uuid, JSON.stringify(response))).toEqual([
{ id: 123, content: {key1: 'value1', key2: 'value2'} },
{ id: 123, content: {key3: 'value3', key4: 'value4'} },
{ id: 123, content: 'null' },
{ id: 123, content: 'Hello!' },
{ id: 123, content: 'how is it going?' }
]);
});
test('response is a JSON string: an object', () => {
const response = {key: 'value'};
expect(createVariants(uuid, JSON.stringify(response))).toEqual([
{ id: 123, content: {key: 'value'} },
]);
});
test('response is a string', () => {
const response = 'Hello! How can I assist you today?';
expect(createVariants(uuid, response)).toEqual([{ id: 123, content: response }]);
});
});
27 changes: 27 additions & 0 deletions web-src/src/helpers/ExportPrompt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2023 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
export function toClipboard(html) {
const span = document.createElement('span');
span.innerHTML = html;
const blob = new Blob([span.outerHTML], { type: 'text/html' });
return [new ClipboardItem({ [blob.type]: blob })];
}

export function toHTML(input) {
if (typeof input === 'string') {
return input;
} else {
return Object.entries(input).map(([key, value]) => {
return `<b>${key}</b>: ${value}`;
}).join('<br/>');
}
}
13 changes: 13 additions & 0 deletions web-src/src/helpers/ExportPrompt.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { toClipboard, toHTML } from './ExportPrompt.js';

describe('toHTML', () => {
test('returns html: prompt result is an key/value pair', () => {
const promptResponse = {key1: 'value1', key2: 'value2'};
expect(toHTML(promptResponse)).toEqual('<b>key1</b>: value1<br/><b>key2</b>: value2');
});
test('returns html: prompt result is a string', () => {
const promptResponse = 'hello';
expect(toHTML(promptResponse)).toEqual('hello');
});
});

0 comments on commit 2ab8e0b

Please sign in to comment.