diff --git a/packages/web-components/src/components/chat/components/chartElement/__stories__/chartElement.stories.ts b/packages/web-components/src/components/chat/components/chartElement/__stories__/chartElement.stories.ts index 376bc6c1..a24abfa2 100644 --- a/packages/web-components/src/components/chat/components/chartElement/__stories__/chartElement.stories.ts +++ b/packages/web-components/src/components/chat/components/chartElement/__stories__/chartElement.stories.ts @@ -184,6 +184,18 @@ export const Thumbnails = { container-height="156px" container-width="156px"> + 0 ? substring(datum.name,0,indexof(datum.name, \' \')) : datum.name","as":"bLabel"}],"mark":"text","encoding":{"longitude":{"field":"cx","type":"quantitative"},"latitude":{"field":"cy","type":"quantitative"},"text":{"field":"bLabel","type":"nominal"},"size":{"value":8},"opacity":{"value":0.6}}},{"data":{"url":"https://vega.github.io/vega-lite/examples/data/londonTubeLines.json","format":{"type":"topojson","feature":"line"}},"mark":{"type":"geoshape","filled":false,"strokeWidth":2},"encoding":{"color":{"field":"id","type":"nominal","legend":{"title":null,"orient":"bottom-right","offset":0},"scale":{"domain":["Bakerloo","Central","Circle","District","DLR","Hammersmith & City","Jubilee","Metropolitan","Northern","Piccadilly","Victoria","Waterloo & City"],"range":["rgb(137,78,36)","rgb(220,36,30)","rgb(255,206,0)","rgb(1,114,41)","rgb(0,175,173)","rgb(215,153,175)","rgb(106,114,120)","rgb(114,17,84)","rgb(0,0,0)","rgb(0,24,168)","rgb(0,160,226)","rgb(106,187,170)"]}}}}]}'}" + container-height="156px" + container-width="156px"> + + window:pointermove!","translate":"[pointerdown[event.shiftKey], window:pointerup] > window:pointermove!","zoom":"wheel![event.shiftKey]"}},{"name":"grid","select":{"type":"interval","resolve":"global","translate":"[pointerdown[!event.shiftKey], window:pointerup] > window:pointermove!","zoom":"wheel![!event.shiftKey]"},"bind":"scales"}],"encoding":{"x":{"field":{"repeat":"column"},"type":"quantitative"},"y":{"field":{"repeat":"row"},"type":"quantitative","axis":{"minExtent":30}},"color":{"condition":{"param":"brush","field":"Origin","type":"nominal"},"value":"grey"}}}}'}" + container-height="156px" + container-width="156px"> + `, }; @@ -404,6 +416,38 @@ export const FacetingTest = { content="${JSON.stringify(item.spec)}"> ` )} +

Interactive multi-scatter plot

+
+ window:pointermove!","translate":"[pointerdown[event.shiftKey], window:pointerup] > window:pointermove!","zoom":"wheel![event.shiftKey]"}},{"name":"grid","select":{"type":"interval","resolve":"global","translate":"[pointerdown[!event.shiftKey], window:pointerup] > window:pointermove!","zoom":"wheel![!event.shiftKey]"},"bind":"scales"}],"encoding":{"x":{"field":{"repeat":"column"},"type":"quantitative"},"y":{"field":{"repeat":"row"},"type":"quantitative","axis":{"minExtent":30}},"color":{"condition":{"param":"brush","field":"Origin","type":"nominal"},"value":"grey"}}}}'}" + container-height="450px" + container-width="600px"> + +
+ window:pointermove!","translate":"[pointerdown[event.shiftKey], window:pointerup] > window:pointermove!","zoom":"wheel![event.shiftKey]"}},{"name":"grid","select":{"type":"interval","resolve":"global","translate":"[pointerdown[!event.shiftKey], window:pointerup] > window:pointermove!","zoom":"wheel![!event.shiftKey]"},"bind":"scales"}],"encoding":{"x":{"field":{"repeat":"column"},"type":"quantitative"},"y":{"field":{"repeat":"row"},"type":"quantitative","axis":{"minExtent":30}},"color":{"condition":{"param":"brush","field":"Origin","type":"nominal"},"value":"grey"}}}}'}" + container-height="950px"> + +
+

Multi histogram

