summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-01-03 15:13:29 +0800
committer魏曹先生 <1992414357@qq.com>2026-01-03 15:13:29 +0800
commitbaeee2f316c8c9ccab3da15943b478aed99c6cad (patch)
tree28891192b9742ab3a5260b2b83a947997db52b4c
parent599b1b7d9f657ed9c36bdcbece8c771db4422bab (diff)
Add host mode support and reference sheet restrictions
- Add `jv account as host/<ACCOUNT_NAME>` command for host mode - Display host mode status in `jv here` and `jv status` - Add reference sheet read-only mode hints and restrictions - Show host mode capabilities and warnings in status output - Prevent structural changes in reference sheets for non-hosts
-rw-r--r--locales/help_docs/en.yml44
-rw-r--r--locales/help_docs/zh-CN.yml44
-rw-r--r--src/bin/jv.rs100
3 files changed, 161 insertions, 27 deletions
diff --git a/locales/help_docs/en.yml b/locales/help_docs/en.yml
index 31544ef..ac8dc8c 100644
--- a/locales/help_docs/en.yml
+++ b/locales/help_docs/en.yml
@@ -287,6 +287,7 @@ jv:
**Usage**:
jv account list - List all accounts on this computer and whether private keys are registered
jv account as <ACCOUNT_NAME> - Switch current account
+ jv account as host/<ACCOUNT_NAME> - Switch to account in Host mode
jv account add <ACCOUNT_NAME> - Add an account to this computer
jv account remove <ACCOUNT_NAME> - Delete this account
jv account movekey <ACCOUNT_NAME> <PRIVATE_KEY_FILE> - Move private key to specified account
@@ -502,9 +503,6 @@ jv:
Sheet `%{sheet_name}` is no holder. Take ownership?
If not, use a different name to create a sheet.
- restore_ref: |
- You can't just grab the reference sheet, it's the team's shared structure! :)
-
drop: |
Are you sure you want to drop sheet `%{sheet_name}`?
After this, you will not be able to work in the sheet!
@@ -711,6 +709,10 @@ jv:
success:
account:
as: Successfully switched this workspace's account to `%{account}`
+ as_host: |
+ Switched to account: `host/%{account}`
+ If the Host identity is recognized by the upstream vault, you can control all sheets and mappings.
+ Please remember: "*With great power comes great responsibility*"
add: Successfully added account `%{account}`!
remove: Successfully removed account `%{account}`!
list:
@@ -879,6 +881,9 @@ jv:
In sheet %{sheet_name}, status based on %{h} hours %{m} minutes %{s} seconds ago
Your workspace is synchronized with upstream, you can proceed with structural and content editing based on this state!
+ no_changes_in_reference_sheet: |
+ In sheet %{sheet_name}, status based on %{h} hours %{m} minutes %{s} seconds ago
+
created_item: |
+ Created: %{path}
@@ -908,8 +913,41 @@ jv:
no_file_modifications: |
No modifications
+ hint_in_reference_sheet: |
+ You are accessing the **reference sheet** in **read-only** mode
+ You can view file content, share visibility, and modify holding rights
+ But you cannot modify the structure or submit content
+
+ hint_as_host: |
+ You are in Host mode.
+ In this mode, you are the final interpreter of vault structure and version progression.
+ Your operations will directly affect others' work status, and may not be reversible.
+
+ **Structure Maintenance**
+ 1. You can use `jv align` or `jv move` to edit the reference sheet structure
+ 2. You can use `jv make` to create new reference sheets,
+ or convert an unheld sheet into a reference sheet
+
+ **Status Arbitration**
+ 3. You can use `jv throw` to forcibly discard everyone's edit rights
+ 4. You can use `jv drop` to forcibly discard others' sheets
+
+ **Fact Advancement**
+ 5. You can directly use `jv track` in any sheet to advance file versions
+
+ [[red]]**Please remember**: Host is not administrator mode,
+ but the ultimate bearer of others' structure, history, and interpretive rights.
+ Every operation you perform will become a fact that others must accept.[[/]]
+
result:
common:
+ not_allowed_in_reference_sheet: |
+ This operation is not allowed in the reference sheet!
+ You cannot make any structural changes to the reference sheet
+ because you are not the maintainer of the reference sheet.
+
+ **Tip**: If you have local moves that need to be aligned,
+ use `jv align moved remote` to revert the moves.
authroize_failed: |
Authentication failed: %{err}!
unknown: |
diff --git a/locales/help_docs/zh-CN.yml b/locales/help_docs/zh-CN.yml
index 1bafb1a..ffa566d 100644
--- a/locales/help_docs/zh-CN.yml
+++ b/locales/help_docs/zh-CN.yml
@@ -279,6 +279,7 @@ jv:
**用法**:
jv account list - 列出该计算机所有的账户,以及是否注册私钥
jv account as <账户名称> - 切换当前账户
+ jv account as host/<账户名称> - 以 Host 模式切换账户
jv account add <账户名称> - 为当前计算机添加账户
jv account remove <账户名称> - 删除该账户
jv account movekey <账户名称> <私钥文件> - 移动私钥到指定账户
@@ -307,6 +308,7 @@ jv:
对于移动项:
jv sheet align <项> [local/remote] - 对齐指定移动项
jv sheet align moved [local/remote] - 对齐所有移动项
+ jv sheet align moved break - 断开所有移动项
对于丢失项:
jv sheet align <项> <创建项> - 指向创建项,以确认移动
@@ -491,9 +493,6 @@ jv:
似乎 `%{sheet_name}` 是一张无人认领的表,是否拿到它的所有权?
如果您不想拿到该表,请使用别的名称建立表
- restore_ref: |
- 您不能直接拿到参照表,它是团队的公共结构 :)
-
drop: |
是否要放弃表 `%{sheet_name}` 的所有权,此后,您将无法在表中工作!
@@ -701,6 +700,10 @@ jv:
success:
account:
as: 成功将此工作区的账户切换至 `%{account}`
+ as_host: |
+ 切换到账户:`host/%{account}`
+ 若 Host 身份受上游库认可,便可控制所有的表、映射
+ 请牢记:"*能力越大,责任越大*"
add: 成功添加账户 `%{account}`!
remove: 成功删除账户 `%{account}`!
list:
@@ -870,6 +873,9 @@ jv:
所在表 %{sheet_name},状态基于 %{h} 小时 %{m} 分钟 %{s} 秒前
您的工作区与上游保持同步,可基于该状态进行结构、内容的编辑!
+ no_changes_in_reference_sheet: |
+ 所在表 %{sheet_name},状态基于 %{h} 小时 %{m} 分钟 %{s} 秒前
+
created_item: |
+ 创建: %{path}
@@ -899,8 +905,40 @@ jv:
no_file_modifications: |
本地无内容修改
+ hint_in_reference_sheet: |
+ 您正在以**只读模式**访问**参考表**
+ 您可以查看文件内容、分享可见性、修改持有权
+ 但无法修改结构或提交内容
+
+ hint_as_host: |
+ [[yellow]]您正处于 Host 模式。[[/]]
+ 在此模式下,您将作为仓库结构与版本推进的最终解释者,
+ 您的操作将直接影响他人的工作状态,且不一定可逆。
+
+ **结构维护**
+ 1. 您可以使用 `jv align` 或 `jv move` 编辑参考表结构
+ 2. 您可以使用 `jv make` 创建新的参考表,
+ 或将某张无人持有的表转换为参考表
+
+ **状态裁决**
+ 3. 您可以使用 `jv throw` 强制收回所有人的编辑权
+ 4. 您可以使用 `jv drop` 强制丢弃其他人的表
+
+ **事实推进**
+ 5. 您可以在任何表中使用 `jv track` 推进文件版本
+
+ [[red]]**请牢记**:Host 并非 管理员模式,
+ 而是对他人结构、历史与解释权的最终承担者。
+ 您的每一次操作,都会成为他人必须接受的事实。[[/]]
+
result:
common:
+ not_allowed_in_reference_sheet: |
+ 不允许在参考表中执行此操作!
+ 您不能对参考表进行任何结构更改,因为您并非参考表的维护者
+
+ **提示**:若您在本地产生了移动需要对齐,
+ 使用 `jv align moved remote` 还原移动
authroize_failed: 身份认证失败:%{err}!
unknown: |
未知结果!
diff --git a/src/bin/jv.rs b/src/bin/jv.rs
index 128c910..55248ab 100644
--- a/src/bin/jv.rs
+++ b/src/bin/jv.rs
@@ -36,7 +36,7 @@ use just_enough_vcs::{
},
constants::{
CLIENT_FILE_TODOLIST, CLIENT_FILE_WORKSPACE, CLIENT_FOLDER_WORKSPACE_ROOT_NAME,
- CLIENT_PATH_WORKSPACE_ROOT, PORT, REF_SHEET_NAME,
+ CLIENT_PATH_WORKSPACE_ROOT, PORT,
},
current::{correct_current_dir, current_cfg_dir, current_local_path},
data::{
@@ -1244,7 +1244,11 @@ async fn main() {
JustEnoughVcsWorkspaceCommand::GetCurrentAccount => {
let _ = correct_current_dir();
if let Ok(local_config) = LocalConfig::read().await {
- println!("{}", local_config.current_account())
+ if local_config.is_host_mode() {
+ println!("host/{}", local_config.current_account())
+ } else {
+ println!("{}", local_config.current_account())
+ }
};
}
JustEnoughVcsWorkspaceCommand::GetCurrentUpstream => {
@@ -1413,12 +1417,18 @@ async fn jv_here(args: HereArgs) {
.unwrap_or_default();
let minutes = duration_updated.as_secs() / 60;
+ let account_str = if local_cfg.is_host_mode() {
+ format!("{}/{}", "host".red(), local_cfg.current_account())
+ } else {
+ local_cfg.current_account()
+ };
+
println!(
"{}",
t!(
"jv.success.here.path_info",
upstream = local_cfg.upstream_addr().to_string(),
- account = local_cfg.current_account(),
+ account = account_str,
sheet_name = sheet_name.yellow(),
path = relative_path,
minutes = minutes
@@ -1773,7 +1783,7 @@ async fn jv_status(_args: StatusArgs) {
return;
};
- let Some(local_workspace) = LocalWorkspace::init_current_dir(local_cfg) else {
+ let Some(local_workspace) = LocalWorkspace::init_current_dir(local_cfg.clone()) else {
eprintln!("{}", md(t!("jv.fail.workspace_not_found")).trim());
return;
};
@@ -1783,6 +1793,9 @@ async fn jv_status(_args: StatusArgs) {
return;
};
+ let in_ref_sheet = latest_info.reference_sheets.contains(&sheet_name);
+ let is_host_mode = local_cfg.is_host_mode();
+
let Ok(analyzed) = AnalyzeResult::analyze_local_status(&local_workspace).await else {
eprintln!("{}", md(t!("jv.fail.status.analyze")).trim());
return;
@@ -1989,17 +2002,40 @@ async fn jv_status(_args: StatusArgs) {
.trim()
);
} else {
+ if in_ref_sheet {
+ println!(
+ "{}",
+ md(t!(
+ "jv.success.status.no_changes_in_reference_sheet",
+ sheet_name = sheet_name,
+ h = h,
+ m = m,
+ s = s
+ ))
+ );
+ } else {
+ println!(
+ "{}",
+ md(t!(
+ "jv.success.status.no_changes",
+ sheet_name = sheet_name,
+ h = h,
+ m = m,
+ s = s
+ ))
+ );
+ }
+ }
+
+ if in_ref_sheet && !is_host_mode {
println!(
- "{}",
- md(t!(
- "jv.success.status.no_changes",
- sheet_name = sheet_name,
- h = h,
- m = m,
- s = s
- ))
+ "\n{}",
+ md(t!("jv.success.status.hint_in_reference_sheet")).yellow()
);
}
+ if is_host_mode {
+ println!("\n{}", md(t!("jv.success.status.hint_as_host")));
+ }
}
async fn jv_info(args: InfoArgs) {}
@@ -3875,6 +3911,7 @@ async fn jv_account_list(user_dir: UserDirectory, args: AccountListArgs) {
let Ok(account_ids) = user_dir.account_ids() else {
return;
};
+ account_ids.iter().for_each(|a| println!("host/{}", a));
account_ids.iter().for_each(|a| println!("{}", a));
return;
}
@@ -3908,12 +3945,11 @@ async fn jv_account_list(user_dir: UserDirectory, args: AccountListArgs) {
}
async fn jv_account_as(user_dir: UserDirectory, args: SetLocalWorkspaceAccountArgs) {
+ let (account, is_host_mode) = process_account_parameter(args.account_name);
+
// Account exist
- let Ok(member) = user_dir.account(&args.account_name).await else {
- eprintln!(
- "{}",
- t!("jv.fail.account.not_found", account = args.account_name)
- );
+ let Ok(member) = user_dir.account(&account).await else {
+ eprintln!("{}", t!("jv.fail.account.not_found", account = &account));
return;
};
@@ -3932,15 +3968,37 @@ async fn jv_account_as(user_dir: UserDirectory, args: SetLocalWorkspaceAccountAr
return;
};
+ local_cfg.set_host_mode(is_host_mode);
+
let Ok(_) = LocalConfig::write(&local_cfg).await else {
eprintln!("{}", t!("jv.fail.write_cfg").trim());
return;
};
- println!(
- "{}",
- t!("jv.success.account.as", account = member.id()).trim()
- );
+ if is_host_mode {
+ println!(
+ "{}",
+ md(t!("jv.success.account.as_host", account = member.id()))
+ );
+ } else {
+ println!(
+ "{}",
+ t!("jv.success.account.as", account = member.id()).trim()
+ );
+ }
+}
+
+/// Input account, get MemberId and whether it's a host
+/// If input is host/xxx, output is xxx, true
+/// If input is xxx, output is xxx, false
+fn process_account_parameter(account_input: String) -> (MemberId, bool) {
+ let is_host = account_input.starts_with("host/");
+ let account_id = if is_host {
+ account_input.strip_prefix("host/").unwrap().to_string()
+ } else {
+ account_input
+ };
+ (snake_case!(account_id), is_host)
}
async fn jv_account_move_key(user_dir: UserDirectory, args: MoveKeyToAccountArgs) {