diff --git a/.vscode/launch.json b/.vscode/launch.json index 00ad0b4271..79f808f1f5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -54,7 +54,8 @@ "MODEL_VERSION": "0.6.287", // "VERSION": "0.6.289", "ELASTIC_INDEX_NAME": "local_storage_index", - "UPLOAD_URL": "/files", + "UPLOAD_URL": "/files", + "AI_BOT_URL": "http://localhost:4010" }, "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], "runtimeVersion": "20", diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 19160c3415..8ddf002b86 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -257,6 +257,7 @@ services: - ELASTIC_INDEX_NAME=local_storage_index - BRANDING_PATH=/var/cfg/branding.json - SUPPORT_WORKSPACE=support + - AI_BOT_URL=http://host.docker.internal:4010 restart: unless-stopped transactor_pg: image: hardcoreeng/transactor @@ -384,6 +385,7 @@ services: - AVATAR_PATH=./avatar.png - AVATAR_CONTENT_TYPE=.png - STATS_URL=http://host.docker.internal:4900 +# - OPENAI_API_KEY=token deploy: resources: limits: diff --git a/dev/import-tool/README.md b/dev/import-tool/README.md index 75088e2ad0..b73bfe9ae1 100644 --- a/dev/import-tool/README.md +++ b/dev/import-tool/README.md @@ -1,80 +1,5 @@ -## How to Import Documents from Notion +### Supported Import Options -To import Notion documents: +1. **Notion**: see [Import from Notion Guide](./notion/README.md) +2. **ClickUp**: see [Import from ClickUp Guide](./clickup/README.md) -1. Export documents from Notion as *Markdown & CSV* -2. Extract the exported archive -3. 1. If you want your docs to be imported along with Notion teamspaces, use this command: - - -``` -rushx run-local import-notion-with-teamspaces ${dir} \ ---user ${user} \ ---password ${password} \ ---workspace ${workspace} -``` - -3. 2. If you want the docs to be imported to a new teamspace, use this command: - -``` -rushx run-local import-notion-to-teamspace {dir} \ ---user ${user} \ ---password ${password} \ ---workspace ${workspace} \ ---teamspace ${teamspace} -``` - -* *dir* - path to the root of the extracted archive -* *user* - your username or email -* *password* - password -* *workspace* - workspace url where the documents should be imported to -* *teamspace* - teamspace to be created for newly imported docs - - -### Examples - -#### For local run: -When importing Notion workspace with teamspaces -``` -rushx run-local import-notion-with-teamspaces /home/john/extracted-notion-docs \ ---user john.doe@gmail.com \ ---password qwe1234 \ ---workspace ws1 -``` -When importing Notion workspace without teamspaces or a page with subpages -``` -rushx run-local import-notion-to-teamspace /home/john/extracted-notion-docs \ ---user john.doe@gmail.com \ ---password qwe1234 \ ---workspace ws1 \ ---teamspace imported -``` - -#### For cloud deployment: -##### Here is an example for those who's using huly.app cloud: -1. Go to the root folder of the extracted archive with exported data. -2. Run import command as follow: - -* To import Notion workspace with teamspaces -``` -docker run \ - -e FRONT_URL="https://huly.app" \ - -v $(pwd):/data \ - hardcoreeng/import-tool:latest \ - -- bundle.js import-notion-with-teamspaces /data \ ---user jane.doe@gmail.com \ ---password 4321qwe \ ---workspace ws1 -``` -* To import Notion workspace without teamspaces or a page with subpages. -``` -docker run \ - -e FRONT_URL="https://huly.app" \ - -v $(pwd):/data \ - hardcoreeng/import-tool:latest \ - -- bundle.js import-notion-to-teamspace /data \ ---user jane.doe@gmail.com \ ---password 4321qwe \ ---workspace ws1 \ ---teamspace notion -``` \ No newline at end of file diff --git a/dev/import-tool/src/clickup/README.md b/dev/import-tool/src/clickup/README.md new file mode 100644 index 0000000000..d2e28a7c6e --- /dev/null +++ b/dev/import-tool/src/clickup/README.md @@ -0,0 +1,32 @@ +## Import from ClickUp Guide + +### Export Data from ClickUp + +1. Follow [ClickUp's official guide](https://help.clickup.com/hc/en-us/articles/6310551109527-Task-data-export) to export your tasks as CSV +2. Save the CSV file to your local machine + +### Run Import Tool +1. Place your ClickUp CSV file in a directory (e.g., /path/to/export) +2. Run the import tool using Docker: + +``` +docker run \ + -e FRONT_URL="https://huly.app" \ + -v /path/to/export:/data \ + hardcoreeng/import-tool:latest \ + -- bundle.js import-clickup-tasks /data/tasks.csv \ + --user your.email@company.com \ + --password yourpassword \ + --workspace workspace-id +``` + +#### User Mapping +* Users must be created in the platform before import +* ClickUp assignees are mapped to platform users by full name (e.g., "Jane Doe") +* If user is not found: + * Task will be imported without assignee + * Original assignee name will be added as a comment: *ClickUp assignee: John Smith* + +#### Limitations +* Checklist items are imported as unchecked since ClickUp export doesn't include checkbox states +* Failed attachment downloads are skipped with warning messages (Original attachment URL is added as a comment) diff --git a/dev/import-tool/src/clickup.ts b/dev/import-tool/src/clickup/clickup.ts similarity index 98% rename from dev/import-tool/src/clickup.ts rename to dev/import-tool/src/clickup/clickup.ts index 6fe978d06d..191d71e964 100644 --- a/dev/import-tool/src/clickup.ts +++ b/dev/import-tool/src/clickup/clickup.ts @@ -16,7 +16,7 @@ import contact, { type Person, type PersonAccount } from '@hcengineering/contact import { type Ref, type Timestamp, type TxOperations } from '@hcengineering/core' import { MarkupNodeType, traverseNode, type MarkupNode } from '@hcengineering/text' import csv from 'csvtojson' -import { download } from './importer/dowloader' +import { download } from '../importer/dowloader' import { WorkspaceImporter, type ImportComment, @@ -24,8 +24,8 @@ import { type ImportProject, type ImportProjectType, type MarkdownPreprocessor -} from './importer/importer' -import { type FileUploader } from './importer/uploader' +} from '../importer/importer' +import { type FileUploader } from '../importer/uploader' interface ClickupTask { 'Task ID': string diff --git a/dev/import-tool/src/index.ts b/dev/import-tool/src/index.ts index 23a670f213..cf8ab5f456 100644 --- a/dev/import-tool/src/index.ts +++ b/dev/import-tool/src/index.ts @@ -20,10 +20,10 @@ import serverClientPlugin, { selectWorkspace } from '@hcengineering/server-client' import { program } from 'commander' -import { importNotion } from './notion' +import { importNotion } from './notion/notion' import { setMetadata } from '@hcengineering/platform' import { FrontFileUploader, type FileUploader } from './importer/uploader' -import { ClickupImporter } from './clickup' +import { ClickupImporter } from './clickup/clickup' /** * @public diff --git a/dev/import-tool/src/notion/README.md b/dev/import-tool/src/notion/README.md new file mode 100644 index 0000000000..3ffd297d78 --- /dev/null +++ b/dev/import-tool/src/notion/README.md @@ -0,0 +1,44 @@ +## Import from Notion Guide + +### Export Data from Notion + +1. Follow [Notion's official guide](https://www.notion.so/help/export-your-content) to export your content +2. Important: Select **Markdown & CSV** export format +3. Extract the exported archive to a local directory + +### Run Import Tool + +1. Place your extracted Notion export in a directory (e.g., `/path/to/export`) +2. Choose one of two import options: +#### Option 1: Import with Original Teamspace Structure +``` +docker run \ + -e FRONT_URL="https://huly.app" \ + -v /path/to/export:/data \ + hardcoreeng/import-tool:latest \ + -- bundle.js import-notion-with-teamspaces /data \ + --user your.email@company.com \ + --password yourpassword \ + --workspace workspace-id +``` +#### Option 2: Import to Single Teamspace +``` +docker run \ + -e FRONT_URL="https://huly.app" \ + -v /path/to/export:/data \ + hardcoreeng/import-tool:latest \ + -- bundle.js import-notion-to-teamspace /data \ + --user your.email@company.com \ + --password yourpassword \ + --workspace workspace-id \ + --teamspace teamspace-name-to-be-created +``` + +#### Limitations +* Notion databases are not imported (CSV format not supported) +* Comments are not included in Notion exports, so they will not be imported +* Images and files must be included in the export to be imported +#### Notes +* All documents preserve their hierarchy +* Internal links between documents are maintained +* Images and attachments are imported automatically if present in export \ No newline at end of file diff --git a/dev/import-tool/src/notion.ts b/dev/import-tool/src/notion/notion.ts similarity index 99% rename from dev/import-tool/src/notion.ts rename to dev/import-tool/src/notion/notion.ts index fc6beb88c8..1ac2aedb0f 100644 --- a/dev/import-tool/src/notion.ts +++ b/dev/import-tool/src/notion/notion.ts @@ -42,7 +42,7 @@ import { type Dirent } from 'fs' import { readdir, readFile, stat } from 'fs/promises' import { contentType } from 'mime-types' import { basename, join, parse } from 'path' -import { type FileUploader } from './importer/uploader' +import { type FileUploader } from '../importer/uploader' interface DocumentMetadata { id: string diff --git a/models/server-ai-bot/src/index.ts b/models/server-ai-bot/src/index.ts index 7a1425d418..7fd057182d 100644 --- a/models/server-ai-bot/src/index.ts +++ b/models/server-ai-bot/src/index.ts @@ -13,18 +13,11 @@ // limitations under the License. // -import { type Builder, Mixin, Model, Prop, TypeRef, TypeString } from '@hcengineering/model' -import core, { type Account, type Class, type Doc, type Domain, type Ref, type Space } from '@hcengineering/core' +import { type Builder, Mixin } from '@hcengineering/model' +import core, { type Domain, type Ref } from '@hcengineering/core' import serverCore from '@hcengineering/server-core' import serverAiBot from '@hcengineering/server-ai-bot' -import { TDoc } from '@hcengineering/model-core' -import { getEmbeddedLabel } from '@hcengineering/platform' -import aiBot, { - type AIBotEvent, - type AIBotTransferEvent, - type AIBotResponseEvent, - type TransferredMessage -} from '@hcengineering/ai-bot' +import aiBot, { type TransferredMessage } from '@hcengineering/ai-bot' import chunter, { type ChatMessage } from '@hcengineering/chunter' import notification from '@hcengineering/notification' import { TChatMessage } from '@hcengineering/model-chunter' @@ -33,48 +26,6 @@ export { serverAiBotId } from '@hcengineering/server-ai-bot' export const DOMAIN_AI_BOT = 'ai_bot' as Domain -@Model(aiBot.class.AIBotEvent, core.class.Doc, DOMAIN_AI_BOT) -export class TAIBotEvent extends TDoc implements AIBotEvent { - @Prop(TypeRef(chunter.class.ChatMessage), core.string.Class) - messageClass!: Ref> - - @Prop(TypeRef(chunter.class.ChatMessage), core.string.Ref) - messageId!: Ref - - @Prop(TypeString(), getEmbeddedLabel('Collection')) - collection!: string - - @Prop(TypeString(), getEmbeddedLabel('Message')) - message!: string -} - -@Model(aiBot.class.AIBotResponseEvent, aiBot.class.AIBotEvent) -export class TAIBotResponseEvent extends TAIBotEvent implements AIBotResponseEvent { - @Prop(TypeRef(core.class.Doc), core.string.Object) - objectId!: Ref - - @Prop(TypeRef(core.class.Class), core.string.Class) - objectClass!: Ref> - - @Prop(TypeRef(core.class.Space), core.string.Space) - objectSpace!: Ref - - @Prop(TypeRef(core.class.Account), core.string.Account) - user!: Ref - - email!: string -} - -@Model(aiBot.class.AIBotTransferEvent, aiBot.class.AIBotEvent) -export class TAIBotTransferEvent extends TAIBotEvent implements AIBotTransferEvent { - toEmail!: string - toWorkspace!: string - fromWorkspace!: string - fromWorkspaceName!: string - fromWorkspaceUrl!: string - parentMessageId?: Ref -} - @Mixin(aiBot.mixin.TransferredMessage, chunter.class.ChatMessage) export class TTransferredMessage extends TChatMessage implements TransferredMessage { messageId!: Ref @@ -82,7 +33,7 @@ export class TTransferredMessage extends TChatMessage implements TransferredMess } export function createModel (builder: Builder): void { - builder.createModel(TAIBotEvent, TAIBotTransferEvent, TAIBotResponseEvent, TTransferredMessage) + builder.createModel(TTransferredMessage) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverAiBot.trigger.OnMessageSend, diff --git a/models/telegram/src/index.ts b/models/telegram/src/index.ts index 80e8a139c4..91878b222b 100644 --- a/models/telegram/src/index.ts +++ b/models/telegram/src/index.ts @@ -156,6 +156,7 @@ export function createModel (builder: Builder): void { { label: telegram.string.Telegram, description: telegram.string.TelegramIntegrationDesc, + descriptionComponent: telegram.component.TelegramIntegrationDescription, icon: telegram.component.IconTelegram, allowMultiple: false, createComponent: telegram.component.Connect, diff --git a/models/telegram/src/plugin.ts b/models/telegram/src/plugin.ts index 487a43ba1c..0f8b81f7ac 100644 --- a/models/telegram/src/plugin.ts +++ b/models/telegram/src/plugin.ts @@ -31,7 +31,6 @@ export default mergeIds(telegramId, telegram, { Incoming: '' as IntlString, Messages: '' as IntlString, Telegram: '' as IntlString, - TelegramIntegrationDesc: '' as IntlString, Status: '' as IntlString, ConfigLabel: '' as IntlString, ConfigDescription: '' as IntlString, @@ -53,6 +52,7 @@ export default mergeIds(telegramId, telegram, { TelegramMessageCreated: '' as AnyComponent }, component: { - NotificationProviderPresenter: '' as AnyComponent + NotificationProviderPresenter: '' as AnyComponent, + TelegramIntegrationDescription: '' as AnyComponent } }) diff --git a/packages/core/src/measurements/context.ts b/packages/core/src/measurements/context.ts index 25595c0889..f6e3bddc98 100644 --- a/packages/core/src/measurements/context.ts +++ b/packages/core/src/measurements/context.ts @@ -57,6 +57,7 @@ export class MeasureMetricsContext implements MeasureContext { private readonly fullParams: FullParamsType | (() => FullParamsType) = {} logger: MeasureLogger metrics: Metrics + id?: string st = Date.now() contextData: object = {} @@ -125,6 +126,8 @@ export class MeasureMetricsContext implements MeasureContext { this, this.logParams ) + result.id = this.id + result.onEnd = this.onEnd.bind(this) result.contextData = this.contextData return result } @@ -197,6 +200,8 @@ export class MeasureMetricsContext implements MeasureContext { end (): void { this.done() } + + async onEnd (ctx: MeasureContext): Promise {} } /** diff --git a/packages/core/src/measurements/types.ts b/packages/core/src/measurements/types.ts index 85c7cceed3..7ea9339a54 100644 --- a/packages/core/src/measurements/types.ts +++ b/packages/core/src/measurements/types.ts @@ -111,4 +111,6 @@ export interface MeasureContext { // Mark current context as complete // If no value is passed, time difference will be used. end: (value?: number) => void + + onEnd?: (ctx: MeasureContext) => Promise } diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index fe33ba5367..3247cfbf48 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -501,6 +501,7 @@ input.search { .ml-8 { margin-left: 2rem; } .ml-10 { margin-left: 2.5rem; } .ml-12 { margin-left: 3rem; } +.ml-14 { margin-left: 3.5rem; } .ml-22 { margin-left: 5.5rem; } .ml-auto { margin-left: auto; } .mr-0-5 { margin-right: .125rem; } @@ -744,6 +745,7 @@ input.search { .min-h-16 { min-height: 4rem; } .min-h-30 { min-height: 7.5rem; } .min-h-60 { min-height: 15rem; } +.max-w-0 { max-width: 0; } .max-w-2 { max-width: .5rem; } .max-w-4 { max-width: 1rem; } .max-w-9 { max-width: 2.25rem; } @@ -752,6 +754,7 @@ input.search { .max-w-40 { max-width: 10rem; } .max-w-60 { max-width: 15rem; } .max-w-80 { max-width: 20rem; } +.max-w-100 { max-width: 25rem; } .max-w-120 { max-width: 30rem; } .max-w-240 { max-width: 60rem; } .max-h-0 { max-height: 0; } diff --git a/packages/theme/styles/common.scss b/packages/theme/styles/common.scss index 87c6982bc2..3509ad2b06 100644 --- a/packages/theme/styles/common.scss +++ b/packages/theme/styles/common.scss @@ -141,23 +141,40 @@ } .antiPanel-navigator { position: relative; - min-width: 12.5rem; - max-width: 22.5rem; - width: 17.5rem; background-color: var(--theme-navpanel-color); + + &:not(.right) { + min-width: 12.5rem; + max-width: 22.5rem; + width: 17.5rem; + } + &.right { + overflow: hidden; + border-radius: var(--medium-BorderRadius) 0 0 var(--medium-BorderRadius); + } } @media (max-width: 1024px) { .antiPanel-navigator { position: fixed; - top: calc(var(--status-bar-height) + 1px); - height: calc(100% - var(--status-bar-height) - 1px); background-color: var(--theme-navpanel-color); - filter: drop-shadow(2px 0 1px rgba(0, 0, 0, .2)); z-index: 450; + + &:not(.right) { + top: calc(var(--status-bar-height) + 1px); + height: calc(100% - var(--status-bar-height) - 1px); + filter: drop-shadow(2px 0 5px rgba(0, 0, 0, .2)); - &.portrait { left: 0; } - &.landscape { left: var(--app-panel-width); } + &.portrait { left: 0; } + &.landscape { left: var(--app-panel-width); } + } + &.right { + top: var(--status-bar-height); + right: 0; + height: calc(100% - var(--status-bar-height) - 1px); + background-color: var(--theme-statusbar-color); + filter: drop-shadow(-2px 0 5px rgba(0, 0, 0, .2)); + } } } .antiPanel-component { diff --git a/packages/theme/styles/global.scss b/packages/theme/styles/global.scss index f3e054d191..a7f883a923 100644 --- a/packages/theme/styles/global.scss +++ b/packages/theme/styles/global.scss @@ -78,6 +78,7 @@ scrollbar-width: none; --body-font-size: .875rem; --status-bar-height: 36px; + --status-bar-normal-height: 36px; --panel-aside-width: 25rem; // 20rem; --font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto; --mono-font: 'IBM Plex Mono', monospace; @@ -89,6 +90,10 @@ &::after, &::before { box-sizing: border-box; } + + @media (max-width: 480px) { + --status-bar-height: 70px; + } } :root { --app-height: 100%; diff --git a/packages/ui/src/components/ModernTab.svelte b/packages/ui/src/components/ModernTab.svelte index 54054aafa3..24918a7bd7 100644 --- a/packages/ui/src/components/ModernTab.svelte +++ b/packages/ui/src/components/ModernTab.svelte @@ -36,7 +36,7 @@
{/if} - + {#if label} {label} {:else if labelIntl} @@ -100,11 +100,14 @@ &.horizontal { padding: 0.125rem 0.125rem 0.125rem 0.5rem; height: 1.625rem; + min-height: 1.625rem; + min-width: 6rem; } &.vertical { padding: 0.5rem 0.125rem 0.125rem 0.125rem; width: 1.625rem; + min-height: 6rem; writing-mode: vertical-rl; text-orientation: sideways; } diff --git a/packages/ui/src/components/Panel.svelte b/packages/ui/src/components/Panel.svelte index 7821e9832e..066d2bcd97 100644 --- a/packages/ui/src/components/Panel.svelte +++ b/packages/ui/src/components/Panel.svelte @@ -168,7 +168,7 @@
{#if $deviceInfo.isMobile} - -
{ - innerWidth = element.clientWidth - }} - > +
{ + innerWidth = element.clientWidth + }} + > + {#if $$slots.header && isHeader}
{/if} -
- + +
{:else}
- -
-
+
+
@@ -133,8 +117,8 @@ class="bar" class:hovered={isScrolling} bind:this={divBar} - on:mousedown={onScrollStart} - on:mouseleave={checkMask} + on:pointerdown={onScrollStart} + on:pointerleave={checkMask} />
diff --git a/packages/ui/src/components/icons/Details.svelte b/packages/ui/src/components/icons/Details.svelte index 26e6dd6629..82bd6fc69b 100644 --- a/packages/ui/src/components/icons/Details.svelte +++ b/packages/ui/src/components/icons/Details.svelte @@ -16,7 +16,7 @@ diff --git a/packages/ui/src/components/icons/DetailsFilled.svelte b/packages/ui/src/components/icons/DetailsFilled.svelte index 2da11f89d8..a223fbf9c7 100644 --- a/packages/ui/src/components/icons/DetailsFilled.svelte +++ b/packages/ui/src/components/icons/DetailsFilled.svelte @@ -15,7 +15,7 @@ --> diff --git a/packages/ui/src/components/icons/MoreH.svelte b/packages/ui/src/components/icons/MoreH.svelte index 98f32623c1..5b9ed2859a 100644 --- a/packages/ui/src/components/icons/MoreH.svelte +++ b/packages/ui/src/components/icons/MoreH.svelte @@ -1,6 +1,6 @@ diff --git a/packages/ui/src/components/internal/Root.svelte b/packages/ui/src/components/internal/Root.svelte index 443ae4b365..8356f9006f 100644 --- a/packages/ui/src/components/internal/Root.svelte +++ b/packages/ui/src/components/internal/Root.svelte @@ -5,7 +5,16 @@ import { deviceSizes, deviceWidths } from '../../types' // import { applicationShortcutKey } from '../../utils' import { Theme } from '@hcengineering/theme' - import { IconArrowLeft, IconArrowRight, checkMobile, deviceOptionsStore as deviceInfo } from '../../' + import { + IconArrowLeft, + IconArrowRight, + checkMobile, + deviceOptionsStore as deviceInfo, + checkAdaptiveMatching, + ButtonIcon, + IconDetailsFilled, + IconDetails + } from '../../' import { desktopPlatform, getCurrentLocation, location, locationStorageKeyId, navigate } from '../../location' import uiPlugin from '../../plugin' import Component from '../Component.svelte' @@ -129,6 +138,14 @@ } } updateDeviceSize() + + $: secondRow = checkAdaptiveMatching($deviceInfo.size, 'xs') + $: asideFloat = $deviceInfo.navigator.float + $: asideOpen = $deviceInfo.aside.visible + $: appsMini = + $deviceInfo.isMobile && + (($deviceInfo.isPortrait && $deviceInfo.docWidth <= 480) || + (!$deviceInfo.isPortrait && $deviceInfo.docHeight <= 480)) @@ -161,9 +178,15 @@
{/if} -
- -
+ {#if !secondRow} +
+ +
+ {/if}
+ {#if asideFloat && !secondRow} +
+ { + $deviceInfo.aside.visible = !$deviceInfo.aside.visible + }} + /> + {/if}
- + {#if !secondRow} + + {/if}
+ {#if secondRow} +
+
+ +
+
+ + { + $deviceInfo.aside.visible = !$deviceInfo.aside.visible + }} + /> +
+
+ {/if}
{#if application} @@ -213,6 +273,7 @@ .antiStatusBar { -webkit-app-region: drag; + min-width: 0; min-height: var(--status-bar-height); height: var(--status-bar-height); // min-width: 600px; @@ -248,6 +309,21 @@ margin: 0 12px 0 8px; } + .second-row { + display: none; + } + + @media (max-width: 480px) { + display: flex; + flex-direction: column; + gap: 2px; + padding: 2px 0; + width: 100%; + + .second-row { + display: flex; + } + } @media print { display: none; } diff --git a/packages/ui/src/components/internal/RootBarExtension.svelte b/packages/ui/src/components/internal/RootBarExtension.svelte index 5d33a0863e..6ce22ec56e 100644 --- a/packages/ui/src/components/internal/RootBarExtension.svelte +++ b/packages/ui/src/components/internal/RootBarExtension.svelte @@ -17,7 +17,7 @@ {#each sorted as ext (ext[1].id)} {#if ext[0] === position} -
+
({ isPortrait: false, isMobile: false, navigator: { visible: true, float: false, direction: 'vertical' }, + aside: { visible: true }, fontSize: 0, size: null, sizes: { xs: false, sm: false, md: false, lg: false, xl: false, xxl: false }, diff --git a/packages/ui/src/resize.ts b/packages/ui/src/resize.ts index aad3a64c4f..7ebae9e983 100644 --- a/packages/ui/src/resize.ts +++ b/packages/ui/src/resize.ts @@ -166,7 +166,7 @@ export const settingsSeparators: DefSeparators = [ export const mainSeparators: DefSeparators = [ { minSize: 30, size: 'auto', maxSize: 'auto' }, - { minSize: 20, size: 30, maxSize: 80, float: 'sidebar' } + { minSize: 25, size: 30, maxSize: 80, float: 'sidebar' } ] export const secondNavSeparators: DefSeparators = [{ minSize: 7, size: 7.5, maxSize: 15, float: 'navigator' }, null] diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts index 33038525ad..f96794cdb3 100644 --- a/packages/ui/src/types.ts +++ b/packages/ui/src/types.ts @@ -378,6 +378,7 @@ export interface DeviceOptions { isPortrait: boolean isMobile: boolean navigator: { visible: boolean, float: boolean, direction: 'vertical' | 'horizontal' } + aside: { visible: boolean } fontSize: number size: WidthType | null sizes: Record diff --git a/plugins/ai-bot/src/index.ts b/plugins/ai-bot/src/index.ts index e7748b0129..483c317c49 100644 --- a/plugins/ai-bot/src/index.ts +++ b/plugins/ai-bot/src/index.ts @@ -13,41 +13,18 @@ // limitations under the License. // -import { Account, Class, Doc, type Mixin, Ref, Space } from '@hcengineering/core' +import { Account, type Mixin, Ref } from '@hcengineering/core' import type { Metadata, Plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import { ChatMessage } from '@hcengineering/chunter' export * from './types' +export * from './rest' export const aiBotId = 'ai-bot' as Plugin export const aiBotAccountEmail = 'huly.ai.bot@hc.engineering' -export interface AIBotEvent extends Doc { - collection: string - messageClass: Ref> - messageId: Ref - message: string -} - -export interface AIBotResponseEvent extends AIBotEvent { - objectId: Ref - objectClass: Ref> - objectSpace: Ref - user: Ref - email: string -} - -export interface AIBotTransferEvent extends AIBotEvent { - toEmail: string - toWorkspace: string - fromWorkspace: string - fromWorkspaceName: string - fromWorkspaceUrl: string - parentMessageId?: Ref -} - export interface TransferredMessage extends ChatMessage { messageId: Ref parentMessageId?: Ref @@ -57,11 +34,6 @@ const aiBot = plugin(aiBotId, { metadata: { EndpointURL: '' as Metadata }, - class: { - AIBotEvent: '' as Ref>, - AIBotTransferEvent: '' as Ref>, - AIBotResponseEvent: '' as Ref> - }, mixin: { TransferredMessage: '' as Ref> }, diff --git a/plugins/ai-bot/src/rest.ts b/plugins/ai-bot/src/rest.ts new file mode 100644 index 0000000000..4a75914b7b --- /dev/null +++ b/plugins/ai-bot/src/rest.ts @@ -0,0 +1,58 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public 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 https://www.eclipse.org/legal/epl-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 CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Account, Class, Doc, Markup, Ref, Space, Timestamp } from '@hcengineering/core' +import { ChatMessage } from '@hcengineering/chunter' + +export enum AIEventType { + Message = 'message', + Transfer = 'transfer' +} + +export interface AIEventRequest { + type: AIEventType + collection: string + messageClass: Ref> + messageId: Ref + message: string + createdOn: Timestamp +} + +export interface AIMessageEventRequest extends AIEventRequest { + objectId: Ref + objectClass: Ref> + objectSpace: Ref + user: Ref + email: string +} + +export interface AITransferEventRequest extends AIEventRequest { + toEmail: string + toWorkspace: string + fromWorkspace: string + fromWorkspaceName: string + fromWorkspaceUrl: string + parentMessageId?: Ref +} + +export interface TranslateRequest { + text: Markup + lang: string +} + +export interface TranslateResponse { + text: Markup + lang: string +} diff --git a/plugins/ai-bot/src/types.ts b/plugins/ai-bot/src/types.ts index 8f999d2452..5d447977d0 100644 --- a/plugins/ai-bot/src/types.ts +++ b/plugins/ai-bot/src/types.ts @@ -13,18 +13,6 @@ // limitations under the License. // -import { Markup } from '@hcengineering/core' - -export interface TranslateRequest { - text: Markup - lang: string -} - -export interface TranslateResponse { - text: Markup - lang: string -} - export enum OnboardingEvent { OpenChatInSidebar = 'openChatInSidebar' } diff --git a/plugins/chunter-resources/src/navigation.ts b/plugins/chunter-resources/src/navigation.ts index 8e13ecae37..6dc72ed16e 100644 --- a/plugins/chunter-resources/src/navigation.ts +++ b/plugins/chunter-resources/src/navigation.ts @@ -5,7 +5,8 @@ import { getLocation, type Location, navigate, - languageStore + languageStore, + deviceOptionsStore as deviceInfo } from '@hcengineering/ui' import { type Ref, type Doc, type Class, generateId } from '@hcengineering/core' import activity, { type ActivityMessage } from '@hcengineering/activity' @@ -179,6 +180,10 @@ export async function replyToThread (message: ActivityMessage, e: Event): Promis const fromSidebar = isElementFromSidebar(e.target as HTMLElement) const loc = getCurrentLocation() + const dev = get(deviceInfo) + dev.aside.visible = true + deviceInfo.set(dev) + threadMessagesStore.set(message) if (fromSidebar) { diff --git a/plugins/contact-resources/src/components/MergeAttributeComparer.svelte b/plugins/contact-resources/src/components/MergeAttributeComparer.svelte index b88c5b9882..b8f2f27988 100644 --- a/plugins/contact-resources/src/components/MergeAttributeComparer.svelte +++ b/plugins/contact-resources/src/components/MergeAttributeComparer.svelte @@ -23,7 +23,7 @@ export let targetEmp: Person export let key: string export let onChange: (key: string, value: boolean) => void - export let selected = false + export let selected: boolean = false const client = getClient() const hierarchy = client.getHierarchy() diff --git a/plugins/contact-resources/src/components/MergeComparer.svelte b/plugins/contact-resources/src/components/MergeComparer.svelte index 9417c0670f..d5ca6b4916 100644 --- a/plugins/contact-resources/src/components/MergeComparer.svelte +++ b/plugins/contact-resources/src/components/MergeComparer.svelte @@ -16,13 +16,14 @@ import { Person } from '@hcengineering/contact' import { Doc, Mixin, Ref } from '@hcengineering/core' import { getClient } from '@hcengineering/presentation' - import { CheckBox, Label } from '@hcengineering/ui' + import { Label, RadioButton } from '@hcengineering/ui' + import { FixedColumn } from '@hcengineering/view-resources' export let value: Person export let targetEmp: Person export let cast: Ref> | undefined = undefined export let key: string - export let selected = false + export let selected: boolean = false export let onChange: (key: string, value: boolean) => void const client = getClient() @@ -38,51 +39,51 @@ return (value as any)[key] === (targetEmp as any)[key] } $: attribute = hierarchy.findAttribute(cast ?? value._class, key) + const change = (sel: boolean): void => { + selected = sel + onChange(key, sel) + } {#if !isEqual(value, targetEmp, key)} -
-
+
+ {#if attribute?.label}
+ -
-
- { - selected = false - onChange(key, false) - }} - /> -
- -
-
-
-
- { - selected = true - onChange(key, true) - }} - /> -
- -
+ + { + if (selected) change(false) + }} + > + + + + + + { + if (!selected) change(true) + }} + > + + +
{/if} diff --git a/plugins/workbench-resources/src/components/sidebar/Sidebar.svelte b/plugins/workbench-resources/src/components/sidebar/Sidebar.svelte index c8422d2553..b761d46c4f 100644 --- a/plugins/workbench-resources/src/components/sidebar/Sidebar.svelte +++ b/plugins/workbench-resources/src/components/sidebar/Sidebar.svelte @@ -17,7 +17,7 @@ import { WidgetPreference, SidebarEvent, TxSidebarEvent, OpenSidebarWidgetParams } from '@hcengineering/workbench' import { Tx } from '@hcengineering/core' import { onMount } from 'svelte' - import { panelstore } from '@hcengineering/ui' + import { panelstore, deviceOptionsStore as deviceInfo } from '@hcengineering/ui' import workbench from '../../plugin' import { createWidgetTab, openWidget, sidebarStore, SidebarVariant } from '../../sidebar' @@ -61,7 +61,12 @@ }) -