+
+ + +
+ + +
+ + +
`, }; diff --git a/packages/web-components/src/components/chat/components/chartElement/__stories__/examples2.json b/packages/web-components/src/components/chat/components/chartElement/__stories__/examples2.json index be684ed8..89e05d2f 100644 --- a/packages/web-components/src/components/chat/components/chartElement/__stories__/examples2.json +++ b/packages/web-components/src/components/chat/components/chartElement/__stories__/examples2.json @@ -806,7 +806,7 @@ "result": { "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "data": { - "url": "https://vega.github.io/vega-lite/data/seattle-weather.csv" + "url": "https://vega.github.io/editor/data/weather.csv" }, "title": "Daily Max Temperatures (C) in Cleveland, OH", "config": { diff --git a/packages/web-components/src/components/chat/components/chartElement/__stories__/examples3.json b/packages/web-components/src/components/chat/components/chartElement/__stories__/examples3.json index 70fcb41e..c0ad596d 100644 --- a/packages/web-components/src/components/chat/components/chartElement/__stories__/examples3.json +++ b/packages/web-components/src/components/chat/components/chartElement/__stories__/examples3.json @@ -1,4 +1,36 @@ [ + {"name": "concat", + "spec":{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Two horizonally concatenated charts that show a histogram of precipitation in Seattle and the relationship between min and max temperature.", + "data": {"url": "https://vega.github.io/editor/data/weather.csv"}, + "transform": [{"filter": "datum.location === 'Seattle'"}], + "columns": 2, + "concat": [ + { + "mark": "bar", + "encoding": { + "x": {"timeUnit": "month", "field": "date", "type": "ordinal"}, + "y": {"aggregate": "mean", "field": "precipitation"} + } + }, + { + "mark": "bar", + "encoding": { + "x": {"timeUnit": "month", "field": "date", "type": "ordinal"}, + "y": {"aggregate": "median", "field": "precipitation"} + } + }, + { + "mark": "point", + "encoding": { + "x": {"field": "temp_min", "bin": true}, + "y": {"field": "temp_max", "bin": true}, + "size": {"aggregate": "count"} + } + } + ] +}}, { "name": "trellis", "spec": { @@ -439,7 +471,7 @@ "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "description": "A population pyramid for the US in 2000.", "data": { - "url": "https://vega.github.io/vega-lite/data/population.json" + "url": "https://vega.github.io/editor/data/population.json" }, "transform": [ { @@ -655,36 +687,5 @@ } } } -},{"name": "concat", - "spec":{ - "$schema": "https://vega.github.io/schema/vega-lite/v5.json", - "description": "Two horizonally concatenated charts that show a histogram of precipitation in Seattle and the relationship between min and max temperature.", - "data": {"url": "https://vega.github.io/vega-lite/data/seattle-weather.csv"}, - "transform": [{"filter": "datum.location === 'Seattle'"}], - "columns": 2, - "concat": [ - { - "mark": "bar", - "encoding": { - "x": {"timeUnit": "month", "field": "date", "type": "ordinal"}, - "y": {"aggregate": "mean", "field": "precipitation"} - } - }, - { - "mark": "bar", - "encoding": { - "x": {"timeUnit": "month", "field": "date", "type": "ordinal"}, - "y": {"aggregate": "median", "field": "precipitation"} - } - }, - { - "mark": "point", - "encoding": { - "x": {"field": "temp_min", "bin": true}, - "y": {"field": "temp_max", "bin": true}, - "size": {"aggregate": "count"} - } - } - ] -}} +} ] \ No newline at end of file diff --git a/packages/web-components/src/components/chat/components/chartElement/src/chartElement.scss b/packages/web-components/src/components/chat/components/chartElement/src/chartElement.scss index 9ab5bd98..74983cbf 100644 --- a/packages/web-components/src/components/chat/components/chartElement/src/chartElement.scss +++ b/packages/web-components/src/components/chat/components/chartElement/src/chartElement.scss @@ -15,9 +15,9 @@ overflow: hidden; box-sizing: border-box; padding: 0; - border: 1px solid $border-subtle-00; + border: 1px solid var(--chat-chart-border-color, $border-subtle-00); border-radius: 8px; - background: $background; + background: $text-inverse; block-size: var(--chat-chart-element-height, '320px'); font-family: 'IBM Plex Sans Condensed', sans-serif; inline-size: var(--chat-chart-element-width, '100%'); @@ -32,6 +32,10 @@ inline-size: 100%; } + .#{$clabs-prefix}--chat-chart-container-selected { + border: 2px solid $highlight; + } + .#{$clabs-prefix}--chat-chart-options { position: absolute; display: flex; @@ -66,8 +70,8 @@ .#{$clabs-prefix}--chat-chart-thumbnail-container img { position: absolute; - block-size: calc(100%); - inline-size: calc(100%); + block-size: 100%; + inline-size: 100%; inset-block-start: 0; inset-inline-start: 0; } diff --git a/packages/web-components/src/components/chat/components/chartElement/src/chartElement.template.ts b/packages/web-components/src/components/chat/components/chartElement/src/chartElement.template.ts index fd4bc9cf..3af1cef8 100644 --- a/packages/web-components/src/components/chat/components/chartElement/src/chartElement.template.ts +++ b/packages/web-components/src/components/chat/components/chartElement/src/chartElement.template.ts @@ -72,6 +72,7 @@ export function chartElementTemplate(customElementClass) { _chartClicked: chartClicked, exportedImageURL, _handleFullScreenScroll: handleFullScreenScroll, + selected, } = customElementClass; return html` @@ -207,7 +208,9 @@ export function chartElementTemplate(customElementClass) { ${_visualizationSpec && !errorMessage && !streaming ? html`
` : html`
diff --git a/packages/web-components/src/components/chat/components/chartElement/src/chartElement.ts b/packages/web-components/src/components/chat/components/chartElement/src/chartElement.ts index 5846868e..8d475a24 100644 --- a/packages/web-components/src/components/chat/components/chartElement/src/chartElement.ts +++ b/packages/web-components/src/components/chat/components/chartElement/src/chartElement.ts @@ -32,6 +32,18 @@ export default class chartElement extends LitElement { @property({ type: Boolean, attribute: 'debug-mode' }) debugMode = true; + /** + * selected- highlight if attr is chosen + */ + @property({ type: Boolean, attribute: 'selected' }) + selected; + + /** + * selectable- enable highlight if clicked + */ + @property({ type: Boolean, attribute: 'selectable' }) + selectable = true; + /** * Event listener to check if parent visibility changed */ @@ -270,6 +282,18 @@ export default class chartElement extends LitElement { @state() isHovered = false; + /** + * cellHeight - for multi charts, record max cell size + */ + @state() + cellHeight; + + /** + * cellWidth - for multi charts, record max cell size + */ + @state() + cellWidth; + /** * _latestError - Vega erro message to display */ @@ -282,6 +306,12 @@ export default class chartElement extends LitElement { @state() _specType; + /** + * selected - show highlight when chart clicked if selectable enabled + */ + @state() + _isSelected = false; + /** detect when component is rendered to process visualization specification object */ firstUpdated() { @@ -560,9 +590,10 @@ export default class chartElement extends LitElement { if (this._specType === 'plain' || this._specType === 'layered') { chosenSpec.height = 'container'; chosenSpec.width = 'container'; + } else { + chosenSpec.autosize = { resize: false }; } - chosenSpec.autosize = { resize: false }; if (this.thumbNail) { chosenSpec.width = 400; chosenSpec.height = 300; @@ -1295,6 +1326,8 @@ export default class chartElement extends LitElement { let plainSpec; let subChartWidth; let subChartHeight; + this.cellWidth = null; + this.cellHeight = null; if ('layer' in spec) { this._specType = 'layered'; @@ -1354,9 +1387,9 @@ export default class chartElement extends LitElement { if (currentContainerWidth) { let rowCount; let columnCount; - const legendHeight = 16 * 3; + const legendHeight = 16 + 16 * Math.floor(currentContainerWidth / 130); const paddingOffset = { vertical: 0, horizontal: 0 }; - const gapSize = 17; + const gapSize = 22; if (spec.repeat) { if (Array.isArray(repeatedSpec['repeat'])) { @@ -1414,6 +1447,9 @@ export default class chartElement extends LitElement { (rowCount + 1) * gapSize) / rowCount - 42; + + this.cellWidth = subChartWidth; + this.cellHeight = subChartHeight; } if (spec.repeat) { @@ -1432,6 +1468,8 @@ export default class chartElement extends LitElement { if (subChartHeight) { repeatedSpec['spec']['height'] = subChartHeight; } + this.cellWidth = subChartWidth; + this.cellHeight = subChartHeight; } else if (spec.vconcat || spec.hconcat || spec.concat) { for (let l = 0; l < repeatedSpec[this._specType].length; l++) { repeatedSpec[this._specType][l] = this._prepareSpecification( @@ -1440,7 +1478,9 @@ export default class chartElement extends LitElement { true, 0 ); - if (subChartWidth) { + if (repeatedSpec[this._specType][l].mark?.type === 'text') { + repeatedSpec[this._specType][l]['width'] = 40; + } else if (subChartWidth) { repeatedSpec[this._specType][l]['width'] = subChartWidth; //repeatedSpec[this._specType][l].config.legend.columns =calcColumn; @@ -1453,6 +1493,8 @@ export default class chartElement extends LitElement { repeatedSpec['width'] = subChartWidth; repeatedSpec['height'] = subChartHeight; } + this.cellWidth = subChartWidth; + this.cellHeight = subChartHeight; } else { this._specType = 'plain'; if (!spec['data']) { @@ -2005,8 +2047,8 @@ export default class chartElement extends LitElement { title: null, symbolType: 'square', symbolLimit: 30, - labelLimit: 60, - columns: { signal: 'floor(width / 70)' }, + labelLimit: 120, + columns: { signal: 'floor(width / 130)' }, orient: 'bottom', symbolOpacity: 1, direction: 'horizontal', @@ -2027,7 +2069,12 @@ export default class chartElement extends LitElement { }, }; - spec['config'].axis.titleLimit = 100; //Math.min(spec.height,spec.width) + if (this.cellHeight && this.cellWidth) { + spec['config'].axis.titleLimit = Math.min( + this.cellHeight, + this.cellWidth + ); + } } this._authorizeSingleSelection = false; @@ -2268,8 +2315,13 @@ export default class chartElement extends LitElement { } if (this._authorizeMultiSelection) { - const brushInteraction: { name: string; select: object } = { + const brushInteraction: { + name: string; + resolve: string; + select: object; + } = { name: 'brush', + resolve: 'union', select: { type: 'interval' }, }; params.push(brushInteraction); diff --git a/packages/web-components/src/components/chat/components/chat/src/chat.template.ts b/packages/web-components/src/components/chat/components/chat/src/chat.template.ts index 17fa0ef3..feba4396 100644 --- a/packages/web-components/src/components/chat/components/chat/src/chat.template.ts +++ b/packages/web-components/src/components/chat/components/chat/src/chat.template.ts @@ -33,6 +33,7 @@ export function chatTemplate(customElementClass) { agentName, loading, closed, + forceAutoUpdate, maxCharacterCount, disableHeaderMenu, disableHeaderButtons, @@ -151,6 +152,7 @@ export function chatTemplate(customElementClass) { .messages="${messages}" user-name="${userName}" agent-name="${agentName}" + ?force-scroll-down="${forceAutoUpdate}" ?docking-enabled="${enableDocking}" ?loading="${queryInProgress}" ?stream-responses="${streamResponses}" diff --git a/packages/web-components/src/components/chat/components/chat/src/chat.ts b/packages/web-components/src/components/chat/components/chat/src/chat.ts index 658670ed..32895077 100644 --- a/packages/web-components/src/components/chat/components/chat/src/chat.ts +++ b/packages/web-components/src/components/chat/components/chat/src/chat.ts @@ -57,6 +57,12 @@ export default class CLABSChat extends LitElement { @property({ type: Boolean, attribute: 'auto-update', reflect: true }) autoUpdate; + /** + * force-auto-update - force scroll down no matter what + */ + @property({ type: Boolean, attribute: 'force-auto-update' }) + forceAutoUpdate; + /** * show launcher when closed */ diff --git a/packages/web-components/src/components/chat/components/codeElement/__stories__/codeElement.stories.js b/packages/web-components/src/components/chat/components/codeElement/__stories__/codeElement.stories.js index 823c6da4..9d3fe1f7 100644 --- a/packages/web-components/src/components/chat/components/codeElement/__stories__/codeElement.stories.js +++ b/packages/web-components/src/components/chat/components/codeElement/__stories__/codeElement.stories.js @@ -37,41 +37,190 @@ const defaultPlaygroundArgs = { disableCopyButton: true, }; +const frenchLocalized = { + 'code-copypaste-button': 'Copier le code', + 'code-copypaste-success': 'Copié!', + 'code-estimated-warning': '(estimé)', + 'code-editing-validation': 'Appliquer', + 'code-editing-cancelled': 'Annuler', + 'code-line-descriptor': 'lignes', +}; const codeExamples = { - 'single command': 'node -v', - 'npm command': '$ npm install --save carbon-components', + cmd: 'node -v', + bash: '$ npm install --save carbon-components', 'console multi': 'user@Macbook-Air server % npm run build\nuser@Macbook-Air server % npm run lint:styles --fix\nuser@Macbook-Air server % npm format:write\nuser@Macbook-Air server % python3 server.py', - 'html example': - '\n\n\t\n\t\tThis is the title of the webpage\n\t\n\t\n\t\n\t\t

This is an example paragraph. Anything in the body tag will appear on the page, just like this p tag and its contents.

\n\t\n', + html: '\n\n\t\n\t\tThis is the title of the webpage\n\t\n\t\n\t\n\t\t

This is an example paragraph. Anything in the body tag will appear on the page, just like this p tag and its contents.

\n\t\n', + CSS: `@import url('style.css');\n@media(min-width:760px) {\n\t.box {\n\t\tgrid-template-rows: auto 1fr;\n\t}\nbody {\n\t--main-color: #ff0;\n\tmargin:0;\n\tanimation: spin 3s infinite;\n}\n.container {\n\tcontent:"test";\n\tdisplay: flex;\n\tpadding: 10px;\n}\n.item[data-id="1"]:hover::after {\n\tcontent: attr(data-id);\n\tcolor:red !important;\n}`, 'python code': 'from math import sqrt\n#prime function to check given number prime or not:\ndef Prime(number,itr):\n\t#base condition\n\tif itr == 1:\n\t\treturn True\n\t#if given number divided by itr or not\n\tif number % itr == 0:\n\t\treturn False\n\t#Recursive function Call\n\tif Prime(number,itr-1) == False:\n\t\treturn False\n\treturn True\n', - 'carbon datatable': `import React from "react";\nimport { DataTable } from "..";\nconst {\n\tTable,\n\tTableBody,\n\tTableCell,\n\tTableContainer,\n\tTableHead,\n\tTableHeader,\n\tTableRow\n} = DataTable;\nimport mdx from "../DataTable.mdx";\nimport "./datatable-story.scss";\nexport default {\n\ttitle: "Components/DataTable/Basic",\n\tcomponent: DataTable,\n\tsubcomponents: {\n\t\tTableContainer,\n\t\tTable,\n\t\tTableHead,\n\t\tTableRow,\n\t\tTableHeader,\n\t\tTableBody,\n\t\tTableCell\n\t},\n\tparameters: {\n\t\tdocs: {\n\t\t\tpage: mdx\n\t\t}\n\t}\n};\nexport const Default = () => {\n\tconst rows = [{\n\t\tid: "load-balancer-1",\n\t\tname: "Load Balancer 1",\n\t\trule: "Round robin",\n\t\tStatus: "Starting",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-2",\n\t\tname: "Load Balancer 2",\n\t\trule: "DNS delegation",\n\t\tstatus: "Active",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-3",\n\t\tname: "Load Balancer 3",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-4",\n\t\tname: "Load Balancer 4",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-5",\n\t\tname: "Load Balancer 5",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-6",\n\t\tname: "Load Balancer 6",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-7",\n\t\tname: "Load Balancer 7",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}];\n\tconst headers = ["Name", "Rule", "Status", "Other", "Example"];\n\treturn \n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{headers.map(header => \n\t\t\t\t\t\t\t{header}\n\t\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t{rows.map(row => \n\t\t\t\t\t\t{Object.keys(row).filter(key => key !== "id").map(key => {\n\t\t\t\t\treturn {row[key]};\n\t\t\t\t})}\n\t\t\t\t\t)}\n\t\t\t\n\t\t
;\n};`, + 'C++': + '#include \n#include "llama.cpp/llama.h"\n\nint main() {\n\tllama_model *model = llama_load_model_from_file("path/to/model.bin"); \n\n\tif (model == nullptr) {\n\t\tstd::cerr << "Failed to load model.";\n\t\treturn 1;\n\t}\n\n\tstd::string prompt = "Hello, how are you today?";\n\tllama_context *ctx = llama_new_context_with_model(model, 512); \n\n\tstd::cout << "Prompt: " << prompt << std::endl;\n\tstd::cout << "Response: ";\n\n\tfor (int i = 0; i < 100; ++i) {\n\t\tllama_token token = llama_sample_token(ctx, nullptr);\n\t\tstd::cout << llama_token_to_str(model, token);\n\t}\n\n\tstd::cout << std::endl;\n\n\tllama_free_context(ctx);\n\tllama_free_model(model);\n\n\treturn 0;\n}', 'SQL example': `-- Simple SQL file example\n-- Creating a table named 'employees'\nCREATE TABLE employees (\nid INT PRIMARY KEY,\nfirst_name VARCHAR(50),\nlast_name VARCHAR(50),\nemail VARCHAR(100),\ndepartment_id INT,\nhire_date DATE\n);\n-- Creating a table named 'departments'\nCREATE TABLE departments (\nid INT PRIMARY KEY,\nname VARCHAR(50)\n);\n-- Inserting data into the 'departments' table\nINSERT INTO departments (id, name) VALUES\n(1, 'Human Resources'),\n(2, 'Marketing'),\n(3, 'Sales'),\n(4, 'IT');\n-- Inserting data into the 'employees' table\nINSERT INTO employees (id, first_name, last_name, email, department_id, hire_date) VALUES\n(1, 'John', 'Doe', 'john.doe@example.com', 3, '2020-01-01'),\n(2, 'Jane', 'Doe', 'jane.doe@example.com', 2, '2019-06-15'),\n(3, 'Jim', 'Smith', 'jim.smith@example.com', 3, '2021-02-20');\n`, COBOL: `IDENTIFICATION DIVISION.\nPROGRAM-ID. VARS.\nDATA DIVISION.\n\t*> working storage defines variables\n\tWORKING-STORAGE SECTION.\n\t*> define a number with a sign, 3 numbers, a decimal, and then\n\t*> two numbers aafter the decimal. by default it should be 0 filled\n\t01 FIRST-VAR PIC S9(3)V9(2).\n\t*> do the same thing as above but actually initialize\n\t*> to a number -123.45\n\t01 SECOND-VAR PIC S9(3)V9(2) VALUE -123.45.\n\t*> defines an alphabetic string and initialize it to abcdef\n\t01 THIRD-VAR PIC A(6) VALUE 'ABCDEF'.\n\t*> define an alphanumeric string and initialize it to a121$\n\t01 FOURTH-VAR PIC X(5) VALUE 'A121$'.\n\t*> create a grouped variable\n\t01 GROUP-VAR.\n\t\t05 SUBVAR-1 PIC 9(3) VALUE 337.\n\t\t*> create 3 alphanumerics, but use less than\n\t\t*> the allocated space for each of them\n\t\t05 SUBVAR-2 PIC X(15) VALUE 'LALALALA'.\n\t\t05 SUBVAR-3 PIC X(15) VALUE 'LALALA'.\n\t\t05 SUBVAR-4 PIC X(15) VALUE 'LALALA'.\n*> print our variables\nPROCEDURE DIVISION.\n\tDISPLAY "1ST VAR :"FIRST-VAR.\n\tDISPLAY "2ND VAR :"SECOND-VAR.\n\tDISPLAY "3RD VAR :"THIRD-VAR.\n\tDISPLAY "4TH VAR :"FOURTH-VAR.\n\tDISPLAY "GROUP VAR :"GROUP-VAR.\n\tSTOP RUN.`, Java: `public class BinaryConverter {\n\t\n\tpublic static void main(String[] args){\n\t\tfor(int i = -5; i < 33; i++){\n\t\t\tSystem.out.println(i + ": " + toBinary(i));\n\t\t\tSystem.out.println(i);\n\t\t\t//always another way\n\t\t\tSystem.out.println(i + ": " + Integer.toBinaryString(i));\n\t\t}\n\t}\n\t\n\t/*\n\t * pre: none\n\t * post: returns a String with base10Num in base 2\n\t */\n\tpublic static String toBinary(int base10Num){\n\t\tboolean isNeg = base10Num < 0;\n\t\tbase10Num = Math.abs(base10Num);\n\t\tString result = "";\n\t\t\n\t\twhile(base10Num > 1){\n\t\t\tresult = (base10Num % 2) + result;\n\t\t\tbase10Num /= 2;\n\t\t}\n\t\tassert base10Num == 0 || base10Num == 1 : "value is not <= 1: " + base10Num;\n\t\t\n\t\tresult = base10Num + result;\n\t\tassert all0sAnd1s(result);\n\t\t\n\t\tif( isNeg )\n\t\t\tresult = "-" + result;\n\t\treturn result;\n\t}\n\t\n\t/*\n\t * pre: cal != null\n\t * post: return true if val consists only of characters 1 and 0, false otherwise\n\t */\n\tpublic static boolean all0sAnd1s(String val){\n\t\tassert val != null : "Failed precondition all0sAnd1s. parameter cannot be null";\n\t\tboolean all = true;\n\t\tint i = 0;\n\t\tchar c;\n\t\t\n\t\twhile(all && i < val.length()){\n\t\t\tc = val.charAt(i);\n\t\t\tall = c == '0' || c == '1';\n\t\t\ti++;\n\t\t}\n\t\treturn all;\n\t}\n}`, - 'C++': - '#include \nusing namespace std;\n\n\nint main() {\n\tint x = 5;\n\tint y = 6;\n\tint sum = x + y;\n\tcout << sum;\n\treturn 0;\n}\n', JavaScript: '// A simple JavaScript function to calculate the area of a rectangle\nfunction calculateRectangleArea(length, width) {\n\treturn length * width;\n}\n\n// Example usage:\nlet area = calculateRectangleArea(5, 10);\nconsole.log("Area:", area); // Output: Area: 50', + R: `# Fibonacci sequence function in R +fibonacci <- function(n) { + if (n <= 0) { + return(NULL) + } else if (n == 1) { + return(0) + } else if (n == 2) { + return(1) + } else { + a <- 0 + b <- 1 + for (i in 3:n) { + c <- a + b + a <- b + b <- c + } + return(b) + } +} +cat("Fibonacci(10): ", fibonacci(10), "\n")`, + Lisp: `; Fibonacci sequence function in Lisp +(defun fibonacci (n) +(cond ((< n 1) nil) +((= n 1) 0) +((= n 2) 1) +(t (let ((a 0) +(b 1)) +(loop for i from 3 to n +do (let ((c (+ a b))) +(setf a b) +(setf b c))) +b))))) +(print (fibonacci 10))`, + 'C#': `// Fibonacci sequence function in C# +using System; +class Program +{ + static void Main() + { + Console.WriteLine("Fibonacci(10): " + Fibonacci(10)); + } + static int Fibonacci(int n) + { + if (n <= 0) + { + return -1; + } + else if (n == 1) + { + return 0; + } + else if (n == 2) + { + return 1; + } + else + { + int a = 0; + int b = 1; + for (int i = 3; i <= n; i++) + { + int c = a + b; + a = b; + b = c; + } + return b; + } + } +}`, + PHP: `=<;:9876543s+O\n#include "llama.cpp/llama.h"\n\nint main() {\n\tllama_model *model = llama_load_model_from_file("path/to/model.bin"); \n\n\tif (model == nullptr) {\n\t\tstd::cerr << "Failed to load model.";\n\t\treturn 1;\n\t}\n\n\tstd::string prompt = "Hello, how are you today?";\n\tllama_context *ctx = llama_new_context_with_model(model, 512); \n\n\tstd::cout << "Prompt: " << prompt << std::endl;\n\tstd::cout << "Response: ";\n\n\tfor (int i = 0; i < 100; ++i) {\n\t\tllama_token token = llama_sample_token(ctx, nullptr);\n\t\tstd::cout << llama_token_to_str(model, token);\n\t}\n\n\tstd::cout << std::endl;\n\n\tllama_free_context(ctx);\n\tllama_free_model(model);\n\n\treturn 0;\n}', + 'carbon datatable': `import React from "react";\nimport { DataTable } from "..";\nconst {\n\tTable,\n\tTableBody,\n\tTableCell,\n\tTableContainer,\n\tTableHead,\n\tTableHeader,\n\tTableRow\n} = DataTable;\nimport mdx from "../DataTable.mdx";\nimport "./datatable-story.scss";\nexport default {\n\ttitle: "Components/DataTable/Basic",\n\tcomponent: DataTable,\n\tsubcomponents: {\n\t\tTableContainer,\n\t\tTable,\n\t\tTableHead,\n\t\tTableRow,\n\t\tTableHeader,\n\t\tTableBody,\n\t\tTableCell\n\t},\n\tparameters: {\n\t\tdocs: {\n\t\t\tpage: mdx\n\t\t}\n\t}\n};\nexport const Default = () => {\n\tconst rows = [{\n\t\tid: "load-balancer-1",\n\t\tname: "Load Balancer 1",\n\t\trule: "Round robin",\n\t\tStatus: "Starting",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-2",\n\t\tname: "Load Balancer 2",\n\t\trule: "DNS delegation",\n\t\tstatus: "Active",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-3",\n\t\tname: "Load Balancer 3",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-4",\n\t\tname: "Load Balancer 4",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-5",\n\t\tname: "Load Balancer 5",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-6",\n\t\tname: "Load Balancer 6",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}, {\n\t\tid: "load-balancer-7",\n\t\tname: "Load Balancer 7",\n\t\trule: "Round robin",\n\t\tstatus: "Disabled",\n\t\tother: "Test",\n\t\texample: "22"\n\t}];\n\tconst headers = ["Name", "Rule", "Status", "Other", "Example"];\n\treturn \n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{headers.map(header => \n\t\t\t\t\t\t\t{header}\n\t\t\t\t\t\t)}\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t{rows.map(row => \n\t\t\t\t\t\t{Object.keys(row).filter(key => key !== "id").map(key => {\n\t\t\t\t\treturn {row[key]};\n\t\t\t\t})}\n\t\t\t\t\t)}\n\t\t\t\n\t\t
;\n};`, }; -export const Showcase = { +export const OptionShowcase = { /** * Renders the template for Storybook * * @returns {TemplateResult<1>} */ render: () => html` -

Single line code

+

Single line code without copy


@@ -79,66 +228,193 @@ export const Showcase = {

Single line command with copy



-

Multi line command


- +

Maximum height

+
+

C++ with no height set

+ +
+

C++ with max-height 200px

+
+
+ +

Coloring and Ticks

+

Python example with no coloring or ticks

-

SQL example with ticks

+
+

SQL example without ticks


-

Python code example with language name

+
+

Info display (language and count)

+
+

Python code example with language name: user language



-

C++ example

+

C++ example with guessed language from highlight js


- + + +
+
+

C++ example with line count (no ticks)

+
+ +
-

HTML with ticks


- +

C++ example with both

+
+
-

COBOL example


- +

Tab sizing

+
+

HTML example with default tab-size


-

Java example

+
- +

HTML example with tab-size=6

+
+ +
-

JS example

-
-

FORTRAN example

- +

Localization


-

Malbolge example

+

HTML example with customLabels in french


- + + `, }; +/* +export const ColorTesting = { + render: () => html` +

Top languages

+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+

Web development

+
+ +
+ +
+ +
+ +
+ +
+

Scripting

+
+ +
+ +
+ +
+

Data science

+
+ +
+ +
+ +
+

Misc

+
+ +
+ +
+ +
+ `, +};*/ +export const ColorTesting = { + /** + * Renders the template for Storybook + * + * @returns {TemplateResult<1>} + */ + render: () => + html` ${Object.keys(codeExamples).map( + (key) => + html` + + +
+ ` + )}`, +}; + export const Editing = { /** * Renders the template for Storybook @@ -177,13 +453,6 @@ export const Editing = { content="${codeExamples['SQL example']}">

-

C++ example

- - -

JS carbon example

- editable + `editable` Make every line editable by users When a user-input is needed to fix an object - streaming + `streaming` Rerenders component when content is streamed in When streaming is enabled - enable-coloring - When editable is invoked, remove edit button - Disable button to validate edit + `max-height` + Limits total height and scrolls if needed + When code is large or height is limited + + + `display-line-count` + Shows line count at top of component + When code length is variable + + + `tab-size` + Set tab spacing for new lines + When default spacing of 2 is insufficient + + + `enable-language-display` + Show language at top of component + When generation offers multiple options + + + `displayed-language` + Force top-level language name + When automatic detection isn't accurate + + + `enable-estimated-language` + Show guessed language from coloring + When language is unknown + + + `disable-coloring` + Disable auto-coloring on render + When unneeded + + + `disable-coloring-char-threshold` + Max character count to auto disable coloring + To optimize performance with large code pieces - enable-language-display - Shows estimated language at top of component - When any type of code can be returned + `disable-coloring-line-threshold` + Max line count to auto disable coloring + To optimize performance with large code pieces - disable-auto-compacting + `disable-auto-compacting` Stops line-ticks from being removed when space isn't sufficient Hide ticks when compacted - disable-edit-button + `disable-edit-button` When editable is invoked, remove edit button Disable button to validate edit - disable-line-ticks + `disable-line-ticks` Remove line counts on the left side-bar of code When showing code such as console commands - disable-copy-button + `disable-copy-button` Remove copy button from top-right corner When displaying code not meant to be replicated + + `customLabels` + Define all button labels + See below + +### Custom Labels for localization ### + +Place `customLabels` with the following object to edit all button tooltip values +```js +{ + 'code-copypaste-button': 'Copy code', + 'code-copypaste-success': 'Copied!', + 'code-estimated-warning': '(estimated)', + 'code-line-descriptor': 'lines', + 'code-editing-validation': 'Save edits', + 'code-editing-cancelled': 'Revert edits' +} +``` + +### <clabs-chat-code> events + + + + + + + + + + + + + + + + + + + + + + + +
**Event listener name****Trigger condition**
`on-code-edit-change`Each edit when in editing mode
`on-code-edit-validation`Editing finalized and locked
`on-code-edit-cancellation`Last set of edits was undone
`on-coloring-auto-disabled`Rendering threshold exceeded
+ +### Usage example +```html + + +``` ### Troubleshooting diff --git a/packages/web-components/src/components/chat/components/codeElement/src/codeElement.scss b/packages/web-components/src/components/chat/components/codeElement/src/codeElement.scss index 84802322..3bf7147a 100644 --- a/packages/web-components/src/components/chat/components/codeElement/src/codeElement.scss +++ b/packages/web-components/src/components/chat/components/codeElement/src/codeElement.scss @@ -29,9 +29,14 @@ $css--plex: true !default; } .#{$clabs-prefix}--chat-code-lang { + color: $text-secondary; + font-size: 14px; font-style: italic; - padding-block-start: 16px; - padding-inline-start: 16px; + padding-block: 16px 4px; + padding-inline-start: calc( + var(--chat-code-tick-width, '0px') + var(--chat-code-inset-start, '13px') + + 2px + ); } .#{$clabs-prefix}--chat-code-color-area { @@ -53,7 +58,7 @@ $css--plex: true !default; min-block-size: 100%; padding-block: 16px; padding-inline-start: 8px; - tab-size: 3; + tab-size: 2; } .#{$clabs-prefix}--chat-code-edit-area { @@ -85,7 +90,7 @@ $css--plex: true !default; 2px ); resize: none; - tab-size: 3; + tab-size: 2; word-break: break-word; } @@ -97,7 +102,7 @@ $css--plex: true !default; .#{$clabs-prefix}--chat-code-line-tick { flex: 0 0 var(--chat-code-tick-width, '13px'); - color: $text-placeholder; + color: $text-secondary; inline-size: var(--chat-code-tick-width, '13px'); padding-inline-end: 0.5em; text-align: end; @@ -239,7 +244,7 @@ $css--plex: true !default; padding-inline-start: var(--chat-code-tick-width, '13px'); resize: none; - tab-size: 3; + tab-size: 2; word-break: break-word; } @@ -283,219 +288,401 @@ $css--plex: true !default; inset-inline-end: 0; } - .hljs { + .hljs, + .hljs-subst { color: $text-primary; } - .hljs-comment, - .hljs-quote { - color: $support-success; - } - .#{$clabs-prefix}--chat-code-white-theme { - .hljs-literal, - .hljs-built_in, - .hljs-builtin-name, .hljs-selector-tag, - .hljs-doctag { + .hljs-title, + .hljs-section, + .hljs-function { color: $purple-60; } + .hljs-literal, + .hljs-doctype, + .hljs-keyword { + color: $blue-60; + } + + .hljs-tag .hljs-name { + color: $yellow-60; + } + .hljs-string, - .hljs-attribute, - .hljs-template-variable, - .hljs-template-tag { + .hljs-meta .hljs-string { color: $green-60; } - .hljs-keyword, .hljs-number, .hljs-symbol, .hljs-bullet { - color: $blue-60; + color: $teal-60; } - .hljs-function .hljs-title, - .hljs-title, - .hljs-function { - color: $cyan-60; + .hljs-comment, + .hljs-quote { + color: $green-60; } - .hljs-operator, - .hljs-punctuation, - .hljs-subst { - color: $red-60; + .hljs-meta, + .hljs-meta .hljs-keyword { + color: $purple-60; } - .hljs-variable, - .hljs-property, - .hljs-params { - color: $magenta-60; + .hljs-built_in, + .hljs-builtin-name { + color: $cyan-60; } - .hljs-attr { - color: $orange-60; + .hljs-attr, + .hljs-attribute { + color: $teal-60; } - .hljs-class .hljs-title, - .hljs-type, - .hljs-class { - color: $teal-60; + .hljs-variable, + .hljs-template-variable, + .hljs-class .hljs-title { + color: $yellow-60; + } + + .hljs-tag { + color: $red-60; } .hljs-name, - .hljs-section, .hljs-selector-id, - .hljs-tag, - .hljs-meta, .hljs-selector-class { + color: $blue-60; + } + + .hljs-attribute { + color: $teal-60; + } + + .hljs-template-tag, + .hljs-template-variable { color: $yellow-60; } + .hljs-selector-tag { + color: $blue-60; + } + + .hljs-selector-id { + color: $purple-60; + } + + .hljs-selector-class { + color: $blue-60; + } + + .hljs-selector-pseudo { + color: $cyan-60; + } + + .hljs-selector-attr { + color: $green-60; + } + + .hljs-decorator { + color: $magenta-60; + } + + .hljs-bullet { + color: $orange-60; + } + + .hljs-link { + color: $blue-60; + } + .hljs-emphasis { - font-style: italic; + color: $gray-60; } .hljs-strong { - font-weight: bold; + color: $gray-60; + } + + .hljs-error { + color: $red-60; + } + + .hljs-addition { + color: $green-60; + } + + .hljs-params { + color: $yellow-60; + } + + .hljs-subst { + color: $gray-60; } } .#{$clabs-prefix}--chat-code-default-theme { - .hljs-literal, - .hljs-built_in, - .hljs-builtin-name, .hljs-selector-tag, - .hljs-doctag { + .hljs-title, + .hljs-section, + .hljs-function { color: $purple-50; } + .hljs-literal, + .hljs-doctype, + .hljs-keyword { + color: $blue-50; + } + + .hljs-tag .hljs-name { + color: $yellow-50; + } + .hljs-string, - .hljs-attribute, - .hljs-template-variable, - .hljs-template-tag { + .hljs-meta .hljs-string { color: $green-50; } - .hljs-keyword, .hljs-number, .hljs-symbol, .hljs-bullet { - color: $blue-50; + color: $teal-50; } - .hljs-function .hljs-title, - .hljs-title, - .hljs-function { - color: $cyan-50; + .hljs-comment, + .hljs-quote { + color: $green-50; } - .hljs-operator, - .hljs-punctuation, - .hljs-subst { - color: $red-50; + .hljs-meta, + .hljs-meta .hljs-keyword { + color: $purple-50; } - .hljs-variable, - .hljs-property, - .hljs-params { - color: $magenta-50; + .hljs-built_in, + .hljs-builtin-name { + color: $cyan-50; } - .hljs-attr { - color: $orange-50; + .hljs-attr, + .hljs-attribute { + color: $teal-50; } - .hljs-class .hljs-title, - .hljs-type, - .hljs-class { - color: $teal-50; + .hljs-variable, + .hljs-template-variable, + .hljs-class .hljs-title { + color: $yellow-50; + } + + .hljs-tag { + color: $red-50; } .hljs-name, - .hljs-section, .hljs-selector-id, - .hljs-tag, - .hljs-meta, .hljs-selector-class { + color: $blue-50; + } + + .hljs-attribute { + color: $teal-50; + } + + .hljs-template-tag, + .hljs-template-variable { color: $yellow-50; } + .hljs-selector-tag { + color: $blue-50; + } + + .hljs-selector-id { + color: $purple-50; + } + + .hljs-selector-class { + color: $blue-50; + } + + .hljs-selector-pseudo { + color: $cyan-50; + } + + .hljs-selector-attr { + color: $green-50; + } + + .hljs-decorator { + color: $magenta-50; + } + + .hljs-bullet { + color: $orange-50; + } + + .hljs-link { + color: $blue-50; + } + .hljs-emphasis { - font-style: italic; + color: $gray-50; } .hljs-strong { - font-weight: bold; + color: $gray-50; + } + + .hljs-error { + color: $red-50; + } + + .hljs-addition { + color: $green-50; + } + + .hljs-params { + color: $yellow-50; + } + + .hljs-subst { + color: $gray-50; } } .#{$clabs-prefix}--chat-code-g100-theme { - .hljs-literal, - .hljs-built_in, - .hljs-builtin-name, .hljs-selector-tag, - .hljs-doctag { + .hljs-title, + .hljs-section, + .hljs-literal, + .hljs-function { color: $purple-40; } + .hljs-doctype, + .hljs-keyword { + color: $blue-40; + } + + .hljs-tag .hljs-name { + color: $yellow-40; + } + .hljs-string, - .hljs-attribute, - .hljs-template-variable, - .hljs-template-tag { + .hljs-meta .hljs-string { color: $green-40; } - .hljs-keyword, .hljs-number, .hljs-symbol, .hljs-bullet { - color: $blue-40; + color: $teal-40; } - .hljs-function .hljs-title, - .hljs-title, - .hljs-function { - color: $cyan-40; + .hljs-comment, + .hljs-quote { + color: $green-40; } - .hljs-operator, - .hljs-punctuation, - .hljs-subst { - color: $red-40; + .hljs-meta, + .hljs-meta .hljs-keyword { + color: $purple-40; } - .hljs-variable, - .hljs-property, - .hljs-params { - color: $magenta-40; + .hljs-built_in, + .hljs-builtin-name { + color: $cyan-40; } - .hljs-attr { - color: $orange-40; + .hljs-attr, + .hljs-attribute { + color: $teal-40; } - .hljs-class .hljs-title, - .hljs-type, - .hljs-class { - color: $teal-40; + .hljs-variable, + .hljs-template-variable, + .hljs-class .hljs-title { + color: $yellow-40; + } + + .hljs-tag { + color: $red-40; } .hljs-name, - .hljs-section, .hljs-selector-id, - .hljs-tag, - .hljs-meta, .hljs-selector-class { + color: $blue-40; + } + + .hljs-attribute { + color: $teal-40; + } + + .hljs-template-tag, + .hljs-template-variable { color: $yellow-40; } + .hljs-selector-tag { + color: $blue-40; + } + + .hljs-selector-id { + color: $purple-40; + } + + .hljs-selector-class { + color: $blue-40; + } + + .hljs-selector-pseudo { + color: $cyan-40; + } + + .hljs-selector-attr { + color: $green-40; + } + + .hljs-decorator { + color: $magenta-40; + } + + .hljs-bullet { + color: $orange-40; + } + + .hljs-link { + color: $blue-40; + } + .hljs-emphasis { - font-style: italic; + color: $gray-40; } .hljs-strong { - font-weight: bold; + color: $gray-40; + } + + .hljs-error { + color: $red-40; + } + + .hljs-addition { + color: $green-40; + } + + .hljs-params { + color: $yellow-40; + } + + .hljs-subst { + color: $gray-40; } } } diff --git a/packages/web-components/src/components/chat/components/codeElement/src/codeElement.template.ts b/packages/web-components/src/components/chat/components/codeElement/src/codeElement.template.ts index ff7d5e15..8ea087e1 100644 --- a/packages/web-components/src/components/chat/components/codeElement/src/codeElement.template.ts +++ b/packages/web-components/src/components/chat/components/codeElement/src/codeElement.template.ts @@ -30,7 +30,6 @@ import Undo16 from '@carbon/web-components/es/icons/undo/16.js'; export function codeElementTemplate(customElementClass) { const { _renderedLines, - content, disableLineTicks, disableCopyButton, disableEditButton, @@ -39,10 +38,15 @@ export function codeElementTemplate(customElementClass) { _handleEditValidation: handleEditValidation, _handleEditCancellation: handleEditCancellation, editable, + assignedLanguage, + autoAssignLanguage, + _editedContent: editedContent, _currentlyEdited: currentlyEdited, _highlightLine: highlightLine, enableColoring, language, + lineCount, + displayLineCount, enableLanguageDisplay, _renderLabel: renderLabel, _handleScroll: handleScroll, @@ -52,9 +56,26 @@ export function codeElementTemplate(customElementClass) { } = customElementClass; return html`
- ${enableLanguageDisplay - ? html`
${language}
` + ${enableLanguageDisplay || displayLineCount + ? html`
+ ${enableLanguageDisplay + ? html` + ${assignedLanguage + ? assignedLanguage + : autoAssignLanguage + ? language + ' ' + renderLabel('code-estimated-warning') + : 'no language detected'} + ` + : ``} + ${displayLineCount && lineCount > 1 + ? (enableLanguageDisplay ? ', ' : '') + + lineCount + + ' ' + + renderLabel('code-line-descriptor') + : ''} +
` : ``} +
${!disableEditButton @@ -77,6 +98,7 @@ export function codeElementTemplate(customElementClass) { tooltip-alignment="end" tooltip-position="bottom" align="left" + size="sm" feedback="${renderLabel('code-copypaste-success')}" feedback-timeout="2000"> ${renderLabel('code-copypaste-button')} @@ -99,7 +121,7 @@ export function codeElementTemplate(customElementClass) { class="${clabsPrefix}--chat-code-edit-area ${!editable ? clabsPrefix + '--chat-code-edit-hidden' : ''}"> -${content}
${Undo16({ slot: 'icon' })} - Undo edit + ${renderLabel('code-editing-cancelled')} ${Checkmark16({ slot: 'icon' })} - Apply edit + ${renderLabel('code-editing-validation')}
` : html``} diff --git a/packages/web-components/src/components/chat/components/codeElement/src/codeElement.ts b/packages/web-components/src/components/chat/components/codeElement/src/codeElement.ts index 34529483..b14de854 100644 --- a/packages/web-components/src/components/chat/components/codeElement/src/codeElement.ts +++ b/packages/web-components/src/components/chat/components/codeElement/src/codeElement.ts @@ -37,6 +37,18 @@ export default class codeElement extends LitElement { @property({ type: Boolean, attribute: 'editable', reflect: true }) editable; + /** + * character count render limit for coloring performance + */ + @property({ type: Number, attribute: 'disable-coloring-char-threshold' }) + coloringCharacterThreshold; + + /** + * line count render limit for coloring performance + */ + @property({ type: Number, attribute: 'disable-coloring-line-threshold' }) + coloringLineThreshold; + /** * add coloring with highlightJS */ @@ -62,16 +74,34 @@ export default class codeElement extends LitElement { maxHeight; /** - * Set max height for code piece + * Set lang name */ - @property({ type: String, attribute: 'lang' }) + @property({ type: String, attribute: 'displayed-language' }) assignedLanguage; + /** + * Set render language + */ + @property({ type: String, attribute: 'render-language' }) + renderLanguage; + + /** + * Show language guessed by hljs + */ + @property({ type: Boolean, attribute: 'enable-estimated-language' }) + autoAssignLanguage; + + /** + * Show line count + */ + @property({ type: Boolean, attribute: 'display-line-count' }) + displayLineCount; + /** * Set tab size flag int */ @property({ type: Number, attribute: 'tab-size' }) - tabSize = 3; + tabSize = 2; /** * Editable boolean flag to let users know lines can be changed @@ -94,8 +124,8 @@ export default class codeElement extends LitElement { /** * Editable boolean flag to let users know lines can be changed */ - @property({ type: Boolean, attribute: 'disable-auto-compacting' }) - disableAutoCompacting; + @property({ type: Boolean, attribute: 'enable-auto-compacting' }) + enableAutoCompacting = true; /** * Source content - save original code text content @@ -151,6 +181,12 @@ export default class codeElement extends LitElement { @state() _preRender = true; + /** + * line count + */ + @state() + lineCount; + /** * Array of lines parsed from content attribute */ @@ -202,6 +238,7 @@ export default class codeElement extends LitElement { if (!this._originalContent) { this._originalContent = this.content; } + if (this.streaming) { this._formatCode(false); } else { @@ -243,6 +280,11 @@ export default class codeElement extends LitElement { this.style.setProperty('--chat-code-tick-offset', '0px'); this.style.setProperty('--chat-code-inset-start', '14px'); } + + if (this.enableLanguageDisplay || this.displayLineCount) { + this.style.setProperty('--chat-code-info-offset', '46px'); + } + if (this.content !== undefined) { const codeAnalysis = this._clearCode(this.content); if (codeAnalysis.language) { @@ -262,12 +304,11 @@ export default class codeElement extends LitElement { ]; } //if (!this.disableAutoCompacting) { - this.resizeObserver = new ResizeObserver(async () => { - this._handleScroll(); + this.resizeObserver = new ResizeObserver(async (_event) => { + this._handleResize(_event); }); this.resizeObserver.observe(this); - //} } /** _handleScroll @@ -285,11 +326,11 @@ export default class codeElement extends LitElement { this.editable ) { editArea.scrollTop = textArea.scrollTop; - setTimeout(() => { + /*setTimeout(() => { if (Math.abs(textArea.scrollHeight - editArea.scrollHeight) > 10) { this._formatCode(true); } - }, 100); + }, 100);*/ } } @@ -298,8 +339,10 @@ export default class codeElement extends LitElement { * @param {event} _event - resize event */ _handleResize(_event) { - if (!this.disableLineTicks) { - this.disableLineTicks = this.clientWidth < 300; + if (this.enableAutoCompacting) { + if (this.clientWidth < 300) { + this.disableLineTicks = true; + } } this._handleScroll(); } @@ -469,13 +512,16 @@ export default class codeElement extends LitElement { }); this.dispatchEvent(codeEditedEvent); this._currentlyEdited = false; - this.requestUpdate(); + this._formatCode(false); } /** * _handleCancellation - button event when user aborts edit of code */ _handleEditCancellation() { + //this._editedContent = this.content; + //this.content=this._originalContent + this._editedContent = this.content; this._editedContent = this._originalContent; this._currentlyEdited = false; @@ -490,6 +536,7 @@ export default class codeElement extends LitElement { }); this.dispatchEvent(codeEditedEvent); this._formatCode(false); + this._handleScroll(); } /** _highlightLine - run code coloring system @@ -508,13 +555,16 @@ export default class codeElement extends LitElement { const formattedText = edited ? this._editedContent : this.content; const htmlSafeText = formattedText.replace(/```/g, ''); - try { - if (!this.language) { - const detection = hljs.highlightAuto(htmlSafeText); - this.language = detection.language; + if (this.coloringCharacterThreshold) { + if (formattedText.length > this.coloringCharacterThreshold) { + this.disableColoring = true; } - } catch (e) { - this.language = 'javascript'; + } + + const tabConversion = ' '; + let tabHTML = ''; + if (this.tabSize) { + tabHTML = tabConversion.repeat(this.tabSize); } const lines = htmlSafeText.trim().split('\n'); @@ -526,6 +576,25 @@ export default class codeElement extends LitElement { paddingLeft: string; }[] = []; + this.lineCount = lines.length; + + if (!this.disableColoring) { + try { + if (!this.language) { + const detection = hljs.highlightAuto(htmlSafeText); + this.language = detection.language; + } + } catch (e) { + this.language = 'javascript'; + } + } + + if (this.coloringLineThreshold) { + if (lines.length > this.coloringLineThreshold) { + this.disableColoring = true; + } + } + const highlightMode = !this.disableColoring; if (highlightMode) { const highlightedCode = hljs.highlightAuto(htmlSafeText).value; @@ -540,9 +609,7 @@ export default class codeElement extends LitElement { if (lines) { for (let k = 0; k < lines.length; k++) { if (k > 0) { - codeLines.push( - currentLine.replace(/\t/g, '   ') - ); + codeLines.push(currentLine.replace(/\t/g, tabHTML)); currentLine = ''; } currentLine += lines[k]; @@ -555,7 +622,7 @@ export default class codeElement extends LitElement { } if (currentLine) { - codeLines.push(currentLine.replace(/\t/g, '   ')); + codeLines.push(currentLine.replace(/\t/g, tabHTML)); } textValues = codeLines.map((line) => ({ content: line, @@ -565,7 +632,7 @@ export default class codeElement extends LitElement { } else { for (let i = 0; i < lines.length; i++) { textValues.push({ - content: lines[i].replace(/\t/g, '   '), + content: lines[i].replace(/\t/g, tabHTML), type: '', paddingLeft: '0px', }); @@ -599,7 +666,19 @@ export default class codeElement extends LitElement { customValue = labels[key] || 'Copy code'; break; case 'code-copypaste-success': - customValue = labels[key] || 'Copieddddd!'; + customValue = labels[key] || 'Copied!'; + break; + case 'code-estimated-warning': + customValue = labels[key] || '(estimated)'; + break; + case 'code-editing-validation': + customValue = labels[key] || 'Save edits'; + break; + case 'code-editing-cancelled': + customValue = labels[key] || 'Revert edits'; + break; + case 'code-line-descriptor': + customValue = labels[key] || 'lines'; break; } } diff --git a/packages/web-components/src/components/chat/components/footer/src/footer.ts b/packages/web-components/src/components/chat/components/footer/src/footer.ts index 942009e6..e992b261 100644 --- a/packages/web-components/src/components/chat/components/footer/src/footer.ts +++ b/packages/web-components/src/components/chat/components/footer/src/footer.ts @@ -97,6 +97,12 @@ export default class footer extends LitElement { @state() hideContextMessage = false; + /** + * focus-prompt set focus targeting + */ + @property({ type: Boolean, attribute: 'focus-prompt' }) + _focusPrompt; + /** * add context meesage above prompt */ @@ -210,6 +216,18 @@ export default class footer extends LitElement { this._checkLimit(); } } + + if (changedProperties.has('_focusPrompt')) { + if (this._focusPrompt) { + const textArea = this.shadowRoot?.querySelector( + '.' + clabsPrefix + '--chat-search-query' + ); + if (textArea instanceof HTMLElement) { + textArea.focus(); + this._isPromptFocused = true; + } + } + } if (changedProperties.has('_fullscreenMode')) { this._checkSize(); } diff --git a/packages/web-components/src/components/chat/components/formulaElement/src/formulaElement.ts b/packages/web-components/src/components/chat/components/formulaElement/src/formulaElement.ts index 5dc8f186..5cbe22f1 100644 --- a/packages/web-components/src/components/chat/components/formulaElement/src/formulaElement.ts +++ b/packages/web-components/src/components/chat/components/formulaElement/src/formulaElement.ts @@ -33,6 +33,36 @@ export default class formulaElement extends LitElement { @state() formula; + /** + * startMathJax - edit target document for rendering + */ + async startMathJax() { + // @ts-ignore + /** + * getComponents + */ + // @ts-ignore + MathJax.startup.getComponents = () => { + // @ts-ignore + MathJax.startup.document = MathJax.startup.document.constructor({ + // @ts-ignore + options: MathJax.config.options, + // @ts-ignore + renderActions: MathJax.startup.renderActions, + // @ts-ignore + inputJax: MathJax.startup.input, + // @ts-ignore + outputJax: MathJax.startup.output, + // @ts-ignore + adaptor: MathJax.startup.adaptor, + }); + // @ts-ignore + MathJax.startup.document.document = this.shadowRoot; + }; + // @ts-ignore + await MathJax.startup.promise; + } + /** detect when component is rendered to process visualization specification object */ firstUpdated() { @@ -62,6 +92,7 @@ export default class formulaElement extends LitElement { * Prepare table object for rendering from content string */ async _renderFormula() { + await this.startMathJax(); const targetDiv = this.shadowRoot?.querySelector( '.' + clabsPrefix + '--chat-formula-container' ); diff --git a/packages/web-components/src/components/chat/components/message/src/message.ts b/packages/web-components/src/components/chat/components/message/src/message.ts index 417c5de6..e2f2f80b 100644 --- a/packages/web-components/src/components/chat/components/message/src/message.ts +++ b/packages/web-components/src/components/chat/components/message/src/message.ts @@ -1514,9 +1514,13 @@ export default class message extends LitElement { const messageDetails = this._prepareEventDetail(); if (this.positiveFeedbackSelected) { messageDetails['action'] = 'message: user gave feedback to response'; + + messageDetails['feedbackRetracted'] = false; this._focusOnPopup(); } else { messageDetails['action'] = 'message: user removed feedback to response'; + + messageDetails['feedbackRetracted'] = true; } messageDetails['type'] = 'positive'; messageDetails['rawTextMessage'] = this.rawText; @@ -1546,9 +1550,11 @@ export default class message extends LitElement { const messageDetails = this._prepareEventDetail(); if (this.negativeFeedbackSelected) { messageDetails['action'] = 'message: user gave feedback to response'; + messageDetails['feedbackRetracted'] = false; this._focusOnPopup(); } else { messageDetails['action'] = 'message: user removed feedback to response'; + messageDetails['feedbackRetracted'] = true; } messageDetails['type'] = 'negative'; messageDetails['rawTextMessage'] = this.rawText; diff --git a/packages/web-components/src/components/chat/components/messages/src/messages.ts b/packages/web-components/src/components/chat/components/messages/src/messages.ts index efbfd3dd..e672447d 100644 --- a/packages/web-components/src/components/chat/components/messages/src/messages.ts +++ b/packages/web-components/src/components/chat/components/messages/src/messages.ts @@ -36,6 +36,12 @@ export default class messages extends LitElement { @property({ type: Boolean, attribute: 'loading', reflect: true }) loading; + /** + * force scroll down on new messages no matter what + */ + @property({ type: Boolean, attribute: 'force-scroll-down' }) + forceScrollDown; + /** * user-assigned boolean denoting when text content is streamed in token by token */ @@ -196,7 +202,11 @@ export default class messages extends LitElement { } if (changedProperties.has('_computedMessages')) { - this._scrollMessage(); + if (!this.forceScrollDown) { + this._scrollMessage(); + } else { + this._scrollToBottom(); + } } if (changedProperties.has('loading')) { diff --git a/packages/web-components/src/components/chat/components/popupElement/src/popupElement.scss b/packages/web-components/src/components/chat/components/popupElement/src/popupElement.scss index 1e86f910..b97ab0d4 100644 --- a/packages/web-components/src/components/chat/components/popupElement/src/popupElement.scss +++ b/packages/web-components/src/components/chat/components/popupElement/src/popupElement.scss @@ -126,7 +126,7 @@ } .#{$clabs-prefix}--chat-popup-title { - padding: 0.5rem; + padding: 1rem; color: $text-primary; font-size: 18px; font-weight: 400; @@ -142,19 +142,19 @@ font-weight: 400; letter-spacing: 0.32px; line-height: 16px; - padding-inline: 0.5rem; + padding-inline: 1rem; text-align: start; } .#{$clabs-prefix}--chat-popup-description { font-size: 14px; - margin-block-start: 0.5rem; - padding-inline: 0.5rem; + margin-block-start: 1rem; + padding-inline: 1rem; text-align: start; } .#{$clabs-prefix}--chat-popup-model-title { font-size: 14px; padding-block: 6px; - padding-inline: 0.5rem; + padding-inline: 1rem; text-align: start; } @@ -162,8 +162,8 @@ overflow: hidden; box-sizing: border-box; max-inline-size: 100%; - padding-block: 0.5rem 8px; - padding-inline: 0.5rem; + padding-block: 1rem 8px; + padding-inline: 1rem; } .#{$clabs-prefix}--chat-popup-feedback-text { @@ -173,7 +173,7 @@ inline-size: 100%; inset-block-start: 205.54px; inset-inline-start: 24px; - padding-inline: 0.5rem; + padding-inline: 1rem; } .#{$clabs-prefix}--chat-popup-feedback-text-area { overflow: hidden; @@ -188,7 +188,7 @@ letter-spacing: 0.32px; line-height: 16px; margin-block: 0.5rem 1.5rem; - padding-inline: 0.5rem; + padding-inline: 1rem; text-align: start; } @@ -203,7 +203,7 @@ .#{$clabs-prefix}--chat-popup-checkbox { margin-inline-start: -2px; padding-block-end: 0.5rem; - padding-inline: 0.5rem; + padding-inline: 1rem; } .#{$clabs-prefix}--chat-popup-submit { diff --git a/packages/web-components/src/components/chat/components/popupElement/src/popupElement.template.ts b/packages/web-components/src/components/chat/components/popupElement/src/popupElement.template.ts index 2eff358d..dc81d688 100644 --- a/packages/web-components/src/components/chat/components/popupElement/src/popupElement.template.ts +++ b/packages/web-components/src/components/chat/components/popupElement/src/popupElement.template.ts @@ -128,6 +128,7 @@ export function popupElementTemplate(customElementClass) {