Skip to content

Commit 6a3a8d3

Browse files
committed
feat(cli): add qifi command and parse args
1 parent f70f0b1 commit 6a3a8d3

11 files changed

+269
-114
lines changed

cspell.config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ words:
99
- luby
1010
- Nuxt
1111
- pako
12+
- Positionals
1213
- qifi
1314
- QRCODE
1415
- Soliton

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"nuxt": "^3.13.2",
4343
"ohash": "^1.1.4",
4444
"pinia": "^2.2.4",
45+
"tsx": "^4.19.1",
4546
"typescript": "^5.6.2",
4647
"unbuild": "^2.0.0",
4748
"vitest": "^2.1.1",

packages/cli/README.md

+5-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# @qifi/generate
1+
# QiFi CLI
22

33
<!-- Some beautiful tags -->
44
<p align="left">
@@ -9,11 +9,11 @@
99
<img alt="docs" src="https://img.shields.io/badge/-docs%20%26%20demos-1e8a7a">
1010
</a>
1111
<a href="https://github.com/sponsors/LittleSound">
12-
<img alt="docs" src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86">
12+
<img alt="sponsors" src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86">
1313
</a>
1414
</p>
1515

16-
Stream Generated QR Codes for data transmission
16+
Stream Generated QR Codes for file transmission in your terminal
1717

1818
## Sponsors
1919

@@ -31,29 +31,6 @@ Stream Generated QR Codes for data transmission
3131

3232
## Usage
3333

