【ChatGPT with Date】使用 ChatGPT 时显示消息时间的插件

news2024/9/26 3:25:58

文章目录

    • 1. 介绍
    • 2. 使用方法
      • 2.1 安装 Tampermonkey
      • 2.2 安装脚本
      • 2.3 使用
    • 3. 配置
      • 3.1 时间格式
      • 3.2 时间位置
      • 3.3 高级配置
        • (1) 生命周期钩子函数
        • (2) 示例
    • 4. 反馈
    • 5. 未来计划
    • 6. 开源协议
    • 7. 供给开发者自定义修改脚本的文档
      • 7.1 项目组织架构
      • 7.2 定义新的 Component
        • (1) 定义一个新的 Component 类
        • (2) 注册该 Component
      • 7.3 一些建议
        • (1) 注入外部 JavaScript 库
    • X. Changelog


1. 介绍

有时我们希望看到 ChatGPT 的消息时间,但 ChatGPT 并没有显示消息时间的功能。
本项目通过 Tampermonkey 开发,可以在 Chrome 和 Firefox 等支持 Tampermonkey 插件的浏览器上使用,实现显示 ChatGPT
每一条消息时间的功能。

该插件不但可以获取以往的消息时间,还可以实时获取新消息的时间。

在交互时添加时间标签

提供多种配置选项,例如时间显示格式、时间显示位置等。

配置面板高级使用

如果你了解网页三剑客(HTML、CSS、JavaScript),你完全可以高度自定义时间样式。

配置面板高级使用

我们将在第三节介绍如上图所示的配置以及更多的规则。

2. 使用方法

2.1 安装 Tampermonkey

可查看 Tampermonkey 首页 查看详细的使用方法。

2.2 安装脚本

访问链接: Greasy Fork - ChatGPT with Date,点击 安装此脚本 安装脚本。

2.3 使用

首次使用请允许跨源资源共享(CORS)请求,本项目将请求 Vue.js 和 NaiveUI 的资源,以便生成配置面板。

允许跨源资源共享请求

打开 ChatGPT 页面,即可看到消息时间。你可以在此处打开配置面板。

配置面板打开引导

3. 配置

3.1 时间格式

支持的时间元素有(以 2024年4月3日18点9分1秒999毫秒周五 为例)

