diff --git a/example/dev.js b/example/dev.js index 203694db..40b4a48b 100644 --- a/example/dev.js +++ b/example/dev.js @@ -1,2 +1,11 @@ -import open from 'open'; -await open('./dist/index.html'); +const openFile = async () => { + try { + // Use dynamic import to load the ES module + const open = (await import('open')).default; + await open('./dist/index.html'); + } catch (err) { + console.error('Error opening file:', err); + } +}; + +openFile(); diff --git a/package.json b/package.json index b2c7036c..c4d917b4 100644 --- a/package.json +++ b/package.json @@ -16,17 +16,20 @@ "clean:dist": "find . -name 'dist' -type d -prune -exec rm -rf '{}' +", "clean:node": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +", "build": "webpack --config webpack.config.js --mode production", - "packdemo": "cd ./example && npm run pack", + "packdemo": "npm run --prefix ./example pack", "watch": "webpack --config webpack.config.js --mode development --watch", - "watch:example": "cd ./example && npm run watch", - "start:example": "cd ./example && npm install && npm run build && npm run dev", + "watch:example": "npm run --prefix ./example watch", + "start:example": "npm install --prefix ./example && npm run build --prefix ./example", + "watch:web": "run-p watch watch:example serve:example", + "serve:example": "live-server --port=9000 example/dist", "dev": "npm run clean && npm install && npm run build && npm run start:example", - "lint-fix": "npx eslint \"./**\" --fix", - "lint": "npx eslint \"./**\"", + "dev:watch": "npm run dev && npm run watch:web", + "lint-fix": "npx eslint './**' --fix", + "lint": "npx eslint './**'", "format:check": "npx prettier --check .", "format:write": "npx prettier --write .", "tests:unit": "jest", - "tests:e2e": "cd ./ui-tests && npm install && npm run prepare && npm run e2e", + "tests:e2e": "npm install --prefix ./ui-tests && npm run --prefix ./ui-tests prepare && npm run --prefix ./ui-tests e2e", "api-docs": "npx typedoc src/main.ts --out ./api-docs", "api-doc-deploy": "npx typedoc src/main.ts --out ./example/dist/api-doc", "postinstall": "node postinstall.js", diff --git a/src/components/chat-item/prompt-input/prompt-text-input.ts b/src/components/chat-item/prompt-input/prompt-text-input.ts index d6ae9005..8f8dad60 100644 --- a/src/components/chat-item/prompt-input/prompt-text-input.ts +++ b/src/components/chat-item/prompt-input/prompt-text-input.ts @@ -58,6 +58,7 @@ export class PromptTextInput { maxlength: MAX_USER_INPUT().toString(), type: 'text', placeholder: MynahUITabsStore.getInstance().getTabDataStore(this.props.tabId).getValue('promptInputPlaceholder'), + style: this.getPlaceholderWidth(), value: '', ...(Config.getInstance().config.autoFocus ? { autofocus: 'autofocus' } : {}) }, @@ -87,7 +88,6 @@ export class PromptTextInput { } }, focus: () => { - this.render.addClass('input-has-focus'); if (typeof this.props.onFocus !== 'undefined') { this.props.onFocus(); } @@ -142,6 +142,27 @@ export class PromptTextInput { this.clear(); } + private readonly getPlaceholderWidth = (): string => { + const span = document.createElement('span'); + span.style.visibility = 'hidden'; + span.setAttribute('class', 'mynah-chat-prompt-input-scroll-measure'); + document.body.appendChild(span); + + const inputStyles = window.getComputedStyle(span); + span.style.font = inputStyles.getPropertyValue('font'); + span.style.fontSize = inputStyles.getPropertyValue('font-size'); + span.style.fontFamily = inputStyles.getPropertyValue('font-family'); + span.style.fontWeight = inputStyles.getPropertyValue('font-weight'); + span.style.letterSpacing = inputStyles.getPropertyValue('letter-spacing'); + span.style.whiteSpace = 'nowrap'; + span.textContent = MynahUITabsStore.getInstance().getTabDataStore(this.props.tabId).getValue('promptInputPlaceholder'); + + const textWidth = span.offsetWidth; + document.body.removeChild(span); + + return `--mynah-placeholder-width: ${textWidth * 1.125}px`; // + }; + private readonly updatePromptTextInputSizer = (placeHolder?: { index?: number; text?: string; diff --git a/src/styles/components/chat/_chat-prompt-wrapper.scss b/src/styles/components/chat/_chat-prompt-wrapper.scss index fd190dcd..2ba36d43 100644 --- a/src/styles/components/chat/_chat-prompt-wrapper.scss +++ b/src/styles/components/chat/_chat-prompt-wrapper.scss @@ -55,9 +55,19 @@ width: 100%; position: relative; align-self: center; - overflow: hidden; + overflow-x: scroll; - > .mynah-chat-prompt-input { + &.no-text { + &::-webkit-scrollbar { + display: none; + } + -ms-overflow-style: none; + scrollbar-width: none; + } + + > .mynah-chat-prompt-input, + > .mynah-chat-prompt-input-scroll-measure { + --mynah-placeholder-width: 100%; font-family: var(--mynah-font-family); border: none; resize: none; @@ -74,6 +84,7 @@ overflow-wrap: break-word; &:placeholder-shown { + width: var(--mynah-placeholder-width) !important; text-overflow: ellipsis; } @@ -106,6 +117,7 @@ color: rgba(0, 0, 0, 0); position: relative; z-index: 150; + > span.context { position: relative; color: var(--mynah-color-button-reverse); @@ -268,6 +280,17 @@ border: var(--mynah-border-width) solid var(--mynah-color-button); } + > .mynah-chat-prompt-chars-indicator { + width: 100%; + font-size: var(--mynah-font-size-xsmall); + padding-top: var(--mynah-sizing-2); + opacity: 0.5; + display: flex; + align-items: center; + justify-content: flex-end; + font-style: italic; + } + & + .mynah-chat-prompt-input-info { padding-top: 0; margin-top: calc(-1 * var(--mynah-sizing-2)); @@ -292,11 +315,9 @@ > .mynah-chat-prompt-input-info { display: flex; flex-flow: row nowrap; - justify-content: center; box-sizing: border-box; overflow: hidden; padding: var(--mynah-sizing-4); - text-align: center; &, & * { @@ -313,7 +334,5 @@ margin-block-end: 0; margin-top: 0; margin-bottom: 0; - max-width: 100%; - box-sizing: border-box; } } diff --git a/ui-tests/__test__/__image_snapshots__/webkit/main-spec-ts-open-mynah-ui-should-open-a-new-the-tab-2-snap.png b/ui-tests/__test__/__image_snapshots__/webkit/main-spec-ts-open-mynah-ui-should-open-a-new-the-tab-2-snap.png new file mode 100644 index 00000000..115770a6 Binary files /dev/null and b/ui-tests/__test__/__image_snapshots__/webkit/main-spec-ts-open-mynah-ui-should-open-a-new-the-tab-2-snap.png differ diff --git a/ui-tests/__test__/main.spec.ts b/ui-tests/__test__/main.spec.ts index 82e56ea3..1e777753 100644 --- a/ui-tests/__test__/main.spec.ts +++ b/ui-tests/__test__/main.spec.ts @@ -20,7 +20,7 @@ describe('Open MynahUI', () => { allowSizeMismatch: true, failureThresholdType: 'percent', storeReceivedOnFailure: true, - customSnapshotsDir: `./__test__/__image_snapshots__/${browserName}` + customSnapshotsDir: `./__test__/__image_snapshots__/${String(browserName)}` }); expect.extend({ toMatchImageSnapshot }); diff --git a/ui-tests/src/main.ts b/ui-tests/src/main.ts index ac3d0581..5ffa0980 100644 --- a/ui-tests/src/main.ts +++ b/ui-tests/src/main.ts @@ -74,7 +74,7 @@ export const createMynahUI = (): MynahUI => { onChatPrompt: (tabId: string, prompt: ChatPrompt) => { if (tabId === 'tab-1') { mynahUI.updateStore(tabId, { - tabCloseConfirmationMessage: `Working on "${prompt.prompt != null ? (prompt.prompt) : ''}"`, + tabCloseConfirmationMessage: `Working on "${prompt.prompt != null ? String(prompt.prompt) : ''}"`, }); } onChatPrompt(tabId, prompt); @@ -142,7 +142,7 @@ export const createMynahUI = (): MynahUI => { mynahUI.addChatItem(tabId, { type: ChatItemType.PROMPT, messageId: generateUID(), - body: `**${(prompt.command != null ? prompt.command : '').replace('/', '')}**\n${(prompt.escapedPrompt != null ? prompt.escapedPrompt : '')}`, + body: `**${String(prompt.command != null ? prompt.command.replace('/', '') : '')}**\n${String(prompt.escapedPrompt != null ? prompt.escapedPrompt : '')}`, }); getGenerativeAIAnswer(tabId); break;