34-
```javascript
35-
import {
36-
createGeneraterANSI,
37-
createGeneraterUnicode,
38-
createGeneraterUnicodeCompact,
39-
createGeneraterSVG,
40-
createGeneraterQRCodeArray,
41-
} from '@qifi/generate'
42-
43-
const generaterSvg = createGeneraterSVG(new Uint8Array(file.buffer))
44-
45-
const generaterANSI = createGeneraterANSI(new Uint8Array(file.buffer), {
46-
// Size of each data slice
47-
sliceSize: 250,
48-
// Error correction level
49-
ecc: 'L',
50-
// Border width
51-
border: 2,
52-
})
53-
54-
// display QR Code in terminal
55-
for (const blockQRCode of generaterANSI()) {
56-
console.log(blockQRCode)
57-
}
58-
34+
```bash
35+
npx qifi <file> [options]
5936
```

packages/cli/bin/qifi.mjs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env node
2+
'use strict'
3+
import { main } from '../dist/cli.mjs'
4+
5+
main()

packages/cli/build.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { defineBuildConfig } from 'unbuild'
33
export default defineBuildConfig({
44
entries: [
55
'src/index.ts',
6+
'src/cli.ts',
67
],
78
declaration: true,
89
rollup: {

packages/cli/package.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@
2222
"main": "./dist/index.mjs",
2323
"module": "./dist/index.mjs",
2424
"types": "./dist/index.d.mts",
25+
"bin": {
26+
"qifi": "./bin/qifi.mjs"
27+
},
2528
"files": [
2629
"dist"
2730
],
2831
"scripts": {
29-
"dev": "tsx ./src",
32+
"dev": "tsx ./src/cli",
3033
"build": "unbuild",
3134
"stub": "unbuild --stub"
3235
},
3336
"dependencies": {
3437
"@qifi/generate": "workspace:*",
35-
"mime": "^4.0.4",
36-
"tsx": "^4.19.1"
38+
"cac": "^6.7.14",
39+
"mime": "^4.0.4"
3740
}
3841
}

packages/cli/src/cli.ts

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
import process from 'node:process'
4+
import readline from 'node:readline'
5+
import { appendFileHeaderMetaToBuffer, createGeneraterANSI, createGeneraterUnicode, createGeneraterUnicodeCompact } from '@qifi/generate'
6+
import mime from 'mime'
7+
import {
8+
black,
9+
boostEcc,
10+
border,
11+
compact,
12+
contentType,
13+
ecc,
14+
fps,
15+
invert,
16+
maskPattern,
17+
maxVersion,
18+
minVersion,
19+
positional,
20+
prefix,
21+
sliceSize,
22+
unicode,
23+
white,
24+
} from './parse-args'
25+
import { handleArgsError } from './shared'
26+
27+
function chooseGenerater() {
28+
if (compact) {
29+
return createGeneraterUnicodeCompact
30+
}
31+
if (unicode) {
32+
return createGeneraterUnicode
33+
}
34+
return createGeneraterANSI
35+
}
36+
37+
// Function to read file and generate QR codes
38+
export async function main() {
39+
const fullPath = path.resolve(positional)
40+
41+
console.log('fullPath:', fullPath)
42+
await new Promise(resolve => setTimeout(resolve, 1000))
43+
if (!fs.existsSync(fullPath)) {
44+
handleArgsError(new TypeError(`File not found: ${fullPath}`))
45+
}
46+
47+
const fileBuffer = fs.readFileSync(fullPath)
48+
const data = new Uint8Array(fileBuffer)
49+
const meta = {
50+
filename: path.basename(fullPath),
51+
contentType: contentType || mime.getType(fullPath) || 'application/octet-stream',
52+
}
53+
54+
const merged = appendFileHeaderMetaToBuffer(data, meta)
55+
56+
const generator = chooseGenerater()(merged, {
57+
sliceSize,
58+
urlPrefix: prefix,
59+
...{
60+
whiteChar: white,
61+
blackChar: black,
62+
} as any,
63+
invert,
64+
ecc,
65+
maskPattern,
66+
boostEcc,
67+
minVersion,
68+
maxVersion,
69+
border,
70+
})
71+
72+
// Clear console function
73+
const clearConsole = () => {
74+
readline.cursorTo(process.stdout, 0, 0)
75+
readline.clearScreenDown(process.stdout)
76+
}
77+
78+
// Display QR codes
79+
for (const blockQRCode of generator.fountain()) {
80+
clearConsole()
81+
process.stdout.write(`${blockQRCode}\n`)
82+
process.stdout.write(`${meta.filename} (${meta.contentType}) | size: ${data.length} bytes`)
83+
await new Promise(resolve => setTimeout(resolve, 1000 / fps))
84+
}
85+
}

packages/cli/src/index.ts

+2-67
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,2 @@
1-
#!/usr/bin/env tsx
2-
3-
import fs from 'node:fs'
4-
import path from 'node:path'
5-
import process from 'node:process'
6-
import readline from 'node:readline'
7-
import { appendFileHeaderMetaToBuffer, createGeneraterANSI } from '@qifi/generate'
8-
import mime from 'mime'
9-
10-
// Function to read file and generate QR codes
11-
async function generateQRCodes(filePath: string, sliceSize: number = 80, fps: number = 20) {
12-
const fullPath = path.resolve(filePath)
13-
14-
console.log('fullPath:', fullPath)
15-
await new Promise(resolve => setTimeout(resolve, 1000))
16-
if (!fs.existsSync(fullPath)) {
17-
console.error(`File not found: ${fullPath}`)
18-
process.exit(1)
19-
}
20-
21-
const fileBuffer = fs.readFileSync(fullPath)
22-
const data = new Uint8Array(fileBuffer)
23-
const meta = {
24-
filename: path.basename(fullPath),
25-
contentType: mime.getType(fullPath) || 'application/octet-stream',
26-
}
27-
28-
const merged = appendFileHeaderMetaToBuffer(data, meta)
29-
30-
const generator = createGeneraterANSI(merged, {
31-
urlPrefix: 'https://qrss.netlify.app/#',
32-
sliceSize,
33-
border: 2,
34-
})
35-
36-
// Clear console function
37-
const clearConsole = () => {
38-
readline.cursorTo(process.stdout, 0, 0)
39-
readline.clearScreenDown(process.stdout)
40-
}
41-
42-
// Display QR codes
43-
for (const blockQRCode of generator.fountain()) {
44-
clearConsole()
45-
process.stdout.write(`${blockQRCode}\n`)
46-
process.stdout.write(`${meta.filename} (${meta.contentType}) | size: ${data.length} bytes`)
47-
await new Promise(resolve => setTimeout(resolve, 1000 / fps))
48-
}
49-
}
50-
51-
// Parse command line arguments
52-
const args = process.argv.slice(2)
53-
if (args.length < 1) {
54-
console.error('Usage: qr-file-transfer <file-path> [slice-size]')
55-
process.exit(1)
56-
}
57-
58-
const [filePath, sliceSizeStr, fpsStr] = args
59-
const sliceSize = sliceSizeStr ? Number.parseInt(sliceSizeStr, 10) : undefined
60-
const fps = fpsStr ? Number.parseInt(fpsStr, 10) : undefined
61-
62-
if (!filePath) {
63-
console.error('File path is required')
64-
process.exit(1)
65-
}
66-
67-
generateQRCodes(filePath, sliceSize, fps)
1+
console.warn('qifi is a cli package, it should not be imported in other packages')
2+
export default function () {}

0 commit comments

Comments
 (0)