元素介绍示例
{yyyy}四位的年份2024
{yy}两位的年份24
{MM}, {MM:02}至少两位的月份04
{MM:01}至少一位的月份4
{MM#name@zh}中文月份
{MM#name@en}, {MM#fullname@en}英文月份April
{MM#shortname@en}英文缩写月份Apr
{dd}, {dd:02}当前月已过天数03
{dd:01}当前月已过天数3
{HH}, {HH:02}, {HH#24}, {HH#24:02}小数18
{HH:01}, {HH#24:01}小时18
{HH#12}, {HH#12:02}12小时制小时06
{HH#12:01}12小时制小时6
{HH#tag}, {HH#tag@en}上下午PM
{HH#tag@zh}上下午下午
{mm}, {mm:02}分钟09
{mm:01}分钟9
{ss}, {ss:02}秒数01
{ss:01}秒数1
{ms}毫秒999
{week}, {week:02}星期05
{week:01}星期5
{week#name@zh}中文星期
{week#name@en}, {week#fullname@en}英文星期Friday
{week#shortname@en}英文缩写星期Fri

3.2 时间位置

时间位置有三种选择:

  • 角色之后(靠左): 时间显示在角色名字的后面,并靠左显示。
  • 角色之后(靠右): 时间显示在角色名字的后面,并靠右显示。
  • 角色之下: 时间显示在角色名字下方。

3.3 高级配置

高级配置提供了更多的自定义选项,你可以自定义时间标签的 HTML、CSS、JavaScript 代码。

(1) 生命周期钩子函数

本项目提供了两个生命周期钩子函数,分别是 window.beforeCreateTimeTag(messageId, timeTagHTML)window.afterCreateTimeTag(messageId, timeTagNode)

  • window.beforeCreateTimeTag(messageId, timeTagHTML): 在创建时间标签之前调用,你可以在这里修改时间标签的 HTML 内容。
    • messageId: 消息的 ID。
    • timeTagHTML: 时间标签的 HTML 内容。
  • window.afterCreateTimeTag(messageId, timeTagNode): 在创建时间标签之后调用,你可以在这里修改时间标签的 DOM 节点。
    • messageId: 消息的 ID。
    • timeTagNode: 时间标签的 DOM 节点。
(2) 示例

在介绍章节中,我们展示了如何使用高级配置功能来显示一个可以将日期显示为几天前的时间标签,下面是具体的代码:

<div class="text-tag-box">
    <span class="date">{yyyy}-{MM}-{dd}</span>
    <span class="time">{HH}:{mm}:{ss}</span>
</div>
.text-tag-box {
    border-radius: 8px;
    color: #E0E0E0;
    font-size: 0.9em;
    overflow: hidden;
    display: inline-block;
}

.text-tag-box .date {
    background: #333;
    float: left;
    padding: 2px 8px 2px 10px;
    display: inline-block;
    transition: width 0.5s ease-out;
    white-space: nowrap;
}

.text-tag-box .time {
    background: #606060;
    float: left;
    padding: 2px 10px 2px 8px;
    display: inline-block;
}
// 建议将以下代码放在一个 IIFE 中,以避免全局变量污染
(() => {
    const getNewWidth = (targetNode, text) => {
        // 创建一个临时元素来测量文本宽度
        const temp = targetNode.cloneNode();
        temp.style.width = 'auto'; // 自动宽度
        temp.style.visibility = 'hidden'; // 隐藏元素,不影响布局
        temp.style.position = 'absolute'; // 避免影响其他元素
        temp.style.whiteSpace = 'nowrap'; // 无换行
        temp.innerText = text;
        document.body.appendChild(temp);
        const newWidth = temp.offsetWidth;
        document.body.removeChild(temp);
        return newWidth;
    }

    window.afterCreateTimeTag = (messageId, timeTagNode) => {
        const dateNode = timeTagNode.querySelector('.date');
        const date = dateNode.innerText;
        const originalWidth = getNewWidth(dateNode, date);
        const paddingWidth = 18;
        dateNode.style.width = `${originalWidth + paddingWidth}px`;

        timeTagNode.addEventListener('mouseover', () => {
            const now = new Date();
            const offset = now - new Date(date);
            const days = Math.floor(offset / (24 * 60 * 60 * 1000));
            let text = '';
            if (days < 1)
                text = '今天';
            else if (days < 2)
                text = '昨天';
            else if (days < 3)
                text = '前天';
            else if (days < 7)
                text = `${days}天前`;
            else if (days < 30)
                text = `${Math.floor(days / 7)}周前`;
            else if (days < 365)
                text = `${Math.floor(days / 30)}个月前`;
            else
                text = `${Math.floor(days / 365)}年前`;
            dateNode.innerText = text;
            dateNode.style.width = `${getNewWidth(dateNode, text) + paddingWidth}px`
        });

        // 鼠标移出 timeTagNode 时恢复 dateNode 的内容为原来的日期
        timeTagNode.addEventListener('mouseout', () => {
            dateNode.innerText = date;
            dateNode.style.width = `${originalWidth + paddingWidth}px`
        });
    }
})()

4. 反馈

如果你有任何问题或建议,欢迎在 GitHub Issues 或 脚本反馈区 中提出。

5. 未来计划

  • 国际化:脚本支持多种语言(日志、提示等)。
  • 时间格式化细粒度配置面板:优化时间格式自定义功能,而不是难以维护的 HTML字符串 表示。
  • 时间格式化元素:支持更多的时间格式化元素,例如星期、月份(英文)等。
  • 时间格式化规则:支持更多的时间格式化规则,例如 12 小时制、24 小时制等。
  • 支持分享的界面:支持显示 https://chat.openai.com/share/uuid 的界面(即分享的聊天界面)的时间。
  • 主题网站:提供一个主题网站,展示用户分享的时间标签主题。
  • 重置脚本:由于会将用户输入的内容应用到本地导致奔溃,提供重置脚本的功能。
  • 提供更多的生命周期钩子函数和自定义函数:例如可以自定义时间元素如何解析。

6. 开源协议

本项目遵循 MIT 开源协议。

CopyRight © 2024~Present Jiang Liu

7. 供给开发者自定义修改脚本的文档

7.1 项目组织架构

源代码总览

本项目采用依赖注入(DI)的方式组织各个 Component,主要包括以下几个部分:

  • UserConfig: 用户配置信息,包括时间格式、时间位置等。
  • StyleService: 样式服务,负责跟踪管理一些可变化的样式。
  • MessageService: 消息服务,负责管理和存储消息信息。
  • MonitorService: 监控服务,负责劫持 Fetch 请求和实时监听页面新消息的添加。
  • TimeRendererService: 时间渲染服务,负责渲染时间。
  • ConfigPanelService: 配置面板服务,负责生成基于 Vue+NaiveUI 的配置面板。

上述服务之间的依赖关系如下:

UserConfig
StyleService
MessageService
MonitorService
TimeRendererService
ConfigPanelService

本项目采用的依赖注入方式限制了循环依赖的发生,依赖注入的设计架构允许组件保持独立于其依赖项的具体实现。这种技术可以使代码更容易理解、维护和测试。之所以要避免循环依赖,因为循环依赖违反了单一职责原则、依赖倒置原则。

7.2 定义新的 Component

如果你想自定义一个新的 Component,你需要:

(1) 定义一个新的 Component 类
  • 继承:该类必须继承自 Component
  • 构造函数:在构造函数中定义 dependencies 属性,该属性是一个数组,数组中的每个元素是一个对象,对象包含两个属性:field
    clazz,分别表示依赖的字段名和依赖的类。
  • 依赖注入:不应该在构造函数中进行除了定义依赖关系之外的其他操作,因为依赖关系的注入是在 Component::initDependencies
    方法中进行的。在该方法执行之前,依赖关系是无法使用的。
  • 初始化函数:在 init 方法中进行正式的初始化操作,在该方法中可以使用依赖注入的属性。
  • 依赖关系:应当避免循环依赖,否则会导致初始化失败。
class NewComponent extends Component {
    constructor() {
        super();
        this.userConfig = null
        this.timeRendererService = null
        this.messageService = null
        this.dependencies = [
            {field: 'userConfig', clazz: UserConfig},
            {field: 'timeRendererService', clazz: TimeRendererService},
            {field: 'messageService', clazz: MessageService},
        ]
    }

    init() {
        // 正式初始化
    }
}
(2) 注册该 Component

将你的 Component 注册到 Main.ComponentsConfig 中,不必担心注册的顺序,因为 Main 类会自动按照依赖关系的顺序进行初始化。

class Main {
    static ComponentsConfig = [
        UserConfig, StyleService, MessageService,
        MonitorService, TimeRendererService, ConfigPanelService,
        NewComponent,
    ]
}

于是新的依赖关系图如下所示:

UserConfig
StyleService
MessageService
MonitorService
TimeRendererService
ConfigPanelService
NewComponent

7.3 一些建议

(1) 注入外部 JavaScript 库

请使用 GM_xmlhttpRequest 来获取外部 JavaScript 库的内容,再以字符串的形式注入到页面中。
如果你直接在页面中引入外部 JavaScript 库,可能有CSP限制。
下面是在项目中注入 Vue.js 和 NaiveUI 的示例:

function loadScript() {
    return new Promise(resolve => {
        let completeCount = 0;
        const resources = [
            {type: 'js', url: 'https://unpkg.com/vue@3.4.26/dist/vue.global.js'},
            {type: 'js', url: 'https://unpkg.com/naive-ui@2.38.1/dist/index.js'},
        ]
        const addScript = (content) => {
            let script = document.createElement('script');
            script.textContent = content;
            document.body.appendChild(script);
            completeCount++;
            if (completeCount === resources.length) {
                resolve()
            }
        }
        resources.forEach(resource => {
            GM_xmlhttpRequest({
                method: "GET", url: resource.url, onload: function (response) {
                    addScript(response.responseText);
                }
            });
        })
        // 以下方法有 CSP 限制
        // const naiveScript = document.createElement('script');
        // naiveScript.setAttribute("type", "text/javascript");
        // naiveScript.text = "https://unpkg.com/naive-ui@2.38.1/dist/index.js";
        // document.documentElement.appendChild(naiveScript);
    })
}

X. Changelog

  • v1.2.0 - 2024-05-03 21:26:43
    • 优化:限制每次渲染时间标签的次数以及总时长,避免页面卡顿
    • 优化:设置时间标签渲染函数异步执行,避免阻塞页面渲染
    • 优化:修改 Fetch 劫持 URL 匹配规则,更加精确以免干扰其他请求。并在 URL 匹配成功时才进行具体的劫持操作
    • 优化:选择模板时直接显示时间格式的示例,而不是冰冷的模板HTML字符串
    • 新功能:添加更多时间格式的元素,例如星期、月份(英文)等
    • 新功能:添加更多时间格式化规则,例如 12 小时制、24 小时制等
    • 新功能:提供自定义样式的 HTML、CSS、JavaScript 的代码编辑器与注入系统
    • 新功能:提供创建时间标签的生命周期钩子函数 window.beforeCreateTimeTag(messageId, timeTagHTML)
      window.afterCreateTimeTag(messageId, timeTagNode)
  • v1.1.0 - 2024-05-02 17:50:04
    • 添加更多时间格式的模板

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1644383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue2——前端笔记

Vue 一、Vue核心1.1、vue简介1.2、初始vue1.3、模板语法1.4、数据绑定1.5、el与data的两种写法1.6、MVVM模型1.7、Vue中的数据代理1.7.1、Object.defineProperty() 理解1.7.2、Vue中的数据代理 1.8、事件处理1.8.1、事件的基本用法1.8.2、事件修饰符1.8.3、键盘事件 1.9、计算属…

电商中文场景多模态测试prompt

魔搭社区汇聚各领域最先进的机器学习模型&#xff0c;提供模型探索体验、推理、训练、部署和应用的一站式服务。https://www.modelscope.cn/datasets 多模态大模型Yi-VL-plus体验 效果很棒 - 知乎最近测了一下零一万物的多模态大模型Yi-VL-plus的效果&#xff0c;发现多模态理解…

CNN实现卫星图像分类(tensorflow)

使用的数据集卫星图像有两类&#xff0c;airplane和lake&#xff0c;每个类别样本量各700张&#xff0c;大小为256*256&#xff0c;RGB三通道彩色卫星影像。搭建深度卷积神经网络&#xff0c;实现卫星影像二分类。 数据链接百度网盘地址&#xff0c;提取码: cq47 1、查看tenso…

【一刷《剑指Offer》】面试题 14:调整数组顺序使奇数位于偶数前面

力扣对应题目链接&#xff1a;LCR 139. 训练计划 I - 力扣&#xff08;LeetCode&#xff09; 牛客对应题目链接&#xff1a;调整数组顺序使奇数位于偶数前面(二)_牛客题霸_牛客网 (nowcoder.com) 核心考点&#xff1a;数组操作&#xff0c;排序思想的扩展使用。 一、《剑指Off…

LAME及 iOS 编译

文章目录 关于 LAME编译 for iOS 关于 LAME 官网&#xff1a;https://lame.sourceforge.io LAME是根据LGPL许可的高质量MPEG音频层III&#xff08;MP3&#xff09;编码器。 LAME的开发始于1998年年中左右。Mike Cheng 最开始将它作为针对8hz-MP3编码器源的补丁。在其他人提出…

docker-本地私有仓库、harbor私有仓库部署与管理

一、本地私有仓库&#xff1a; 1、本地私有仓库简介&#xff1a; docker本地仓库&#xff0c;存放镜像&#xff0c;本地的机器上传和下载&#xff0c;pull/push。 使用私有仓库有许多优点&#xff1a; 节省网络带宽&#xff0c;针对于每个镜像不用每个人都去中央仓库上面去下…

实现 Trie (前缀树) - LeetCode 热题 54

大家好&#xff01;我是曾续缘&#x1f49c; 今天是《LeetCode 热题 100》系列 发车第 54 天 图论第 4 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 实现 Trie (前缀树) Trie&#xff08;发音类似 "try"&#xff09;或者说 前缀树 是一种树形数据结构…

C#知识|上位机项目主窗体设计思路及流程(实例)

哈喽,你好啊,我是雷工! 昨天练习了登录窗体的设计实现,今天练习上位机项目主窗体的设计实现。 01 主窗体效果展示 02 实现步骤 2.1、添加主窗体 添加窗体,名称:FrmMain.cs 2.2、窗体属性设置 将FrmMain窗体属性FormBorderStyle设置为None,无边框; 将FrmMain窗体属性…

神经网络中的算法优化(皮毛讲解)

抛砖引玉 在深度学习中&#xff0c;优化算法是训练神经网络时至关重要的一部分。 优化算法的目标是最小化&#xff08;或最大化&#xff09;一个损失函数&#xff0c;通常通过调整神经网络的参数来实现。 这个过程可以通过梯度下降法来完成&#xff0c;其中梯度指的是损失函数…

Windows查找JDK的安装路径

如果很久之前安装了JDK&#xff0c;或者在别人的电脑上&#xff0c;想要快速指导JDK 的安装路径&#xff0c;可以通过啥方式指导JDK的安装路径是在哪里呢&#xff1f; 一、确认是否安装了JDK 首先我们打开命令行&#xff0c;如果输入 java -version 如果显示这种&#xff0c;…

IBM收购HashiCorp:开源工具的未来与“好软件的坟墓”

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

VBA 创建透视表,录制宏,自动化报表

目录 一. 数据准备二. 需求三. 准备好报表模板四. 执行统计操作&#xff0c;录制宏4.1 根据数据源创建透视表4.2 填充数据到报表4.3 结束宏录制 五. 执行录制好的宏&#xff0c;自动化报表 一. 数据准备 ⏹数据源1 姓名学科成绩丁志敏语文91李平平语文81王刚语文64张伊语文50…

场景文本检测识别学习 day08(无监督的Loss Function、代理任务、特征金字塔)

无监督的Loss Function&#xff08;无监督的目标函数&#xff09; 根据有无标签&#xff0c;可以将模型的学习方法分为&#xff1a;无监督、有监督两种。而自监督是无监督的一种无监督的目标函数可以分为以下几种&#xff1a; 生成式网络的做法&#xff0c;衡量模型的输出和固…

【C++STL详解(六)】--------list的模拟实现

目录 前言 一、接口总览 一、节点类的模拟实现 二、迭代器类的模拟实现 迭代器的目的 list迭代器为何要写成类&#xff1f; 迭代器类模板参数说明 模拟实现 1.构造函数 2.*运算符重载 3.->运算符重载 4.前置 5.后置 6.前置-- 7.后置-- 8.! 9. 三、list类的…

【知识加油站】——机电产品数字孪生机理模型构建

明确一种多领域、多层次、参数化、一致性的机电一体化装备数字孪生机理模型构建准则&#xff01; 关键词英文简称&#xff1a; 数字孪生&#xff1a;DT物联网&#xff1a;IoT网络物理系统&#xff1a;CPS高级架构&#xff1a;HLA统一建模语言&#xff1a;UML数控机床&#xf…

2-qt之信号与槽-简单实例讲解

前言、因实践课程讲解需求&#xff0c;简单介绍下qt的信号与槽。 一、了解信号与槽 怎样使用信号与槽&#xff1f; 概览 还记得 X-Window 上老旧的回调函数系统吗&#xff1f;通常它不是类型安全的并且很复杂。&#xff08;使用&#xff09;它&#xff08;会&#xff09;有很多…

精析React与Vue架构异同及React核心技术——涵盖JSX、组件、Props、State、生命周期与16.8版后Hooks深化解析

React&#xff0c;Facebook开源的JavaScript库&#xff0c;用于构建高性能用户界面。通过组件化开发&#xff0c;它使UI的构建、维护变得简单高效。利用虚拟DOM实现快速渲染更新&#xff0c;适用于单页应用、移动应用&#xff08;React Native&#xff09;。React极大推动了现代…

【链表】:链表的带环问题

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;数据结构 &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 链表的带环问题在链表中是一类比较难的问题&#xff0c;它对我们的思维有一个比较高的要求&#xff0c;但是这一类…

51单片机入门:DS1302时钟

51单片机内部含有晶振&#xff0c;可以实现定时/计数功能。但是其缺点有&#xff1a;精度往往不高、不能掉电使用等。 我们可以通过DS1302时钟芯片来解决以上的缺点。 DS1302时钟芯片 功能&#xff1a;DS1302是一种低功耗实时时钟芯片&#xff0c;内部有自动的计时功能&#x…

Spring Boot:国际化

Spring Boot 前言国际化 前言 在 Spring MVC&#xff1a;视图与视图解析器 的文章中&#xff0c;介绍过使用 Jstl 的 fmt 标签实现国际化&#xff0c;Spring MVC 会把视图由 InternalResourceViewResolver 转换为 JstlView&#xff08;InternalResourceView 的子类&#xff09…