GITLENS自编译版本,解锁本地功能
- 内容介绍
- 文章标签
- 相关推荐
GITLENS自编译版本
原因
学生优惠从原来期限内永久,变成有限期内 pro 计划,加后续订阅折扣
于是打算依据源码自行构建
准备工作
拉取仓库https://github.com/gitkraken/vscode-gitlens.git
修改
修改文件src/plus/gk/utils/subscription.utils.ts
主要目标是computeSubscriptionState函数,其他随意,按需修改
import { SubscriptionState } from '../../../constants.subscription';
import { getTimeRemaining } from '../../../system/date';
import type {
PaidSubscriptionPlanIds,
Subscription,
SubscriptionPlan,
SubscriptionPlanIds,
SubscriptionStateString,
} from '../models/subscription';
const orderedPlans: SubscriptionPlanIds[] = [
'community',
'community-with-account',
'student',
'pro',
'advanced',
'teams',
'enterprise',
];
const orderedPaidPlans: PaidSubscriptionPlanIds[] = ['student', 'pro', 'advanced', 'teams', 'enterprise'];
export const SubscriptionUpdatedUriPathPrefix = 'did-update-subscription';
export const AiAllAccessOptInPathPrefix = 'ai-all-access-opt-in';
export function compareSubscriptionPlans(
planA: SubscriptionPlanIds | undefined,
planB: SubscriptionPlanIds | undefined,
): number {
return getSubscriptionPlanOrder(planA) - getSubscriptionPlanOrder(planB);
}
export function computeSubscriptionState(subscription: Optional<Subscription, 'state'>): SubscriptionState {
return SubscriptionState.Paid;
}
export function getSubscriptionNextPaidPlanId(subscription: Optional<Subscription, 'state'>): PaidSubscriptionPlanIds {
let next = orderedPaidPlans.indexOf(subscription.plan.actual.id as PaidSubscriptionPlanIds) + 1;
// Skip the student plan since we cannot determine if the user is student-eligible or not
if (next === 0) {
next++;
}
if (next >= orderedPaidPlans.length) return 'enterprise'; // Not sure what to do here
return orderedPaidPlans[next] ?? 'pro';
}
export function getSubscriptionPlan(
id: SubscriptionPlanIds,
bundle: boolean,
trialReactivationCount: number,
organizationId: string | undefined,
startedOn?: Date,
expiresOn?: Date,
cancelled: boolean = false,
nextTrialOptInDate?: string,
): SubscriptionPlan {
return {
id: id,
name: getSubscriptionProductPlanName(id),
bundle: bundle,
cancelled: cancelled,
organizationId: organizationId,
trialReactivationCount: trialReactivationCount,
nextTrialOptInDate: nextTrialOptInDate,
startedOn: (startedOn ?? new Date()).toISOString(),
expiresOn: expiresOn != null ? expiresOn.toISOString() : undefined,
};
}
/** Gets the plan name for the given plan id */
export function getSubscriptionPlanName(
id: SubscriptionPlanIds,
): 'Community' | 'Student' | 'Pro' | 'Advanced' | 'Business' | 'Enterprise' {
return 'Enterprise';
}
export function getSubscriptionPlanOrder(id: SubscriptionPlanIds | undefined): number {
return id != null ? orderedPlans.indexOf(id) : -1;
}
/** Only for gk.dev `planType` query param */
export function getSubscriptionPlanType(
id: SubscriptionPlanIds,
): 'STUDENT' | 'PRO' | 'ADVANCED' | 'BUSINESS' | 'ENTERPRISE' {
return 'ENTERPRISE';
}
/** Gets the "product" (fully qualified) plan name for the given plan id */
export function getSubscriptionProductPlanName(id: SubscriptionPlanIds): string {
return `GitLens ${getSubscriptionPlanName(id)}`;
}
/** Gets the "product" (fully qualified) plan name for the given subscription state */
export function getSubscriptionProductPlanNameFromState(
state: SubscriptionState,
planId?: SubscriptionPlanIds,
effectivePlanId?: SubscriptionPlanIds,
): string {
return getSubscriptionProductPlanName('enterprise');
}
export function getSubscriptionStateString(state: SubscriptionState | undefined): SubscriptionStateString {
return 'paid';
}
export function getSubscriptionTimeRemaining(
subscription: Optional<Subscription, 'state'>,
unit?: 'days' | 'hours' | 'minutes' | 'seconds',
): number | undefined {
return getTimeRemaining(subscription.plan.effective.expiresOn, unit);
}
export function isSubscriptionPaid(subscription: Optional<Subscription, 'state'>): boolean {
return isSubscriptionPaidPlan(subscription.plan.actual.id);
}
export function isSubscriptionPaidPlan(id: SubscriptionPlanIds): id is PaidSubscriptionPlanIds {
return orderedPaidPlans.includes(id as PaidSubscriptionPlanIds);
}
export function isSubscriptionExpired(subscription: Optional<Subscription, 'state'>): boolean {
const remaining = getSubscriptionTimeRemaining(subscription);
return remaining != null && remaining <= 0;
}
export function isSubscriptionTrial(subscription: Optional<Subscription, 'state'>): boolean {
if (subscription.state != null) {
return subscription.state === SubscriptionState.Trial;
}
return subscription.plan.actual.id !== subscription.plan.effective.id;
}
export function isSubscriptionTrialOrPaidFromState(state: SubscriptionState | undefined): boolean {
return state != null ? state === SubscriptionState.Trial || state === SubscriptionState.Paid : false;
}
export function assertSubscriptionState(
subscription: Optional<Subscription, 'state'>,
): asserts subscription is Subscription {}
export function getCommunitySubscription(subscription?: Subscription): Subscription {
return {
...subscription,
plan: {
actual: getSubscriptionPlan(
'enterprise',
false,
0,
undefined,
subscription?.plan?.actual?.startedOn != null
? new Date(subscription.plan.actual.startedOn)
: undefined,
),
effective: getSubscriptionPlan(
'enterprise',
false,
0,
undefined,
subscription?.plan?.actual?.startedOn != null
? new Date(subscription.plan.actual.startedOn)
: undefined,
),
},
account: undefined,
activeOrganization: undefined,
state: SubscriptionState.Community,
};
}
如此便可本地使用所有功能,若需登录账号,请修改 src/plus/gk/utils/checkin.utils.ts下getSubscriptionFromCheckIn函数的返回值构造自定义的订阅计划
以下为一个参考案例
const enterprisePlan = getSubscriptionPlan(
'enterprise',
false,
0,
undefined,
data.user.firstGitLensCheckIn != null
? new Date(data.user.firstGitLensCheckIn)
: data.user.createdDate != null
? new Date(data.user.createdDate)
: undefined,
undefined,
undefined,
data.nextOptInDate,
);
这样可以确保在登录后的界面 ui 显示,当然也有别的方法,请自行研究。
构建
运行命令pnpm install & pnpm package
随后安装 vsix
结果展示
1596×334 46.5 KB
21920×1098 258 KB
注意
本帖内容请勿用于商业用途,仅供学习参考
详情参考
LICENSE.plus
main
GitLens Pro License
Copyright (c) 2021-2026 Axosoft, LLC dba GitKraken ("GitKraken")
With regard to the software set forth in or under any directory named "plus".
This software and associated documentation files (the "Software") may be
compiled as part of the gitkraken/vscode-gitlens open source project (the
"GitLens") to the extent the Software is a required component of the GitLens;
provided, however, that the Software and its functionality may only be used if
you (and any entity that you represent) have agreed to, and are in compliance
with, the GitKraken End User License Agreement, available at
https://gitkraken.com/eula (the "EULA"), or other agreement governing the
use of the Software, as agreed by you and GitKraken, and otherwise have a valid
subscription for the correct number of user seats for the applicable version of
the Software (e.g., GitLens Pro, GitLens Advanced, GitLens Business,
and GitLens Enterprise). Subject to the foregoing sentence, you are free to modify
this Software and publish patches to the Software. You agree that GitKraken and/or
its licensors (as applicable) retain all right, title and interest in and to all
such modifications and/or patches, and all such modifications and/or patches may
此文件已被截断。 显示原始文件
附件
https://fuan.lanzoul.com/ivC5d3e963kb
网友解答:--【壹】--:
学习了,这就整一下
--【贰】--:
感谢佬 mark一下
--【叁】--:
太强了
--【肆】--:
感谢分享~
--【伍】--:
用的是最新主分支
--【陆】--:
image1512×530 47.4 KB
--【柒】--:
佬友太强了
--【捌】--:
改了但是gitlen的面板会有层蒙版 还要研究一下
--【玖】--:
太强了佬! 感谢分享!
--【拾】--:
感谢佬,CY
--【拾壹】--:
太强了 学习了
--【拾贰】--:
大佬,太强了,学不会
--【拾叁】--:
用起来了
--【拾肆】--:
佬,我有点菜,刚刚 google 了下,还是不太清楚 vsix 是啥意思,跪求解释下
--【拾伍】--:
麻烦给个截图,我测试没问题 疑惑.jpg
安装前记得清空下本地许可证
--【拾陆】--:
太神奇了,就这么能用了
--【拾柒】--:
什么是 Visual Studio VSIX 包文件? - Visual Studio (Windows)
浏览 Visual Studio 中 VSIX 包文件的内容,该文件包含一个或多个 Visual Studio 扩展和元数据清单文件。
看微软文档吧,在 vscode 的环境下,就认为是插件的安装包好了
运行命令后会在项目目录下生产对应版本的插件打包产物,后缀为 vsix,以 v17.8.1为例,产品为 gitlens-17.8.1.vsix
--【拾捌】--:
这题我会
# cmd+p feature-gate.ts
# 修改此文件中的css样式
@customElement('gl-feature-gate')
export class GlFeatureGate extends LitElement {
static override styles = [
linkStyles,
css`
:host {
...
/* 移除这段覆盖整个界面的样式 */
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: auto;
z-index: 100; */
/* 添加这一行 */
display: none;
}
:host-context(body[data-placement='editor']),
:host([appearance='alert']) {
...
/* 删除 */
/* backdrop-filter: blur(3px) saturate(0.8); */
...
/* 添加一行 */
display: none;
}
...
`,
];
}
或者更简单粗暴
# 修改 gate.ts 和 feature-gate.ts 返回值为 undefine
override render(): unknown {
// 删除原有代码
return undefine;
}
--【拾玖】--:
太强了佬
GITLENS自编译版本
原因
学生优惠从原来期限内永久,变成有限期内 pro 计划,加后续订阅折扣
于是打算依据源码自行构建
准备工作
拉取仓库https://github.com/gitkraken/vscode-gitlens.git
修改
修改文件src/plus/gk/utils/subscription.utils.ts
主要目标是computeSubscriptionState函数,其他随意,按需修改
import { SubscriptionState } from '../../../constants.subscription';
import { getTimeRemaining } from '../../../system/date';
import type {
PaidSubscriptionPlanIds,
Subscription,
SubscriptionPlan,
SubscriptionPlanIds,
SubscriptionStateString,
} from '../models/subscription';
const orderedPlans: SubscriptionPlanIds[] = [
'community',
'community-with-account',
'student',
'pro',
'advanced',
'teams',
'enterprise',
];
const orderedPaidPlans: PaidSubscriptionPlanIds[] = ['student', 'pro', 'advanced', 'teams', 'enterprise'];
export const SubscriptionUpdatedUriPathPrefix = 'did-update-subscription';
export const AiAllAccessOptInPathPrefix = 'ai-all-access-opt-in';
export function compareSubscriptionPlans(
planA: SubscriptionPlanIds | undefined,
planB: SubscriptionPlanIds | undefined,
): number {
return getSubscriptionPlanOrder(planA) - getSubscriptionPlanOrder(planB);
}
export function computeSubscriptionState(subscription: Optional<Subscription, 'state'>): SubscriptionState {
return SubscriptionState.Paid;
}
export function getSubscriptionNextPaidPlanId(subscription: Optional<Subscription, 'state'>): PaidSubscriptionPlanIds {
let next = orderedPaidPlans.indexOf(subscription.plan.actual.id as PaidSubscriptionPlanIds) + 1;
// Skip the student plan since we cannot determine if the user is student-eligible or not
if (next === 0) {
next++;
}
if (next >= orderedPaidPlans.length) return 'enterprise'; // Not sure what to do here
return orderedPaidPlans[next] ?? 'pro';
}
export function getSubscriptionPlan(
id: SubscriptionPlanIds,
bundle: boolean,
trialReactivationCount: number,
organizationId: string | undefined,
startedOn?: Date,
expiresOn?: Date,
cancelled: boolean = false,
nextTrialOptInDate?: string,
): SubscriptionPlan {
return {
id: id,
name: getSubscriptionProductPlanName(id),
bundle: bundle,
cancelled: cancelled,
organizationId: organizationId,
trialReactivationCount: trialReactivationCount,
nextTrialOptInDate: nextTrialOptInDate,
startedOn: (startedOn ?? new Date()).toISOString(),
expiresOn: expiresOn != null ? expiresOn.toISOString() : undefined,
};
}
/** Gets the plan name for the given plan id */
export function getSubscriptionPlanName(
id: SubscriptionPlanIds,
): 'Community' | 'Student' | 'Pro' | 'Advanced' | 'Business' | 'Enterprise' {
return 'Enterprise';
}
export function getSubscriptionPlanOrder(id: SubscriptionPlanIds | undefined): number {
return id != null ? orderedPlans.indexOf(id) : -1;
}
/** Only for gk.dev `planType` query param */
export function getSubscriptionPlanType(
id: SubscriptionPlanIds,
): 'STUDENT' | 'PRO' | 'ADVANCED' | 'BUSINESS' | 'ENTERPRISE' {
return 'ENTERPRISE';
}
/** Gets the "product" (fully qualified) plan name for the given plan id */
export function getSubscriptionProductPlanName(id: SubscriptionPlanIds): string {
return `GitLens ${getSubscriptionPlanName(id)}`;
}
/** Gets the "product" (fully qualified) plan name for the given subscription state */
export function getSubscriptionProductPlanNameFromState(
state: SubscriptionState,
planId?: SubscriptionPlanIds,
effectivePlanId?: SubscriptionPlanIds,
): string {
return getSubscriptionProductPlanName('enterprise');
}
export function getSubscriptionStateString(state: SubscriptionState | undefined): SubscriptionStateString {
return 'paid';
}
export function getSubscriptionTimeRemaining(
subscription: Optional<Subscription, 'state'>,
unit?: 'days' | 'hours' | 'minutes' | 'seconds',
): number | undefined {
return getTimeRemaining(subscription.plan.effective.expiresOn, unit);
}
export function isSubscriptionPaid(subscription: Optional<Subscription, 'state'>): boolean {
return isSubscriptionPaidPlan(subscription.plan.actual.id);
}
export function isSubscriptionPaidPlan(id: SubscriptionPlanIds): id is PaidSubscriptionPlanIds {
return orderedPaidPlans.includes(id as PaidSubscriptionPlanIds);
}
export function isSubscriptionExpired(subscription: Optional<Subscription, 'state'>): boolean {
const remaining = getSubscriptionTimeRemaining(subscription);
return remaining != null && remaining <= 0;
}
export function isSubscriptionTrial(subscription: Optional<Subscription, 'state'>): boolean {
if (subscription.state != null) {
return subscription.state === SubscriptionState.Trial;
}
return subscription.plan.actual.id !== subscription.plan.effective.id;
}
export function isSubscriptionTrialOrPaidFromState(state: SubscriptionState | undefined): boolean {
return state != null ? state === SubscriptionState.Trial || state === SubscriptionState.Paid : false;
}
export function assertSubscriptionState(
subscription: Optional<Subscription, 'state'>,
): asserts subscription is Subscription {}
export function getCommunitySubscription(subscription?: Subscription): Subscription {
return {
...subscription,
plan: {
actual: getSubscriptionPlan(
'enterprise',
false,
0,
undefined,
subscription?.plan?.actual?.startedOn != null
? new Date(subscription.plan.actual.startedOn)
: undefined,
),
effective: getSubscriptionPlan(
'enterprise',
false,
0,
undefined,
subscription?.plan?.actual?.startedOn != null
? new Date(subscription.plan.actual.startedOn)
: undefined,
),
},
account: undefined,
activeOrganization: undefined,
state: SubscriptionState.Community,
};
}
如此便可本地使用所有功能,若需登录账号,请修改 src/plus/gk/utils/checkin.utils.ts下getSubscriptionFromCheckIn函数的返回值构造自定义的订阅计划
以下为一个参考案例
const enterprisePlan = getSubscriptionPlan(
'enterprise',
false,
0,
undefined,
data.user.firstGitLensCheckIn != null
? new Date(data.user.firstGitLensCheckIn)
: data.user.createdDate != null
? new Date(data.user.createdDate)
: undefined,
undefined,
undefined,
data.nextOptInDate,
);
这样可以确保在登录后的界面 ui 显示,当然也有别的方法,请自行研究。
构建
运行命令pnpm install & pnpm package
随后安装 vsix
结果展示
1596×334 46.5 KB
21920×1098 258 KB
注意
本帖内容请勿用于商业用途,仅供学习参考
详情参考
LICENSE.plus
main
GitLens Pro License
Copyright (c) 2021-2026 Axosoft, LLC dba GitKraken ("GitKraken")
With regard to the software set forth in or under any directory named "plus".
This software and associated documentation files (the "Software") may be
compiled as part of the gitkraken/vscode-gitlens open source project (the
"GitLens") to the extent the Software is a required component of the GitLens;
provided, however, that the Software and its functionality may only be used if
you (and any entity that you represent) have agreed to, and are in compliance
with, the GitKraken End User License Agreement, available at
https://gitkraken.com/eula (the "EULA"), or other agreement governing the
use of the Software, as agreed by you and GitKraken, and otherwise have a valid
subscription for the correct number of user seats for the applicable version of
the Software (e.g., GitLens Pro, GitLens Advanced, GitLens Business,
and GitLens Enterprise). Subject to the foregoing sentence, you are free to modify
this Software and publish patches to the Software. You agree that GitKraken and/or
its licensors (as applicable) retain all right, title and interest in and to all
such modifications and/or patches, and all such modifications and/or patches may
此文件已被截断。 显示原始文件
附件
https://fuan.lanzoul.com/ivC5d3e963kb
网友解答:--【壹】--:
学习了,这就整一下
--【贰】--:
感谢佬 mark一下
--【叁】--:
太强了
--【肆】--:
感谢分享~
--【伍】--:
用的是最新主分支
--【陆】--:
image1512×530 47.4 KB
--【柒】--:
佬友太强了
--【捌】--:
改了但是gitlen的面板会有层蒙版 还要研究一下
--【玖】--:
太强了佬! 感谢分享!
--【拾】--:
感谢佬,CY
--【拾壹】--:
太强了 学习了
--【拾贰】--:
大佬,太强了,学不会
--【拾叁】--:
用起来了
--【拾肆】--:
佬,我有点菜,刚刚 google 了下,还是不太清楚 vsix 是啥意思,跪求解释下
--【拾伍】--:
麻烦给个截图,我测试没问题 疑惑.jpg
安装前记得清空下本地许可证
--【拾陆】--:
太神奇了,就这么能用了
--【拾柒】--:
什么是 Visual Studio VSIX 包文件? - Visual Studio (Windows)
浏览 Visual Studio 中 VSIX 包文件的内容,该文件包含一个或多个 Visual Studio 扩展和元数据清单文件。
看微软文档吧,在 vscode 的环境下,就认为是插件的安装包好了
运行命令后会在项目目录下生产对应版本的插件打包产物,后缀为 vsix,以 v17.8.1为例,产品为 gitlens-17.8.1.vsix
--【拾捌】--:
这题我会
# cmd+p feature-gate.ts
# 修改此文件中的css样式
@customElement('gl-feature-gate')
export class GlFeatureGate extends LitElement {
static override styles = [
linkStyles,
css`
:host {
...
/* 移除这段覆盖整个界面的样式 */
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: auto;
z-index: 100; */
/* 添加这一行 */
display: none;
}
:host-context(body[data-placement='editor']),
:host([appearance='alert']) {
...
/* 删除 */
/* backdrop-filter: blur(3px) saturate(0.8); */
...
/* 添加一行 */
display: none;
}
...
`,
];
}
或者更简单粗暴
# 修改 gate.ts 和 feature-gate.ts 返回值为 undefine
override render(): unknown {
// 删除原有代码
return undefine;
}
--【拾玖】--:
太强了佬

