|
13 | 13 | import PrTemplateSection from '$components/PrTemplateSection.svelte';
|
14 | 14 | import AsyncRender from '$components/v3/AsyncRender.svelte';
|
15 | 15 | import MessageEditor from '$components/v3/editor/MessageEditor.svelte';
|
| 16 | + import MessageEditorInput from '$components/v3/editor/MessageEditorInput.svelte'; |
16 | 17 | import { AIService } from '$lib/ai/service';
|
17 | 18 | import { PostHogWrapper } from '$lib/analytics/posthog';
|
18 | 19 | import { BaseBranch } from '$lib/baseBranch/baseBranch';
|
|
43 | 44 | import { sleep } from '$lib/utils/sleep';
|
44 | 45 | import { getContext } from '@gitbutler/shared/context';
|
45 | 46 | import { persisted } from '@gitbutler/shared/persisted';
|
46 |
| - import Checkbox from '@gitbutler/ui/Checkbox.svelte'; |
47 |
| - import Icon from '@gitbutler/ui/Icon.svelte'; |
48 |
| - import Textbox from '@gitbutler/ui/Textbox.svelte'; |
49 |
| - import Toggle from '@gitbutler/ui/Toggle.svelte'; |
50 |
| - import Link from '@gitbutler/ui/link/Link.svelte'; |
51 | 47 | import { error } from '@gitbutler/ui/toasts';
|
52 | 48 | import { isDefined } from '@gitbutler/ui/utils/typeguards';
|
53 | 49 | import { tick } from 'svelte';
|
|
115 | 111 | !forgeBranch || (branchDetails ? requiresPush(branchDetails.pushStatus) : true)
|
116 | 112 | );
|
117 | 113 |
|
118 |
| - let titleInput = $state<ReturnType<typeof Textbox>>(); |
| 114 | + let titleInput = $state<HTMLInputElement | undefined>(undefined); |
119 | 115 | let messageEditor = $state<MessageEditor>();
|
120 | 116 |
|
121 | 117 | // AI things
|
|
422 | 418 | };
|
423 | 419 | </script>
|
424 | 420 |
|
425 |
| -<div class="pr-content"> |
426 |
| - <Textbox |
427 |
| - testId={TestId.ReviewTitleInput} |
428 |
| - autofocus |
429 |
| - size="large" |
430 |
| - placeholder="PR title" |
431 |
| - bind:this={titleInput} |
432 |
| - value={$prTitle} |
433 |
| - disabled={isExecuting} |
434 |
| - oninput={(value: string) => prTitle.set(value)} |
435 |
| - onkeydown={(e: KeyboardEvent) => { |
436 |
| - if (e.key === 'Enter' || e.key === 'Tab') { |
437 |
| - e.preventDefault(); |
438 |
| - messageEditor?.focus(); |
439 |
| - } |
440 |
| - }} |
441 |
| - /> |
442 |
| - |
| 421 | +<div class="pr-editor"> |
443 | 422 | <PrTemplateSection
|
444 | 423 | {projectId}
|
445 | 424 | disabled={isExecuting}
|
|
450 | 429 | />
|
451 | 430 |
|
452 | 431 | <AsyncRender>
|
453 |
| - <MessageEditor |
454 |
| - bind:this={messageEditor} |
455 |
| - testId={TestId.ReviewDescriptionInput} |
456 |
| - {projectId} |
457 |
| - disabled={isExecuting} |
458 |
| - initialValue={$prBody} |
459 |
| - enableFileUpload |
460 |
| - placeholder="PR Description" |
461 |
| - {onAiButtonClick} |
462 |
| - {canUseAI} |
463 |
| - {aiIsLoading} |
464 |
| - onChange={(text: string) => { |
465 |
| - prBody.set(text); |
466 |
| - }} |
467 |
| - onKeyDown={(e: KeyboardEvent) => { |
468 |
| - if (e.key === 'Tab' && e.shiftKey) { |
469 |
| - e.preventDefault(); |
470 |
| - titleInput?.focus(); |
471 |
| - return true; |
472 |
| - } |
| 432 | + <div class="pr-editor__content"> |
| 433 | + <MessageEditorInput |
| 434 | + testId={TestId.ReviewTitleInput} |
| 435 | + bind:ref={titleInput} |
| 436 | + value={$prTitle} |
| 437 | + onchange={(value) => { |
| 438 | + prTitle.set(value); |
| 439 | + }} |
| 440 | + onkeydown={(e: KeyboardEvent) => { |
| 441 | + if (e.key === 'Enter' || (e.key === 'Tab' && !e.shiftKey)) { |
| 442 | + e.preventDefault(); |
| 443 | + messageEditor?.focus(); |
| 444 | + } |
| 445 | + }} |
| 446 | + placeholder="PR title" |
| 447 | + showCount={false} |
| 448 | + oninput={(e: Event) => { |
| 449 | + const target = e.target as HTMLInputElement; |
| 450 | + prTitle.set(target.value); |
| 451 | + }} |
| 452 | + /> |
| 453 | + <MessageEditor |
| 454 | + bind:this={messageEditor} |
| 455 | + testId={TestId.ReviewDescriptionInput} |
| 456 | + {projectId} |
| 457 | + disabled={isExecuting} |
| 458 | + initialValue={$prBody} |
| 459 | + enableFileUpload |
| 460 | + enableSmiles |
| 461 | + placeholder="PR Description" |
| 462 | + {onAiButtonClick} |
| 463 | + {canUseAI} |
| 464 | + {aiIsLoading} |
| 465 | + onChange={(text: string) => { |
| 466 | + prBody.set(text); |
| 467 | + }} |
| 468 | + onKeyDown={(e: KeyboardEvent) => { |
| 469 | + if (e.key === 'Tab' && e.shiftKey) { |
| 470 | + e.preventDefault(); |
| 471 | + titleInput?.focus(); |
| 472 | + return true; |
| 473 | + } |
473 | 474 |
|
474 |
| - if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { |
475 |
| - e.preventDefault(); |
476 |
| - createReview(); |
477 |
| - return true; |
478 |
| - } |
| 475 | + if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { |
| 476 | + e.preventDefault(); |
| 477 | + createReview(); |
| 478 | + return true; |
| 479 | + } |
479 | 480 |
|
480 |
| - return false; |
481 |
| - }} |
482 |
| - /> |
| 481 | + return false; |
| 482 | + }} |
| 483 | + /> |
| 484 | + </div> |
483 | 485 | </AsyncRender>
|
484 |
| - |
485 |
| - {#if canPublishBR && canPublishPR} |
486 |
| - <AsyncRender> |
487 |
| - <div class="options text-13"> |
488 |
| - <label for="create-br" class="option-card"> |
489 |
| - <div class="option-card-header" class:selected={$createButlerRequest}> |
490 |
| - <div class="option-card-header-content"> |
491 |
| - <div class="option-card-header-title text-semibold"> |
492 |
| - <Icon name="bowtie" /> |
493 |
| - Create Butler Request |
494 |
| - </div> |
495 |
| - <span class="options__learn-more"> |
496 |
| - <Link href="https://docs.gitbutler.com/review/overview">Learn more</Link> |
497 |
| - </span> |
498 |
| - </div> |
499 |
| - <div class="option-card-header-action"> |
500 |
| - <Checkbox |
501 |
| - disabled={isExecuting} |
502 |
| - name="create-br" |
503 |
| - bind:checked={$createButlerRequest} |
504 |
| - /> |
505 |
| - </div> |
506 |
| - </div> |
507 |
| - </label> |
508 |
| - |
509 |
| - <div class="option-card"> |
510 |
| - <label |
511 |
| - for="create-pr" |
512 |
| - class="option-card-header has-settings" |
513 |
| - class:selected={$createPullRequest} |
514 |
| - > |
515 |
| - <div class="option-card-header-content"> |
516 |
| - <div class="option-card-header-title text-semibold"> |
517 |
| - <Icon name="github" /> |
518 |
| - Create Pull Request |
519 |
| - </div> |
520 |
| - </div> |
521 |
| - |
522 |
| - <div class="option-card-header-action"> |
523 |
| - <Checkbox name="create-pr" bind:checked={$createPullRequest} /> |
524 |
| - </div> |
525 |
| - </label> |
526 |
| - <label |
527 |
| - for="create-pr-draft" |
528 |
| - class="option-subcard-drafty" |
529 |
| - class:disabled={!$createPullRequest} |
530 |
| - > |
531 |
| - <span class="text-semibold">PR Draft</span> |
532 |
| - <Toggle disabled={isExecuting} id="create-pr-draft" bind:checked={$createDraft} /> |
533 |
| - </label> |
534 |
| - </div> |
535 |
| - </div> |
536 |
| - </AsyncRender> |
537 |
| - {/if} |
538 | 486 | </div>
|
539 | 487 |
|
540 | 488 | <style lang="postcss">
|
541 |
| - .pr-content { |
| 489 | + .pr-editor { |
542 | 490 | display: flex;
|
543 | 491 | flex: 1;
|
544 | 492 | flex-direction: column;
|
545 | 493 | overflow: hidden;
|
546 |
| - gap: 14px; |
547 |
| - } |
548 |
| -
|
549 |
| - .options { |
550 |
| - display: grid; |
551 |
| - grid-template-columns: 1fr 1fr; |
552 |
| - align-items: stretch; |
553 |
| - width: 100%; |
554 |
| - gap: 8px; |
555 |
| - } |
556 |
| -
|
557 |
| - .option-card { |
558 |
| - display: flex; |
559 |
| - flex-direction: column; |
560 |
| - overflow: hidden; |
561 |
| - border-radius: var(--radius-m); |
562 |
| - } |
563 |
| -
|
564 |
| - /* OPTION BOX */ |
565 |
| - .option-card-header { |
566 |
| - display: flex; |
567 |
| - flex-grow: 1; |
568 |
| - padding: 12px; |
569 |
| - border: 1px solid var(--clr-border-2); |
570 |
| - border-radius: var(--radius-m); |
571 |
| - transition: background-color var(--transition-fast); |
572 |
| -
|
573 |
| - &:hover { |
574 |
| - background-color: var(--clr-bg-1-muted); |
575 |
| - } |
576 |
| -
|
577 |
| - &.has-settings { |
578 |
| - border-radius: var(--radius-m) var(--radius-m) 0 0; |
579 |
| - } |
580 |
| -
|
581 |
| - &.selected { |
582 |
| - border-color: var(--clr-theme-pop-element); |
583 |
| - background-color: var(--clr-theme-pop-bg); |
584 |
| - } |
585 |
| - } |
586 |
| -
|
587 |
| - .option-card-header-content { |
588 |
| - display: flex; |
589 |
| - flex-grow: 1; |
590 |
| - flex-direction: column; |
591 |
| - justify-content: flex-end; |
592 | 494 | gap: 10px;
|
593 | 495 | }
|
594 | 496 |
|
595 |
| - .option-card-header-title { |
| 497 | + .pr-editor__content { |
596 | 498 | display: flex;
|
597 |
| - align-items: center; |
598 |
| - gap: 8px; |
599 |
| - } |
600 |
| -
|
601 |
| - .option-card-header-action { |
602 |
| - display: block; |
603 |
| - flex-grow: 0; |
604 |
| - } |
605 |
| -
|
606 |
| - .option-subcard-drafty { |
607 |
| - display: flex; |
608 |
| - align-items: center; |
609 |
| - justify-content: space-between; |
610 |
| - padding: 12px; |
611 |
| - border: 1px solid var(--clr-border-2); |
612 |
| - border-top: none; |
613 |
| -
|
614 |
| - border-radius: 0 0 var(--radius-m) var(--radius-m); |
615 |
| - transition: background-color var(--transition-fast); |
616 |
| -
|
617 |
| - &:hover { |
618 |
| - background-color: var(--clr-bg-1-muted); |
619 |
| - } |
620 |
| -
|
621 |
| - &.disabled { |
622 |
| - background-color: var(--clr-bg-2); |
623 |
| - cursor: not-allowed; |
624 |
| - opacity: 0.5; |
625 |
| - pointer-events: none; |
626 |
| - } |
627 |
| - } |
628 |
| -
|
629 |
| - .options__learn-more { |
630 |
| - color: var(--clr-text-2); |
| 499 | + flex-direction: column; |
631 | 500 | }
|
632 | 501 | </style>
|
0 commit comments