| 377 | return fileName; | 392 | return fileName; |
| 378 | } | 393 | } |
| 379 | | 394 | |
| 380 | jQuery(function () { | 395 | function registerBookmarksSlashCommands() { |
| | 396 | /** |
| | 397 | * Validates a message ID. (Is a number, exists as a message) |
| | 398 | * |
| | 399 | * @param {number} mesId - The message ID to validate. |
| | 400 | * @param {string} context - The context of the slash command. Will be used as the title of any toasts. |
| | 401 | * @returns {boolean} - Returns true if the message ID is valid, otherwise false. |
| | 402 | */ |
| | 403 | function validateMessageId(mesId, context) { |
| | 404 | if (isNaN(mesId)) { |
| | 405 | toastr.warning('Invalid message ID was provided', context); |
| | 406 | return false; |
| | 407 | } |
| | 408 | if (!chat[mesId]) { |
| | 409 | toastr.warning(`Message for id ${mesId} not found`, context); |
| | 410 | return false; |
| | 411 | } |
| | 412 | return true; |
| | 413 | } |
| | 414 | |
| | 415 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 416 | name: 'branch-create', |
| | 417 | returns: 'Name of the new branch', |
| | 418 | callback: async (args, text) => { |
| | 419 | const mesId = Number(args.mesId ?? text ?? getLastMessageId()); |
| | 420 | if (!validateMessageId(mesId, 'Create Branch')) return ''; |
| | 421 | |
| | 422 | const branchName = await branchChat(mesId); |
| | 423 | return branchName ?? ''; |
| | 424 | }, |
| | 425 | namedArgumentList: [ |
| | 426 | SlashCommandNamedArgument.fromProps({ |
| | 427 | name: 'mes', |
| | 428 | description: 'Message ID', |
| | 429 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 430 | enumProvider: commonEnumProviders.messages(), |
| | 431 | }), |
| | 432 | ], |
| | 433 | unnamedArgumentList: [ |
| | 434 | SlashCommandArgument.fromProps({ |
| | 435 | description: 'Message ID', |
| | 436 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 437 | enumProvider: commonEnumProviders.messages(), |
| | 438 | }), |
| | 439 | ], |
| | 440 | helpString: ` |
| | 441 | <div> |
| | 442 | Create a new branch from the selected message. If no message id is provided, will use the last message. |
| | 443 | </div> |
| | 444 | <div> |
| | 445 | Creating a branch will automatically choose a name for the branch.<br /> |
| | 446 | After creating the branch, the branch chat will be automatically opened. |
| | 447 | </div> |
| | 448 | <div> |
| | 449 | Use Checkpoints and <code>/checkpoint-create</code> instead if you do not want to jump to the new chat. |
| | 450 | </div>`, |
| | 451 | })); |
| | 452 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 453 | name: 'checkpoint-create', |
| | 454 | returns: 'Name of the new checkpoint', |
| | 455 | callback: async (args, text) => { |
| | 456 | const mesId = Number(args.mesId ?? getLastMessageId()); |
| | 457 | if (!validateMessageId(mesId, 'Create Checkpoint')) return ''; |
| | 458 | |
| | 459 | if (!text || typeof text !== 'string') { |
| | 460 | toastr.warning('Checkpoint name must be provided', 'Create Checkpoint'); |
| | 461 | return ''; |
| | 462 | } |
| | 463 | |
| | 464 | const checkPointName = await createNewBookmark(mesId, { forceName: text }); |
| | 465 | return checkPointName ?? ''; |
| | 466 | }, |
| | 467 | namedArgumentList: [ |
| | 468 | SlashCommandNamedArgument.fromProps({ |
| | 469 | name: 'mes', |
| | 470 | description: 'Message ID', |
| | 471 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 472 | enumProvider: commonEnumProviders.messages(), |
| | 473 | }), |
| | 474 | ], |
| | 475 | unnamedArgumentList: [ |
| | 476 | SlashCommandArgument.fromProps({ |
| | 477 | description: 'Checkpoint name', |
| | 478 | typeList: [ARGUMENT_TYPE.STRING], |
| | 479 | isRequired: true, |
| | 480 | }), |
| | 481 | ], |
| | 482 | helpString: ` |
| | 483 | <div> |
| | 484 | Create a new checkpoint for the selected message with the provided name. If no message id is provided, will use the last message. |
| | 485 | </div> |
| | 486 | <div> |
| | 487 | A created checkpoint will be permanently linked with the message.<br /> |
| | 488 | If a checkpoint already exists, the link to it will be overwritten.<br /> |
| | 489 | After creating the checkpoint, the checkpoint chat can be opened with the checkpoint flag, |
| | 490 | using the <code>/go</code> command with the checkpoint name or the <code>/checkpoint-go</code> command on the message. |
| | 491 | </div> |
| | 492 | <div> |
| | 493 | Use Branches and <code>/branch-create</code> instead if you do want to jump to the new chat. |
| | 494 | </div> |
| | 495 | <div> |
| | 496 | <strong>Example:</strong> |
| | 497 | <ul> |
| | 498 | <li> |
| | 499 | <pre><code>/checkpoint-create mes={{lastCharMessage}} Checkpoint for char reply | /setvar key=rememberCheckpoint {{pipe}}</code></pre> |
| | 500 | Will create a new checkpoint to the latest message of the current character, and save it as a local variable for future use. |
| | 501 | </li> |
| | 502 | </ul> |
| | 503 | </div>`, |
| | 504 | })); |
| | 505 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 506 | name: 'checkpoint-go', |
| | 507 | returns: 'Name of the checkpoint', |
| | 508 | callback: async (args, text) => { |
| | 509 | const mesId = Number(args.mesId ?? text ?? getLastMessageId()); |
| | 510 | if (!validateMessageId(mesId, 'Open Checkpoint')) return ''; |
| | 511 | |
| | 512 | const checkPointName = chat[mesId].extra?.bookmark_link; |
| | 513 | if (!checkPointName) { |
| | 514 | toastr.warning('No checkpoint is linked to the selected message', 'Open Checkpoint'); |
| | 515 | return ''; |
| | 516 | } |
| | 517 | |
| | 518 | if (selected_group) { |
| | 519 | await openGroupChat(selected_group, checkPointName); |
| | 520 | } else { |
| | 521 | await openCharacterChat(checkPointName); |
| | 522 | } |
| | 523 | |
| | 524 | return checkPointName; |
| | 525 | }, |
| | 526 | namedArgumentList: [ |
| | 527 | SlashCommandNamedArgument.fromProps({ |
| | 528 | name: 'mes', |
| | 529 | description: 'Message ID', |
| | 530 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 531 | enumProvider: commonEnumProviders.messages(), |
| | 532 | }), |
| | 533 | ], |
| | 534 | unnamedArgumentList: [ |
| | 535 | SlashCommandArgument.fromProps({ |
| | 536 | description: 'Message ID', |
| | 537 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 538 | enumProvider: commonEnumProviders.messages(), |
| | 539 | }), |
| | 540 | ], |
| | 541 | helpString: ` |
| | 542 | <div> |
| | 543 | Open the checkpoint linked to the selected message. If no message id is provided, will use the last message. |
| | 544 | </div> |
| | 545 | <div> |
| | 546 | Use <code>/checkpoint-get</code> if you want to make sure that the selected message has a checkpoint. |
| | 547 | </div>`, |
| | 548 | })); |
| | 549 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 550 | name: 'checkpoint-exit', |
| | 551 | returns: 'The name of the chat exited to. Returns null if not in a checkpoint chat.', |
| | 552 | callback: async () => { |
| | 553 | const mainChat = await backToMainChat(); |
| | 554 | return mainChat ?? ''; |
| | 555 | }, |
| | 556 | helpString: 'Exit the checkpoint chat.<br />If not in a checkpoint chat, returns null.', |
| | 557 | })); |
| | 558 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 559 | name: 'checkpoint-parent', |
| | 560 | returns: 'Name of the parent chat for this checkpoint', |
| | 561 | callback: async () => { |
| | 562 | const mainChatName = getMainChatName(); |
| | 563 | return mainChatName ?? ''; |
| | 564 | }, |
| | 565 | helpString: 'Get the name of the parent chat for this checkpoint. If not in a checkpoint chat, returns null.', |
| | 566 | })) |
| | 567 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 568 | name: 'checkpoint-get', |
| | 569 | returns: 'Name of the chat', |
| | 570 | callback: async (args, text) => { |
| | 571 | const mesId = Number(args.mesId ?? text ?? getLastMessageId()); |
| | 572 | if (!validateMessageId(mesId, 'Get Checkpoint')) return ''; |
| | 573 | |
| | 574 | const checkPointName = chat[mesId].extra?.bookmark_link; |
| | 575 | return checkPointName ?? ''; |
| | 576 | }, |
| | 577 | namedArgumentList: [ |
| | 578 | SlashCommandNamedArgument.fromProps({ |
| | 579 | name: 'mes', |
| | 580 | description: 'Message ID', |
| | 581 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 582 | enumProvider: commonEnumProviders.messages(), |
| | 583 | }), |
| | 584 | ], |
| | 585 | unnamedArgumentList: [ |
| | 586 | SlashCommandArgument.fromProps({ |
| | 587 | description: 'Message ID', |
| | 588 | typeList: [ARGUMENT_TYPE.NUMBER], |
| | 589 | enumProvider: commonEnumProviders.messages(), |
| | 590 | }), |
| | 591 | ], |
| | 592 | helpString: ` |
| | 593 | <div> |
| | 594 | Get the name of the checkpoint linked to the selected message. If no message id is provided, will use the last message.<br /> |
| | 595 | If no checkpoint is linked, the result will be empty. |
| | 596 | </div>`, |
| | 597 | })); |
| | 598 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | 599 | name: 'checkpoint-list', |
| | 600 | returns: 'JSON array of all existing checkpoints in this chat, as an array', |
| | 601 | /** @param {{links?: string}} args @returns {Promise<string>} */ |
| | 602 | callback: async (args, _) => { |
| | 603 | const result = []; |
| | 604 | for (const mesId in chat) { |
| | 605 | if (chat[mesId].extra?.bookmark_link) { |
| | 606 | result.push(args.links ? chat[mesId].extra.bookmark_link : Number(mesId)); |
| | 607 | } |
| | 608 | } |
| | 609 | return JSON.stringify(result); |
| | 610 | }, |
| | 611 | namedArgumentList: [ |
| | 612 | SlashCommandNamedArgument.fromProps({ |
| | 613 | name: 'links', |
| | 614 | description: 'Get a list of all links / chat names of the checkpoints, instead of the message ids', |
| | 615 | typeList: [ARGUMENT_TYPE.BOOLEAN], |
| | 616 | enumList: commonEnumProviders.boolean('trueFalse')(), |
| | 617 | defaultValue: 'false', |
| | 618 | }), |
| | 619 | ], |
| | 620 | helpString: ` |
| | 621 | <div> |
| | 622 | List all existing checkpoints in this chat. |
| | 623 | </div> |
| | 624 | <div> |
| | 625 | Returns a list of all message ids that have a checkpoint, or all checkpoint links if <code>links</code> is set to <code>true</code>.<br /> |
| | 626 | The value will be a JSON array. |
| | 627 | </div>`, |
| | 628 | })); |
| | 629 | } |
| | 630 | |
| | 631 | export function initBookmarks() { |
| 381 | $('#option_new_bookmark').on('click', saveBookmarkMenu); | 632 | $('#option_new_bookmark').on('click', saveBookmarkMenu); |
| 382 | $('#option_back_to_main').on('click', backToMainChat); | 633 | $('#option_back_to_main').on('click', backToMainChat); |
| 383 | $('#option_convert_to_group').on('click', convertSoloToGroupChat); | 634 | $('#option_convert_to_group').on('click', convertSoloToGroupChat); |